percona-xtradb-cluster-galera/AUTHORS0000644000000000000000000000004212247075736017716 0ustar rootroot00000000000000Codership Oy percona-xtradb-cluster-galera/COPYING0000644000000000000000000000202512247075736017704 0ustar rootroot00000000000000Galera replication - implementation of write set replication (wsrep) interface. Copyright (C) 2007-2013 Codership Oy This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License. For the sake of clarity, please note that Codership has made this release specifically using version 2 of the GPL, and not granted an "any later version" option. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You will find the GNU General Public License version 2 in the file LICENSE or at http://www.gnu.org/licenses/gpl-2.0.html docs/ Documentation is dual licensed with the Creative Commons Attribution ShareAlike and GNU Free Documentation Licenses. See docs/COPYING for details. Following modules have their own, different licenses: asio/ see asio/COPYING for details percona-xtradb-cluster-galera/GALERA-REVISION0000644000000000000000000000000412247075736020756 0ustar rootroot00000000000000165 percona-xtradb-cluster-galera/LICENSE0000644000000000000000000004325412247075736017667 0ustar rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. percona-xtradb-cluster-galera/README0000644000000000000000000000376612247075736017546 0ustar rootroot00000000000000Codership Oy http://www.codership.com This is Galera replication - Codership's implementation of the write set replication (wsrep) interface (https://launchpad.net/wsrep/). The software and other files in this directory unless otherwise noted are distributed under GPLv2, see COPYING for details. BUILDING - general Build Requirements: * Scons build system http://www.scons.org/ * Check unit test library http://check.sourceforge.net/ * Boost devel package http://www.boost.org/ * OpenSSL devel package To compile, in Galera root directory do either: $ scons (default optimized build) or $ ./scripts/build.sh (see ./scripts/build.sh --help for information on options) To build MySQL/Galera demo distribution, branch lp:codership-mysql into some directory (hereafter ) and run build script from this directory (hereafter ): $ cd $ bzr branch lp:codership-mysql $ cd $ MYSQL_SRC=/codership-mysql ./scripts/mysql/build.sh -b -s -o -t After successful build, demo package can be found under scripts/mysql. BUILDING on CentOS/RHEL 5 CentOS/RHEL by default uses a very old version of GCC and a likewise ancient version of BOOST that lacks important features. In order to build Galera on CentOS/RHEL one needs to: 1) Install gcc44 and gcc44-c++ packages and set CC and CXX variables accordingly: # yum install gcc44 gcc44-c++ # export CC=gcc44 # export CXX=g++44 2) Remove standard boost and boost-devel packages (if present) 3) Install boost141-devel package: # yum install boost141-devel 4) Make link from /usr/include/boost to /usr/include/boost141: # cd /usr/include # ln -sf boost141/boost boost 5) Install scons check-devel openssl-devel # yum install scons check-devel openssl-devel Then proceeed as described above. For more information, see: * Codership mailing list: http://groups.google.com/group/codership-team * https://launchpad.net/galera/ * Codership home page: http://www.codership.com percona-xtradb-cluster-galera/SConscript0000644000000000000000000000125012247075736020662 0ustar rootroot00000000000000SConscript(['galerautils/SConscript', 'gcache/SConscript', 'gcomm/SConscript', 'gcs/SConscript', 'galera/SConscript', 'garb/SConscript']) Import('env', 'sysname', 'static_ssl', 'with_ssl') libmmgalera_objs = env['LIBGALERA_OBJS'] libmmgalera_objs.extend(env['LIBMMGALERA_OBJS']) if static_ssl == 1: env.Append(LIBS=File('%s/libssl.a' %(with_ssl))) env.Append(LIBS=File('%s/libcrypto.a' %(with_ssl))) env.Append(LIBS=File('%s/libz.a' %(with_ssl))) if sysname == 'darwin': env.SharedLibrary('galera_smm', libmmgalera_objs, SHLIBSUFFIX='.so') else: env.SharedLibrary('galera_smm', libmmgalera_objs) percona-xtradb-cluster-galera/SConstruct0000644000000000000000000003571712247075736020721 0ustar rootroot00000000000000################################################################### # # Copyright (C) 2010-2012 Codership Oy # # SCons build script to build galera libraries # # Script structure: # - Help message # - Default parameters # - Read commandline options # - Set up and configure default build environment # - Set up and configure check unit test build environment # - Run root SConscript with variant_dir # #################################################################### import os import platform import string sysname = os.uname()[0].lower() machine = platform.machine() print 'Host: ' + sysname + ' ' + machine # # Print Help # Help(''' Build targets: build tests check install all Default target: all Commandline Options: static_ssl=[0|1] Build with static SSL with_ssl=path Prefix for SSL debug=n debug build with optimization level n arch=str target architecture [i686|x86_64] build_dir=dir build directory, default: '.' boost=[0|1] disable or enable boost libraries boost_pool=[0|1] use or not use boost pool allocator revno=XXXX source code revision number bpostatic=path a path to static libboost_program_options.a extra_sysroot=path a path to extra development environment (Fink, Homebrew, MacPorts, MinGW) ''') # bpostatic option added on Percona request # # Default params # build_target = 'all' # Optimization level opt_flags = ' -g -O3 -DNDEBUG' # Architecture (defaults to build host type) compile_arch = '' link_arch = '' with_ssl = '' # Build directory build_dir = '' # # Read commandline options # build_dir = ARGUMENTS.get('build_dir', '') with_ssl = ARGUMENTS.get('with_ssl', '/usr/lib64') # Debug/dbug flags debug = ARGUMENTS.get('debug', -1) dbug = ARGUMENTS.get('dbug', False) if int(debug) >= 0 and int(debug) < 3: opt_flags = ' -g -O%d -fno-inline' % int(debug) dbug = True elif int(debug) == 3: opt_flags = ' -g -O3' if dbug: opt_flags = opt_flags + ' -DGU_DBUG_ON' # Target arch arch = ARGUMENTS.get('arch', machine) print 'Target: ' + sysname + ' ' + arch if arch == 'i386' or arch == 'i686': compile_arch = ' -m32 -march=i686' link_arth = compile_arch if sysname != 'darwin' and sysname != 'freebsd': link_arch = compile_arch + ' -Wl,-melf_i386' elif arch == 'x86_64' or arch == 'amd64': compile_arch = ' -m64' link_arth = compile_arch if sysname != 'darwin' and sysname != 'freebsd': link_arch = compile_arch + ' -Wl,-melf_x86_64' elif arch == 'ppc64': compile_arch = ' -mtune=native' link_arch = '' elif sysname == 'sunos': compile_arch = '' link_arch = '' else: compile_arch = '' link_arch = '' boost = int(ARGUMENTS.get('boost', 1)) boost_pool = int(ARGUMENTS.get('boost_pool', 0)) static_ssl = int(ARGUMENTS.get('static_ssl', 0)) ssl = int(ARGUMENTS.get('ssl', 1)) tests = int(ARGUMENTS.get('tests', 1)) strict_build_flags = int(ARGUMENTS.get('strict_build_flags', 1)) GALERA_VER = ARGUMENTS.get('version', '2.8') GALERA_REV = ARGUMENTS.get('revno', 'XXXX') # export to any module that might have use of those Export('GALERA_VER', 'GALERA_REV') print 'Signature: version: ' + GALERA_VER + ', revision: ' + GALERA_REV LIBBOOST_PROGRAM_OPTIONS_A = ARGUMENTS.get('bpostatic', '') LIBBOOST_SYSTEM_A = string.replace(LIBBOOST_PROGRAM_OPTIONS_A, 'boost_program_options', 'boost_system') # # Set up and export default build environment # env = Environment(ENV = {'PATH' : os.environ['PATH'], 'HOME' : os.environ['HOME']}) # Set up environment for ccache and distcc # env['ENV']['HOME'] = os.environ['HOME'] #env['ENV']['DISTCC_HOSTS'] = os.environ['DISTCC_HOSTS'] #env['ENV']['CCACHE_PREFIX'] = os.environ['CCACHE_PREFIX'] if 'CCACHE_DIR' in os.environ: env['ENV']['CCACHE_DIR'] = os.environ['CCACHE_DIR'] if 'CCACHE_CPP2' in os.environ: env['ENV']['CCACHE_CPP2'] = os.environ['CCACHE_CPP2'] # Set CC and CXX compilers cc = os.getenv('CC', 'default') if cc != 'default': env.Replace(CC = cc) cxx = os.getenv('CXX', 'default') if cxx != 'default': env.Replace(CXX = cxx) link = os.getenv('LINK', 'default') if link != 'default': env.Replace(LINK = link) # Initialize CPPFLAGS and LIBPATH from environment to get user preferences env.Replace(CPPFLAGS = os.getenv('CPPFLAGS', '')) env.Replace(LIBPATH = [os.getenv('LIBPATH', '')]) # Set -pthread flag explicitly to make sure that pthreads are # enabled on all platforms. env.Append(CPPFLAGS = ' -pthread') # Freebsd ports are installed under /usr/local if sysname == 'freebsd' or sysname == 'sunos': env.Append(LIBPATH = ['/usr/local/lib']) env.Append(CPPFLAGS = ' -I/usr/local/include ') if sysname == 'sunos': env.Replace(SHLINKFLAGS = '-shared ') # Add paths is extra_sysroot argument was specified extra_sysroot = ARGUMENTS.get('extra_sysroot', '') if sysname == 'darwin' and extra_sysroot == '': # common developer environments and paths if os.system('which -s port') == 0 and os.path.isfile('/opt/local/bin/port'): extra_sysroot = '/opt/local' elif os.system('which -s brew') == 0 and os.path.isfile('/usr/local/bin/brew'): extra_sysroot = '/usr/local' elif os.system('which -s fink') == 0 and os.path.isfile('/sw/bin/fink'): extra_sysroot = '/sw' if extra_sysroot != '': env.Append(LIBPATH = [extra_sysroot + '/lib']) env.Append(CPPFLAGS = ' -I' + extra_sysroot + '/include') # print env.Dump() # # Set up build and link paths # # Include paths env.Append(CPPPATH = Split('''#/common #/asio #/galerautils/src #/gcomm/src #/gcomm/src/gcomm #/gcache/src #/gcs/src #/wsdb/src #/galera/src ''')) # Library paths #env.Append(LIBPATH = Split('''#/galerautils/src # #/gcomm/src # #/gcs/src # #/wsdb/src # #/galera/src # ''')) # Preprocessor flags if sysname != 'sunos' and sysname != 'darwin' and sysname != 'freebsd': env.Append(CPPFLAGS = ' -D_XOPEN_SOURCE=600') if sysname == 'sunos': env.Append(CPPFLAGS = ' -D__EXTENSIONS__') env.Append(CPPFLAGS = ' -DHAVE_COMMON_H') # Common C/CXX flags # These should be kept minimal as they are appended after C/CXX specific flags env.Replace(CCFLAGS = opt_flags + compile_arch + ' -Wall -Wextra -Wno-unused-parameter') # C-specific flags env.Replace(CFLAGS = ' -std=c99 -fno-strict-aliasing -pipe') # CXX-specific flags # Note: not all 3rd-party libs like '-Wold-style-cast -Weffc++' # adding those after checks env.Replace(CXXFLAGS = ' -Wno-long-long -Wno-deprecated -ansi') if sysname != 'sunos': env.Append(CXXFLAGS = ' -pipe') # Linker flags # TODO: enable '-Wl,--warn-common -Wl,--fatal-warnings' after warnings from # static linking have beed addressed # env.Append(LINKFLAGS = link_arch) # # Check required headers and libraries (autoconf functionality) # conf = Configure(env) # System headers and libraries if not conf.CheckLib('pthread'): print 'Error: pthread library not found' Exit(1) if sysname != 'darwin': if not conf.CheckLib('rt'): print 'Error: rt library not found' Exit(1) if sysname == 'freebsd': if not conf.CheckLib('execinfo'): print 'Error: execinfo library not found' Exit(1) if sysname == 'sunos': if not conf.CheckLib('socket'): print 'Error: socket library not found' Exit(1) if not conf.CheckLib('crypto'): print 'Error: crypto library not found' Exit(1) if not conf.CheckLib('nsl'): print 'Error: nsl library not found' Exit(1) if conf.CheckHeader('sys/epoll.h'): conf.env.Append(CPPFLAGS = ' -DGALERA_USE_GU_NETWORK') if conf.CheckHeader('byteswap.h'): conf.env.Append(CPPFLAGS = ' -DHAVE_BYTESWAP_H') if conf.CheckHeader('endian.h'): conf.env.Append(CPPFLAGS = ' -DHAVE_ENDIAN_H') elif conf.CheckHeader('sys/endian.h'): conf.env.Append(CPPFLAGS = ' -DHAVE_SYS_ENDIAN_H') elif conf.CheckHeader('sys/byteorder.h'): conf.env.Append(CPPFLAGS = ' -DHAVE_SYS_BYTEORDER_H') elif sysname != 'darwin': print 'can\'t find byte order information' Exit(1) # Additional C headers and libraries # boost headers if not conf.CheckCXXHeader('boost/shared_ptr.hpp'): print 'boost/shared_ptr.hpp not found or not usable' Exit(1) conf.env.Append(CPPFLAGS = ' -DHAVE_BOOST_SHARED_PTR_HPP') if conf.CheckCXXHeader('tr1/unordered_map'): conf.env.Append(CPPFLAGS = ' -DHAVE_TR1_UNORDERED_MAP') else: if conf.CheckCXXHeader('boost/unordered_map.hpp'): conf.env.Append(CPPFLAGS = ' -DHAVE_BOOST_UNORDERED_MAP_HPP') else: print 'no unordered map header available' Exit(1) # pool allocator if boost == 1: # Default suffix for boost multi-threaded libraries if sysname == 'darwin': boost_library_suffix = '-mt' else: boost_library_suffix = '' if sysname == 'darwin' and extra_sysroot != '': boost_library_path = extra_sysroot + '/lib' else: boost_library_path = '' # Use nanosecond time precision conf.env.Append(CPPFLAGS = ' -DBOOST_DATE_TIME_POSIX_TIME_STD_CONFIG=1') # Common procedure to find boost static library boost_libpaths = [ boost_library_path, '/usr/local/lib', '/usr/local/lib64', '/usr/lib', '/usr/lib64' ] def check_boost_library(libBaseName, header, configuredLibPath, autoadd = 1): libName = libBaseName + boost_library_suffix if configuredLibPath != '' and not os.path.isfile(configuredLibPath): print "Error: file '%s' does not exist" % configuredLibPath Exit(1) if configuredLibPath == '': for libpath in boost_libpaths: libname = libpath + '/lib%s.a' % libName if os.path.isfile(libname): configuredLibPath = libname break if configuredLibPath != '': if not conf.CheckCXXHeader(header): print "Error: header '%s' does not exist" % header Exit (1) if autoadd: conf.env.Append(LIBS=File(configuredLibPath)) else: return File(configuredLibPath) else: if not conf.CheckLibWithHeader(libs=[libName], header=header, language='CXX', autoadd=autoadd): print 'Error: library %s does not exist' % libName Exit (1) return [libName] # Required boost headers/libraries # if boost_pool == 1: if conf.CheckCXXHeader('boost/pool/pool_alloc.hpp'): print 'Using boost pool alloc' conf.env.Append(CPPFLAGS = ' -DGALERA_USE_BOOST_POOL_ALLOC=1') # due to a bug in boost >= 1.50 we need to link with boost_system # - should be a noop with no boost_pool. if sysname == 'darwin': if conf.CheckLib('boost_system' + boost_library_suffix): conf.env.Append(LIBS=['boost_system' + boost_library_suffix]) check_boost_library('boost_system', 'boost/system/error_code.hpp', LIBBOOST_SYSTEM_A) else: print 'Error: boost/pool/pool_alloc.hpp not found or not usable' Exit(1) libboost_program_options = check_boost_library('boost_program_options', 'boost/program_options.hpp', LIBBOOST_PROGRAM_OPTIONS_A, autoadd = 0) else: print 'Not using boost' # asio if conf.CheckCXXHeader('asio.hpp'): conf.env.Append(CPPFLAGS = ' -DHAVE_ASIO_HPP') else: print 'asio headers not found or not usable' Exit(1) # asio/ssl if ssl == 1: if conf.CheckCXXHeader('asio/ssl.hpp'): conf.env.Append(CPPFLAGS = ' -DHAVE_ASIO_SSL_HPP') else: print 'ssl support required but asio/ssl.hpp not found or not usable' print 'compile with ssl=0 or check that openssl devel headers are usable' Exit(1) if static_ssl == 0: if conf.CheckLib('ssl'): conf.CheckLib('crypto') else: print 'ssl support required but openssl library not found' print 'compile with ssl=0 or check that openssl library is usable' Exit(1) else: conf.env.Append(LIBPATH = [with_ssl]) if conf.CheckLib('libssl.a', autoadd=0) or \ conf.CheckLib('libcrypto.a', autoadd=0) or \ conf.CheckLib('libz.a', autoadd=0): pass else: print 'ssl support required but openssl library (static) not found' print 'compile with ssl=0 or check that' print 'openssl static librares - libssl.a, libcrypto.a, libz.a are available' Exit(1) # these will be used only with our softaware if strict_build_flags == 1: conf.env.Append(CPPFLAGS = ' -Werror') conf.env.Append(CCFLAGS = ' -pedantic') conf.env.Append(CXXFLAGS = ' -Weffc++ -Wold-style-cast') # if 'clang' in cc: # conf.env.Append(CPPFLAGS = ' -Qunused-arguments -Wno-tautological-compare -D_Bool=bool') env = conf.Finish() Export('env', 'sysname', 'libboost_program_options', 'static_ssl', 'with_ssl') # # Actions to build .dSYM directories, containing debugging information for Darwin # if sysname == 'darwin' and int(debug) >= 0 and int(debug) < 3: env['LINKCOM'] = [env['LINKCOM'], 'dsymutil $TARGET'] env['SHLINKCOM'] = [env['SHLINKCOM'], 'dsymutil $TARGET'] # # Set up and export environment for check unit tests # # Clone base from default environment check_env = env.Clone() conf = Configure(check_env) # Check header and library if not conf.CheckHeader('check.h'): print 'Error: check header file not found or not usable' Exit(1) if not conf.CheckLib('check'): print 'Error: check library not found or not usable' Exit(1) conf.Finish() # Note: Don't do this, glibc does not like static linking # Link unit tests statically # check_env.Append(LINKFLAGS = ' -static') # # this follows recipes from http://www.scons.org/wiki/UnitTests # def builder_unit_test(target, source, env): app = str(source[0].abspath) if os.spawnl(os.P_WAIT, app, app)==0: open(str(target[0]),'w').write("PASSED\n") else: return 1 def builder_unit_test_dummy(target, source, env): return 0 # Create a builder for tests if tests == 1: bld = Builder(action = builder_unit_test) else: bld = Builder(action = builder_unit_test_dummy) check_env.Append(BUILDERS = {'Test' : bld}) Export('check_env') # # Run root SConscript with variant_dir # SConscript('SConscript', variant_dir=build_dir) percona-xtradb-cluster-galera/asio/0000755000000000000000000000000012247075736017605 5ustar rootroot00000000000000percona-xtradb-cluster-galera/common/0000755000000000000000000000000012247075736020142 5ustar rootroot00000000000000percona-xtradb-cluster-galera/debian/0000755000000000000000000000000012247075736020074 5ustar rootroot00000000000000percona-xtradb-cluster-galera/galera/0000755000000000000000000000000012247075736020105 5ustar rootroot00000000000000percona-xtradb-cluster-galera/galerautils/0000755000000000000000000000000012247075736021166 5ustar rootroot00000000000000percona-xtradb-cluster-galera/garb/0000755000000000000000000000000012247075736017565 5ustar rootroot00000000000000percona-xtradb-cluster-galera/gcache/0000755000000000000000000000000012247075736020064 5ustar rootroot00000000000000percona-xtradb-cluster-galera/gcomm/0000755000000000000000000000000012247075736017754 5ustar rootroot00000000000000percona-xtradb-cluster-galera/gcs/0000755000000000000000000000000012247075736017426 5ustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/0000755000000000000000000000000012247075736020341 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/0000755000000000000000000000000012247075736020014 5ustar rootroot00000000000000percona-xtradb-cluster-galera/asio/LICENSE_1_0.txt0000644000000000000000000000247212247075736022074 0ustar rootroot00000000000000Boost Software License - Version 1.0 - August 17th, 2003 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. percona-xtradb-cluster-galera/asio/asio/0000755000000000000000000000000012247075736020540 5ustar rootroot00000000000000percona-xtradb-cluster-galera/asio/asio.hpp0000644000000000000000000000673512247075736021264 0ustar rootroot00000000000000// // asio.hpp // ~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_HPP #define ASIO_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/basic_datagram_socket.hpp" #include "asio/basic_deadline_timer.hpp" #include "asio/basic_io_object.hpp" #include "asio/basic_raw_socket.hpp" #include "asio/basic_serial_port.hpp" #include "asio/basic_socket_acceptor.hpp" #include "asio/basic_socket_iostream.hpp" #include "asio/basic_socket_streambuf.hpp" #include "asio/basic_stream_socket.hpp" #include "asio/basic_streambuf.hpp" #include "asio/buffer.hpp" #include "asio/buffered_read_stream_fwd.hpp" #include "asio/buffered_read_stream.hpp" #include "asio/buffered_stream_fwd.hpp" #include "asio/buffered_stream.hpp" #include "asio/buffered_write_stream_fwd.hpp" #include "asio/buffered_write_stream.hpp" #include "asio/buffers_iterator.hpp" #include "asio/completion_condition.hpp" #include "asio/datagram_socket_service.hpp" #include "asio/deadline_timer_service.hpp" #include "asio/deadline_timer.hpp" #include "asio/error.hpp" #include "asio/error_code.hpp" #include "asio/handler_alloc_hook.hpp" #include "asio/handler_invoke_hook.hpp" #include "asio/io_service.hpp" #include "asio/ip/address.hpp" #include "asio/ip/address_v4.hpp" #include "asio/ip/address_v6.hpp" #include "asio/ip/basic_endpoint.hpp" #include "asio/ip/basic_resolver.hpp" #include "asio/ip/basic_resolver_entry.hpp" #include "asio/ip/basic_resolver_iterator.hpp" #include "asio/ip/basic_resolver_query.hpp" #include "asio/ip/host_name.hpp" #include "asio/ip/icmp.hpp" #include "asio/ip/multicast.hpp" #include "asio/ip/resolver_query_base.hpp" #include "asio/ip/resolver_service.hpp" #include "asio/ip/tcp.hpp" #include "asio/ip/udp.hpp" #include "asio/ip/unicast.hpp" #include "asio/ip/v6_only.hpp" #include "asio/is_read_buffered.hpp" #include "asio/is_write_buffered.hpp" #include "asio/local/basic_endpoint.hpp" #include "asio/local/connect_pair.hpp" #include "asio/local/datagram_protocol.hpp" #include "asio/local/stream_protocol.hpp" #include "asio/placeholders.hpp" #include "asio/posix/basic_descriptor.hpp" #include "asio/posix/basic_stream_descriptor.hpp" #include "asio/posix/descriptor_base.hpp" #include "asio/posix/stream_descriptor.hpp" #include "asio/posix/stream_descriptor_service.hpp" #include "asio/raw_socket_service.hpp" #include "asio/read.hpp" #include "asio/read_at.hpp" #include "asio/read_until.hpp" #include "asio/serial_port.hpp" #include "asio/serial_port_base.hpp" #include "asio/serial_port_service.hpp" #include "asio/socket_acceptor_service.hpp" #include "asio/socket_base.hpp" #include "asio/strand.hpp" #include "asio/stream_socket_service.hpp" #include "asio/streambuf.hpp" #include "asio/system_error.hpp" #include "asio/thread.hpp" #include "asio/time_traits.hpp" #include "asio/version.hpp" #include "asio/windows/basic_handle.hpp" #include "asio/windows/basic_random_access_handle.hpp" #include "asio/windows/basic_stream_handle.hpp" #include "asio/windows/overlapped_ptr.hpp" #include "asio/windows/random_access_handle.hpp" #include "asio/windows/random_access_handle_service.hpp" #include "asio/windows/stream_handle.hpp" #include "asio/windows/stream_handle_service.hpp" #include "asio/write.hpp" #include "asio/write_at.hpp" #endif // ASIO_HPP percona-xtradb-cluster-galera/asio/version.txt0000644000000000000000000000000612247075736022027 0ustar rootroot000000000000001.4.8 percona-xtradb-cluster-galera/asio/asio/basic_datagram_socket.hpp0000644000000000000000000007524712247075736025561 0ustar rootroot00000000000000// // basic_datagram_socket.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_BASIC_DATAGRAM_SOCKET_HPP #define ASIO_BASIC_DATAGRAM_SOCKET_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/basic_socket.hpp" #include "asio/datagram_socket_service.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { /// Provides datagram-oriented socket functionality. /** * The basic_datagram_socket class template provides asynchronous and blocking * datagram-oriented socket functionality. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. */ template > class basic_datagram_socket : public basic_socket { public: /// The native representation of a socket. typedef typename DatagramSocketService::native_type native_type; /// The protocol type. typedef Protocol protocol_type; /// The endpoint type. typedef typename Protocol::endpoint endpoint_type; /// Construct a basic_datagram_socket without opening it. /** * This constructor creates a datagram socket without opening it. The open() * function must be called before data can be sent or received on the socket. * * @param io_service The io_service object that the datagram socket will use * to dispatch handlers for any asynchronous operations performed on the * socket. */ explicit basic_datagram_socket(asio::io_service& io_service) : basic_socket(io_service) { } /// Construct and open a basic_datagram_socket. /** * This constructor creates and opens a datagram socket. * * @param io_service The io_service object that the datagram socket will use * to dispatch handlers for any asynchronous operations performed on the * socket. * * @param protocol An object specifying protocol parameters to be used. * * @throws asio::system_error Thrown on failure. */ basic_datagram_socket(asio::io_service& io_service, const protocol_type& protocol) : basic_socket(io_service, protocol) { } /// Construct a basic_datagram_socket, opening it and binding it to the given /// local endpoint. /** * This constructor creates a datagram socket and automatically opens it bound * to the specified endpoint on the local machine. The protocol used is the * protocol associated with the given endpoint. * * @param io_service The io_service object that the datagram socket will use * to dispatch handlers for any asynchronous operations performed on the * socket. * * @param endpoint An endpoint on the local machine to which the datagram * socket will be bound. * * @throws asio::system_error Thrown on failure. */ basic_datagram_socket(asio::io_service& io_service, const endpoint_type& endpoint) : basic_socket(io_service, endpoint) { } /// Construct a basic_datagram_socket on an existing native socket. /** * This constructor creates a datagram socket object to hold an existing * native socket. * * @param io_service The io_service object that the datagram socket will use * to dispatch handlers for any asynchronous operations performed on the * socket. * * @param protocol An object specifying protocol parameters to be used. * * @param native_socket The new underlying socket implementation. * * @throws asio::system_error Thrown on failure. */ basic_datagram_socket(asio::io_service& io_service, const protocol_type& protocol, const native_type& native_socket) : basic_socket( io_service, protocol, native_socket) { } /// Send some data on a connected socket. /** * This function is used to send data on the datagram socket. The function * call will block until the data has been sent successfully or an error * occurs. * * @param buffers One ore more data buffers to be sent on the socket. * * @returns The number of bytes sent. * * @throws asio::system_error Thrown on failure. * * @note The send operation can only be used with a connected socket. Use * the send_to function to send data on an unconnected datagram socket. * * @par Example * To send a single data buffer use the @ref buffer function as follows: * @code socket.send(asio::buffer(data, size)); @endcode * See the @ref buffer documentation for information on sending multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template std::size_t send(const ConstBufferSequence& buffers) { asio::error_code ec; std::size_t s = this->service.send(this->implementation, buffers, 0, ec); asio::detail::throw_error(ec); return s; } /// Send some data on a connected socket. /** * This function is used to send data on the datagram socket. The function * call will block until the data has been sent successfully or an error * occurs. * * @param buffers One ore more data buffers to be sent on the socket. * * @param flags Flags specifying how the send call is to be made. * * @returns The number of bytes sent. * * @throws asio::system_error Thrown on failure. * * @note The send operation can only be used with a connected socket. Use * the send_to function to send data on an unconnected datagram socket. */ template std::size_t send(const ConstBufferSequence& buffers, socket_base::message_flags flags) { asio::error_code ec; std::size_t s = this->service.send( this->implementation, buffers, flags, ec); asio::detail::throw_error(ec); return s; } /// Send some data on a connected socket. /** * This function is used to send data on the datagram socket. The function * call will block until the data has been sent successfully or an error * occurs. * * @param buffers One or more data buffers to be sent on the socket. * * @param flags Flags specifying how the send call is to be made. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes sent. * * @note The send operation can only be used with a connected socket. Use * the send_to function to send data on an unconnected datagram socket. */ template std::size_t send(const ConstBufferSequence& buffers, socket_base::message_flags flags, asio::error_code& ec) { return this->service.send(this->implementation, buffers, flags, ec); } /// Start an asynchronous send on a connected socket. /** * This function is used to send data on the datagram socket. The function * call will block until the data has been sent successfully or an error * occurs. * * @param buffers One or more data buffers to be sent on the socket. Although * the buffers object may be copied as necessary, ownership of the underlying * memory blocks is retained by the caller, which must guarantee that they * remain valid until the handler is called. * * @param handler The handler to be called when the send operation completes. * Copies will be made of the handler as required. The function signature of * the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes sent. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note The async_send operation can only be used with a connected socket. * Use the async_send_to function to send data on an unconnected datagram * socket. * * @par Example * To send a single data buffer use the @ref buffer function as follows: * @code * socket.async_send(asio::buffer(data, size), handler); * @endcode * See the @ref buffer documentation for information on sending multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template void async_send(const ConstBufferSequence& buffers, WriteHandler handler) { this->service.async_send(this->implementation, buffers, 0, handler); } /// Start an asynchronous send on a connected socket. /** * This function is used to send data on the datagram socket. The function * call will block until the data has been sent successfully or an error * occurs. * * @param buffers One or more data buffers to be sent on the socket. Although * the buffers object may be copied as necessary, ownership of the underlying * memory blocks is retained by the caller, which must guarantee that they * remain valid until the handler is called. * * @param flags Flags specifying how the send call is to be made. * * @param handler The handler to be called when the send operation completes. * Copies will be made of the handler as required. The function signature of * the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes sent. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note The async_send operation can only be used with a connected socket. * Use the async_send_to function to send data on an unconnected datagram * socket. */ template void async_send(const ConstBufferSequence& buffers, socket_base::message_flags flags, WriteHandler handler) { this->service.async_send(this->implementation, buffers, flags, handler); } /// Send a datagram to the specified endpoint. /** * This function is used to send a datagram to the specified remote endpoint. * The function call will block until the data has been sent successfully or * an error occurs. * * @param buffers One or more data buffers to be sent to the remote endpoint. * * @param destination The remote endpoint to which the data will be sent. * * @returns The number of bytes sent. * * @throws asio::system_error Thrown on failure. * * @par Example * To send a single data buffer use the @ref buffer function as follows: * @code * asio::ip::udp::endpoint destination( * asio::ip::address::from_string("1.2.3.4"), 12345); * socket.send_to(asio::buffer(data, size), destination); * @endcode * See the @ref buffer documentation for information on sending multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template std::size_t send_to(const ConstBufferSequence& buffers, const endpoint_type& destination) { asio::error_code ec; std::size_t s = this->service.send_to( this->implementation, buffers, destination, 0, ec); asio::detail::throw_error(ec); return s; } /// Send a datagram to the specified endpoint. /** * This function is used to send a datagram to the specified remote endpoint. * The function call will block until the data has been sent successfully or * an error occurs. * * @param buffers One or more data buffers to be sent to the remote endpoint. * * @param destination The remote endpoint to which the data will be sent. * * @param flags Flags specifying how the send call is to be made. * * @returns The number of bytes sent. * * @throws asio::system_error Thrown on failure. */ template std::size_t send_to(const ConstBufferSequence& buffers, const endpoint_type& destination, socket_base::message_flags flags) { asio::error_code ec; std::size_t s = this->service.send_to( this->implementation, buffers, destination, flags, ec); asio::detail::throw_error(ec); return s; } /// Send a datagram to the specified endpoint. /** * This function is used to send a datagram to the specified remote endpoint. * The function call will block until the data has been sent successfully or * an error occurs. * * @param buffers One or more data buffers to be sent to the remote endpoint. * * @param destination The remote endpoint to which the data will be sent. * * @param flags Flags specifying how the send call is to be made. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes sent. */ template std::size_t send_to(const ConstBufferSequence& buffers, const endpoint_type& destination, socket_base::message_flags flags, asio::error_code& ec) { return this->service.send_to(this->implementation, buffers, destination, flags, ec); } /// Start an asynchronous send. /** * This function is used to asynchronously send a datagram to the specified * remote endpoint. The function call always returns immediately. * * @param buffers One or more data buffers to be sent to the remote endpoint. * Although the buffers object may be copied as necessary, ownership of the * underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param destination The remote endpoint to which the data will be sent. * Copies will be made of the endpoint as required. * * @param handler The handler to be called when the send operation completes. * Copies will be made of the handler as required. The function signature of * the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes sent. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @par Example * To send a single data buffer use the @ref buffer function as follows: * @code * asio::ip::udp::endpoint destination( * asio::ip::address::from_string("1.2.3.4"), 12345); * socket.async_send_to( * asio::buffer(data, size), destination, handler); * @endcode * See the @ref buffer documentation for information on sending multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template void async_send_to(const ConstBufferSequence& buffers, const endpoint_type& destination, WriteHandler handler) { this->service.async_send_to(this->implementation, buffers, destination, 0, handler); } /// Start an asynchronous send. /** * This function is used to asynchronously send a datagram to the specified * remote endpoint. The function call always returns immediately. * * @param buffers One or more data buffers to be sent to the remote endpoint. * Although the buffers object may be copied as necessary, ownership of the * underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param flags Flags specifying how the send call is to be made. * * @param destination The remote endpoint to which the data will be sent. * Copies will be made of the endpoint as required. * * @param handler The handler to be called when the send operation completes. * Copies will be made of the handler as required. The function signature of * the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes sent. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). */ template void async_send_to(const ConstBufferSequence& buffers, const endpoint_type& destination, socket_base::message_flags flags, WriteHandler handler) { this->service.async_send_to(this->implementation, buffers, destination, flags, handler); } /// Receive some data on a connected socket. /** * This function is used to receive data on the datagram socket. The function * call will block until data has been received successfully or an error * occurs. * * @param buffers One or more buffers into which the data will be received. * * @returns The number of bytes received. * * @throws asio::system_error Thrown on failure. * * @note The receive operation can only be used with a connected socket. Use * the receive_from function to receive data on an unconnected datagram * socket. * * @par Example * To receive into a single data buffer use the @ref buffer function as * follows: * @code socket.receive(asio::buffer(data, size)); @endcode * See the @ref buffer documentation for information on receiving into * multiple buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template std::size_t receive(const MutableBufferSequence& buffers) { asio::error_code ec; std::size_t s = this->service.receive( this->implementation, buffers, 0, ec); asio::detail::throw_error(ec); return s; } /// Receive some data on a connected socket. /** * This function is used to receive data on the datagram socket. The function * call will block until data has been received successfully or an error * occurs. * * @param buffers One or more buffers into which the data will be received. * * @param flags Flags specifying how the receive call is to be made. * * @returns The number of bytes received. * * @throws asio::system_error Thrown on failure. * * @note The receive operation can only be used with a connected socket. Use * the receive_from function to receive data on an unconnected datagram * socket. */ template std::size_t receive(const MutableBufferSequence& buffers, socket_base::message_flags flags) { asio::error_code ec; std::size_t s = this->service.receive( this->implementation, buffers, flags, ec); asio::detail::throw_error(ec); return s; } /// Receive some data on a connected socket. /** * This function is used to receive data on the datagram socket. The function * call will block until data has been received successfully or an error * occurs. * * @param buffers One or more buffers into which the data will be received. * * @param flags Flags specifying how the receive call is to be made. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes received. * * @note The receive operation can only be used with a connected socket. Use * the receive_from function to receive data on an unconnected datagram * socket. */ template std::size_t receive(const MutableBufferSequence& buffers, socket_base::message_flags flags, asio::error_code& ec) { return this->service.receive(this->implementation, buffers, flags, ec); } /// Start an asynchronous receive on a connected socket. /** * This function is used to asynchronously receive data from the datagram * socket. The function call always returns immediately. * * @param buffers One or more buffers into which the data will be received. * Although the buffers object may be copied as necessary, ownership of the * underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param handler The handler to be called when the receive operation * completes. Copies will be made of the handler as required. The function * signature of the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes received. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note The async_receive operation can only be used with a connected socket. * Use the async_receive_from function to receive data on an unconnected * datagram socket. * * @par Example * To receive into a single data buffer use the @ref buffer function as * follows: * @code * socket.async_receive(asio::buffer(data, size), handler); * @endcode * See the @ref buffer documentation for information on receiving into * multiple buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template void async_receive(const MutableBufferSequence& buffers, ReadHandler handler) { this->service.async_receive(this->implementation, buffers, 0, handler); } /// Start an asynchronous receive on a connected socket. /** * This function is used to asynchronously receive data from the datagram * socket. The function call always returns immediately. * * @param buffers One or more buffers into which the data will be received. * Although the buffers object may be copied as necessary, ownership of the * underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param flags Flags specifying how the receive call is to be made. * * @param handler The handler to be called when the receive operation * completes. Copies will be made of the handler as required. The function * signature of the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes received. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note The async_receive operation can only be used with a connected socket. * Use the async_receive_from function to receive data on an unconnected * datagram socket. */ template void async_receive(const MutableBufferSequence& buffers, socket_base::message_flags flags, ReadHandler handler) { this->service.async_receive(this->implementation, buffers, flags, handler); } /// Receive a datagram with the endpoint of the sender. /** * This function is used to receive a datagram. The function call will block * until data has been received successfully or an error occurs. * * @param buffers One or more buffers into which the data will be received. * * @param sender_endpoint An endpoint object that receives the endpoint of * the remote sender of the datagram. * * @returns The number of bytes received. * * @throws asio::system_error Thrown on failure. * * @par Example * To receive into a single data buffer use the @ref buffer function as * follows: * @code * asio::ip::udp::endpoint sender_endpoint; * socket.receive_from( * asio::buffer(data, size), sender_endpoint); * @endcode * See the @ref buffer documentation for information on receiving into * multiple buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template std::size_t receive_from(const MutableBufferSequence& buffers, endpoint_type& sender_endpoint) { asio::error_code ec; std::size_t s = this->service.receive_from( this->implementation, buffers, sender_endpoint, 0, ec); asio::detail::throw_error(ec); return s; } /// Receive a datagram with the endpoint of the sender. /** * This function is used to receive a datagram. The function call will block * until data has been received successfully or an error occurs. * * @param buffers One or more buffers into which the data will be received. * * @param sender_endpoint An endpoint object that receives the endpoint of * the remote sender of the datagram. * * @param flags Flags specifying how the receive call is to be made. * * @returns The number of bytes received. * * @throws asio::system_error Thrown on failure. */ template std::size_t receive_from(const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, socket_base::message_flags flags) { asio::error_code ec; std::size_t s = this->service.receive_from( this->implementation, buffers, sender_endpoint, flags, ec); asio::detail::throw_error(ec); return s; } /// Receive a datagram with the endpoint of the sender. /** * This function is used to receive a datagram. The function call will block * until data has been received successfully or an error occurs. * * @param buffers One or more buffers into which the data will be received. * * @param sender_endpoint An endpoint object that receives the endpoint of * the remote sender of the datagram. * * @param flags Flags specifying how the receive call is to be made. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes received. */ template std::size_t receive_from(const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, socket_base::message_flags flags, asio::error_code& ec) { return this->service.receive_from(this->implementation, buffers, sender_endpoint, flags, ec); } /// Start an asynchronous receive. /** * This function is used to asynchronously receive a datagram. The function * call always returns immediately. * * @param buffers One or more buffers into which the data will be received. * Although the buffers object may be copied as necessary, ownership of the * underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param sender_endpoint An endpoint object that receives the endpoint of * the remote sender of the datagram. Ownership of the sender_endpoint object * is retained by the caller, which must guarantee that it is valid until the * handler is called. * * @param handler The handler to be called when the receive operation * completes. Copies will be made of the handler as required. The function * signature of the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes received. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @par Example * To receive into a single data buffer use the @ref buffer function as * follows: * @code socket.async_receive_from( * asio::buffer(data, size), 0, sender_endpoint, handler); @endcode * See the @ref buffer documentation for information on receiving into * multiple buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template void async_receive_from(const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, ReadHandler handler) { this->service.async_receive_from(this->implementation, buffers, sender_endpoint, 0, handler); } /// Start an asynchronous receive. /** * This function is used to asynchronously receive a datagram. The function * call always returns immediately. * * @param buffers One or more buffers into which the data will be received. * Although the buffers object may be copied as necessary, ownership of the * underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param sender_endpoint An endpoint object that receives the endpoint of * the remote sender of the datagram. Ownership of the sender_endpoint object * is retained by the caller, which must guarantee that it is valid until the * handler is called. * * @param flags Flags specifying how the receive call is to be made. * * @param handler The handler to be called when the receive operation * completes. Copies will be made of the handler as required. The function * signature of the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes received. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). */ template void async_receive_from(const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, socket_base::message_flags flags, ReadHandler handler) { this->service.async_receive_from(this->implementation, buffers, sender_endpoint, flags, handler); } }; } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_BASIC_DATAGRAM_SOCKET_HPP percona-xtradb-cluster-galera/asio/asio/basic_deadline_timer.hpp0000644000000000000000000003502212247075736025361 0ustar rootroot00000000000000// // basic_deadline_timer.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_BASIC_DEADLINE_TIMER_HPP #define ASIO_BASIC_DEADLINE_TIMER_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/basic_io_object.hpp" #include "asio/deadline_timer_service.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { /// Provides waitable timer functionality. /** * The basic_deadline_timer class template provides the ability to perform a * blocking or asynchronous wait for a timer to expire. * * A deadline timer is always in one of two states: "expired" or "not expired". * If the wait() or async_wait() function is called on an expired timer, the * wait operation will complete immediately. * * Most applications will use the asio::deadline_timer typedef. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. * * @par Examples * Performing a blocking wait: * @code * // Construct a timer without setting an expiry time. * asio::deadline_timer timer(io_service); * * // Set an expiry time relative to now. * timer.expires_from_now(boost::posix_time::seconds(5)); * * // Wait for the timer to expire. * timer.wait(); * @endcode * * @par * Performing an asynchronous wait: * @code * void handler(const asio::error_code& error) * { * if (!error) * { * // Timer expired. * } * } * * ... * * // Construct a timer with an absolute expiry time. * asio::deadline_timer timer(io_service, * boost::posix_time::time_from_string("2005-12-07 23:59:59.000")); * * // Start an asynchronous wait. * timer.async_wait(handler); * @endcode * * @par Changing an active deadline_timer's expiry time * * Changing the expiry time of a timer while there are pending asynchronous * waits causes those wait operations to be cancelled. To ensure that the action * associated with the timer is performed only once, use something like this: * used: * * @code * void on_some_event() * { * if (my_timer.expires_from_now(seconds(5)) > 0) * { * // We managed to cancel the timer. Start new asynchronous wait. * my_timer.async_wait(on_timeout); * } * else * { * // Too late, timer has already expired! * } * } * * void on_timeout(const asio::error_code& e) * { * if (e != asio::error::operation_aborted) * { * // Timer was not cancelled, take necessary action. * } * } * @endcode * * @li The asio::basic_deadline_timer::expires_from_now() function * cancels any pending asynchronous waits, and returns the number of * asynchronous waits that were cancelled. If it returns 0 then you were too * late and the wait handler has already been executed, or will soon be * executed. If it returns 1 then the wait handler was successfully cancelled. * * @li If a wait handler is cancelled, the asio::error_code passed to * it contains the value asio::error::operation_aborted. */ template , typename TimerService = deadline_timer_service > class basic_deadline_timer : public basic_io_object { public: /// The time traits type. typedef TimeTraits traits_type; /// The time type. typedef typename traits_type::time_type time_type; /// The duration type. typedef typename traits_type::duration_type duration_type; /// Constructor. /** * This constructor creates a timer without setting an expiry time. The * expires_at() or expires_from_now() functions must be called to set an * expiry time before the timer can be waited on. * * @param io_service The io_service object that the timer will use to dispatch * handlers for any asynchronous operations performed on the timer. */ explicit basic_deadline_timer(asio::io_service& io_service) : basic_io_object(io_service) { } /// Constructor to set a particular expiry time as an absolute time. /** * This constructor creates a timer and sets the expiry time. * * @param io_service The io_service object that the timer will use to dispatch * handlers for any asynchronous operations performed on the timer. * * @param expiry_time The expiry time to be used for the timer, expressed * as an absolute time. */ basic_deadline_timer(asio::io_service& io_service, const time_type& expiry_time) : basic_io_object(io_service) { asio::error_code ec; this->service.expires_at(this->implementation, expiry_time, ec); asio::detail::throw_error(ec); } /// Constructor to set a particular expiry time relative to now. /** * This constructor creates a timer and sets the expiry time. * * @param io_service The io_service object that the timer will use to dispatch * handlers for any asynchronous operations performed on the timer. * * @param expiry_time The expiry time to be used for the timer, relative to * now. */ basic_deadline_timer(asio::io_service& io_service, const duration_type& expiry_time) : basic_io_object(io_service) { asio::error_code ec; this->service.expires_from_now(this->implementation, expiry_time, ec); asio::detail::throw_error(ec); } /// Cancel any asynchronous operations that are waiting on the timer. /** * This function forces the completion of any pending asynchronous wait * operations against the timer. The handler for each cancelled operation will * be invoked with the asio::error::operation_aborted error code. * * Cancelling the timer does not change the expiry time. * * @return The number of asynchronous operations that were cancelled. * * @throws asio::system_error Thrown on failure. * * @note If the timer has already expired when cancel() is called, then the * handlers for asynchronous wait operations will: * * @li have already been invoked; or * * @li have been queued for invocation in the near future. * * These handlers can no longer be cancelled, and therefore are passed an * error code that indicates the successful completion of the wait operation. */ std::size_t cancel() { asio::error_code ec; std::size_t s = this->service.cancel(this->implementation, ec); asio::detail::throw_error(ec); return s; } /// Cancel any asynchronous operations that are waiting on the timer. /** * This function forces the completion of any pending asynchronous wait * operations against the timer. The handler for each cancelled operation will * be invoked with the asio::error::operation_aborted error code. * * Cancelling the timer does not change the expiry time. * * @param ec Set to indicate what error occurred, if any. * * @return The number of asynchronous operations that were cancelled. * * @note If the timer has already expired when cancel() is called, then the * handlers for asynchronous wait operations will: * * @li have already been invoked; or * * @li have been queued for invocation in the near future. * * These handlers can no longer be cancelled, and therefore are passed an * error code that indicates the successful completion of the wait operation. */ std::size_t cancel(asio::error_code& ec) { return this->service.cancel(this->implementation, ec); } /// Get the timer's expiry time as an absolute time. /** * This function may be used to obtain the timer's current expiry time. * Whether the timer has expired or not does not affect this value. */ time_type expires_at() const { return this->service.expires_at(this->implementation); } /// Set the timer's expiry time as an absolute time. /** * This function sets the expiry time. Any pending asynchronous wait * operations will be cancelled. The handler for each cancelled operation will * be invoked with the asio::error::operation_aborted error code. * * @param expiry_time The expiry time to be used for the timer. * * @return The number of asynchronous operations that were cancelled. * * @throws asio::system_error Thrown on failure. * * @note If the timer has already expired when expires_at() is called, then * the handlers for asynchronous wait operations will: * * @li have already been invoked; or * * @li have been queued for invocation in the near future. * * These handlers can no longer be cancelled, and therefore are passed an * error code that indicates the successful completion of the wait operation. */ std::size_t expires_at(const time_type& expiry_time) { asio::error_code ec; std::size_t s = this->service.expires_at( this->implementation, expiry_time, ec); asio::detail::throw_error(ec); return s; } /// Set the timer's expiry time as an absolute time. /** * This function sets the expiry time. Any pending asynchronous wait * operations will be cancelled. The handler for each cancelled operation will * be invoked with the asio::error::operation_aborted error code. * * @param expiry_time The expiry time to be used for the timer. * * @param ec Set to indicate what error occurred, if any. * * @return The number of asynchronous operations that were cancelled. * * @note If the timer has already expired when expires_at() is called, then * the handlers for asynchronous wait operations will: * * @li have already been invoked; or * * @li have been queued for invocation in the near future. * * These handlers can no longer be cancelled, and therefore are passed an * error code that indicates the successful completion of the wait operation. */ std::size_t expires_at(const time_type& expiry_time, asio::error_code& ec) { return this->service.expires_at(this->implementation, expiry_time, ec); } /// Get the timer's expiry time relative to now. /** * This function may be used to obtain the timer's current expiry time. * Whether the timer has expired or not does not affect this value. */ duration_type expires_from_now() const { return this->service.expires_from_now(this->implementation); } /// Set the timer's expiry time relative to now. /** * This function sets the expiry time. Any pending asynchronous wait * operations will be cancelled. The handler for each cancelled operation will * be invoked with the asio::error::operation_aborted error code. * * @param expiry_time The expiry time to be used for the timer. * * @return The number of asynchronous operations that were cancelled. * * @throws asio::system_error Thrown on failure. * * @note If the timer has already expired when expires_from_now() is called, * then the handlers for asynchronous wait operations will: * * @li have already been invoked; or * * @li have been queued for invocation in the near future. * * These handlers can no longer be cancelled, and therefore are passed an * error code that indicates the successful completion of the wait operation. */ std::size_t expires_from_now(const duration_type& expiry_time) { asio::error_code ec; std::size_t s = this->service.expires_from_now( this->implementation, expiry_time, ec); asio::detail::throw_error(ec); return s; } /// Set the timer's expiry time relative to now. /** * This function sets the expiry time. Any pending asynchronous wait * operations will be cancelled. The handler for each cancelled operation will * be invoked with the asio::error::operation_aborted error code. * * @param expiry_time The expiry time to be used for the timer. * * @param ec Set to indicate what error occurred, if any. * * @return The number of asynchronous operations that were cancelled. * * @note If the timer has already expired when expires_from_now() is called, * then the handlers for asynchronous wait operations will: * * @li have already been invoked; or * * @li have been queued for invocation in the near future. * * These handlers can no longer be cancelled, and therefore are passed an * error code that indicates the successful completion of the wait operation. */ std::size_t expires_from_now(const duration_type& expiry_time, asio::error_code& ec) { return this->service.expires_from_now( this->implementation, expiry_time, ec); } /// Perform a blocking wait on the timer. /** * This function is used to wait for the timer to expire. This function * blocks and does not return until the timer has expired. * * @throws asio::system_error Thrown on failure. */ void wait() { asio::error_code ec; this->service.wait(this->implementation, ec); asio::detail::throw_error(ec); } /// Perform a blocking wait on the timer. /** * This function is used to wait for the timer to expire. This function * blocks and does not return until the timer has expired. * * @param ec Set to indicate what error occurred, if any. */ void wait(asio::error_code& ec) { this->service.wait(this->implementation, ec); } /// Start an asynchronous wait on the timer. /** * This function may be used to initiate an asynchronous wait against the * timer. It always returns immediately. * * For each call to async_wait(), the supplied handler will be called exactly * once. The handler will be called when: * * @li The timer has expired. * * @li The timer was cancelled, in which case the handler is passed the error * code asio::error::operation_aborted. * * @param handler The handler to be called when the timer expires. Copies * will be made of the handler as required. The function signature of the * handler must be: * @code void handler( * const asio::error_code& error // Result of operation. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). */ template void async_wait(WaitHandler handler) { this->service.async_wait(this->implementation, handler); } }; } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_BASIC_DEADLINE_TIMER_HPP percona-xtradb-cluster-galera/asio/asio/basic_io_object.hpp0000644000000000000000000000523112247075736024350 0ustar rootroot00000000000000// // basic_io_object.hpp // ~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_BASIC_IO_OBJECT_HPP #define ASIO_BASIC_IO_OBJECT_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/detail/noncopyable.hpp" #include "asio/io_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { /// Base class for all I/O objects. template class basic_io_object : private noncopyable { public: /// The type of the service that will be used to provide I/O operations. typedef IoObjectService service_type; /// The underlying implementation type of I/O object. typedef typename service_type::implementation_type implementation_type; /// (Deprecated: use get_io_service().) Get the io_service associated with /// the object. /** * This function may be used to obtain the io_service object that the I/O * object uses to dispatch handlers for asynchronous operations. * * @return A reference to the io_service object that the I/O object will use * to dispatch handlers. Ownership is not transferred to the caller. */ asio::io_service& io_service() { return service.get_io_service(); } /// Get the io_service associated with the object. /** * This function may be used to obtain the io_service object that the I/O * object uses to dispatch handlers for asynchronous operations. * * @return A reference to the io_service object that the I/O object will use * to dispatch handlers. Ownership is not transferred to the caller. */ asio::io_service& get_io_service() { return service.get_io_service(); } protected: /// Construct a basic_io_object. /** * Performs: * @code service.construct(implementation); @endcode */ explicit basic_io_object(asio::io_service& io_service) : service(asio::use_service(io_service)) { service.construct(implementation); } /// Protected destructor to prevent deletion through this type. /** * Performs: * @code service.destroy(implementation); @endcode */ ~basic_io_object() { service.destroy(implementation); } /// The service associated with the I/O object. service_type& service; /// The underlying implementation of the I/O object. implementation_type implementation; }; } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_BASIC_IO_OBJECT_HPP percona-xtradb-cluster-galera/asio/asio/basic_raw_socket.hpp0000644000000000000000000007450712247075736024570 0ustar rootroot00000000000000// // basic_raw_socket.hpp // ~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_BASIC_RAW_SOCKET_HPP #define ASIO_BASIC_RAW_SOCKET_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/basic_socket.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/raw_socket_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { /// Provides raw-oriented socket functionality. /** * The basic_raw_socket class template provides asynchronous and blocking * raw-oriented socket functionality. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. */ template > class basic_raw_socket : public basic_socket { public: /// The native representation of a socket. typedef typename RawSocketService::native_type native_type; /// The protocol type. typedef Protocol protocol_type; /// The endpoint type. typedef typename Protocol::endpoint endpoint_type; /// Construct a basic_raw_socket without opening it. /** * This constructor creates a raw socket without opening it. The open() * function must be called before data can be sent or received on the socket. * * @param io_service The io_service object that the raw socket will use * to dispatch handlers for any asynchronous operations performed on the * socket. */ explicit basic_raw_socket(asio::io_service& io_service) : basic_socket(io_service) { } /// Construct and open a basic_raw_socket. /** * This constructor creates and opens a raw socket. * * @param io_service The io_service object that the raw socket will use * to dispatch handlers for any asynchronous operations performed on the * socket. * * @param protocol An object specifying protocol parameters to be used. * * @throws asio::system_error Thrown on failure. */ basic_raw_socket(asio::io_service& io_service, const protocol_type& protocol) : basic_socket(io_service, protocol) { } /// Construct a basic_raw_socket, opening it and binding it to the given /// local endpoint. /** * This constructor creates a raw socket and automatically opens it bound * to the specified endpoint on the local machine. The protocol used is the * protocol associated with the given endpoint. * * @param io_service The io_service object that the raw socket will use * to dispatch handlers for any asynchronous operations performed on the * socket. * * @param endpoint An endpoint on the local machine to which the raw * socket will be bound. * * @throws asio::system_error Thrown on failure. */ basic_raw_socket(asio::io_service& io_service, const endpoint_type& endpoint) : basic_socket(io_service, endpoint) { } /// Construct a basic_raw_socket on an existing native socket. /** * This constructor creates a raw socket object to hold an existing * native socket. * * @param io_service The io_service object that the raw socket will use * to dispatch handlers for any asynchronous operations performed on the * socket. * * @param protocol An object specifying protocol parameters to be used. * * @param native_socket The new underlying socket implementation. * * @throws asio::system_error Thrown on failure. */ basic_raw_socket(asio::io_service& io_service, const protocol_type& protocol, const native_type& native_socket) : basic_socket( io_service, protocol, native_socket) { } /// Send some data on a connected socket. /** * This function is used to send data on the raw socket. The function call * will block until the data has been sent successfully or an error occurs. * * @param buffers One ore more data buffers to be sent on the socket. * * @returns The number of bytes sent. * * @throws asio::system_error Thrown on failure. * * @note The send operation can only be used with a connected socket. Use * the send_to function to send data on an unconnected raw socket. * * @par Example * To send a single data buffer use the @ref buffer function as follows: * @code socket.send(asio::buffer(data, size)); @endcode * See the @ref buffer documentation for information on sending multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template std::size_t send(const ConstBufferSequence& buffers) { asio::error_code ec; std::size_t s = this->service.send(this->implementation, buffers, 0, ec); asio::detail::throw_error(ec); return s; } /// Send some data on a connected socket. /** * This function is used to send data on the raw socket. The function call * will block until the data has been sent successfully or an error occurs. * * @param buffers One ore more data buffers to be sent on the socket. * * @param flags Flags specifying how the send call is to be made. * * @returns The number of bytes sent. * * @throws asio::system_error Thrown on failure. * * @note The send operation can only be used with a connected socket. Use * the send_to function to send data on an unconnected raw socket. */ template std::size_t send(const ConstBufferSequence& buffers, socket_base::message_flags flags) { asio::error_code ec; std::size_t s = this->service.send( this->implementation, buffers, flags, ec); asio::detail::throw_error(ec); return s; } /// Send some data on a connected socket. /** * This function is used to send data on the raw socket. The function call * will block until the data has been sent successfully or an error occurs. * * @param buffers One or more data buffers to be sent on the socket. * * @param flags Flags specifying how the send call is to be made. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes sent. * * @note The send operation can only be used with a connected socket. Use * the send_to function to send data on an unconnected raw socket. */ template std::size_t send(const ConstBufferSequence& buffers, socket_base::message_flags flags, asio::error_code& ec) { return this->service.send(this->implementation, buffers, flags, ec); } /// Start an asynchronous send on a connected socket. /** * This function is used to send data on the raw socket. The function call * will block until the data has been sent successfully or an error occurs. * * @param buffers One or more data buffers to be sent on the socket. Although * the buffers object may be copied as necessary, ownership of the underlying * memory blocks is retained by the caller, which must guarantee that they * remain valid until the handler is called. * * @param handler The handler to be called when the send operation completes. * Copies will be made of the handler as required. The function signature of * the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes sent. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note The async_send operation can only be used with a connected socket. * Use the async_send_to function to send data on an unconnected raw * socket. * * @par Example * To send a single data buffer use the @ref buffer function as follows: * @code * socket.async_send(asio::buffer(data, size), handler); * @endcode * See the @ref buffer documentation for information on sending multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template void async_send(const ConstBufferSequence& buffers, WriteHandler handler) { this->service.async_send(this->implementation, buffers, 0, handler); } /// Start an asynchronous send on a connected socket. /** * This function is used to send data on the raw socket. The function call * will block until the data has been sent successfully or an error occurs. * * @param buffers One or more data buffers to be sent on the socket. Although * the buffers object may be copied as necessary, ownership of the underlying * memory blocks is retained by the caller, which must guarantee that they * remain valid until the handler is called. * * @param flags Flags specifying how the send call is to be made. * * @param handler The handler to be called when the send operation completes. * Copies will be made of the handler as required. The function signature of * the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes sent. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note The async_send operation can only be used with a connected socket. * Use the async_send_to function to send data on an unconnected raw * socket. */ template void async_send(const ConstBufferSequence& buffers, socket_base::message_flags flags, WriteHandler handler) { this->service.async_send(this->implementation, buffers, flags, handler); } /// Send raw data to the specified endpoint. /** * This function is used to send raw data to the specified remote endpoint. * The function call will block until the data has been sent successfully or * an error occurs. * * @param buffers One or more data buffers to be sent to the remote endpoint. * * @param destination The remote endpoint to which the data will be sent. * * @returns The number of bytes sent. * * @throws asio::system_error Thrown on failure. * * @par Example * To send a single data buffer use the @ref buffer function as follows: * @code * asio::ip::udp::endpoint destination( * asio::ip::address::from_string("1.2.3.4"), 12345); * socket.send_to(asio::buffer(data, size), destination); * @endcode * See the @ref buffer documentation for information on sending multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template std::size_t send_to(const ConstBufferSequence& buffers, const endpoint_type& destination) { asio::error_code ec; std::size_t s = this->service.send_to( this->implementation, buffers, destination, 0, ec); asio::detail::throw_error(ec); return s; } /// Send raw data to the specified endpoint. /** * This function is used to send raw data to the specified remote endpoint. * The function call will block until the data has been sent successfully or * an error occurs. * * @param buffers One or more data buffers to be sent to the remote endpoint. * * @param destination The remote endpoint to which the data will be sent. * * @param flags Flags specifying how the send call is to be made. * * @returns The number of bytes sent. * * @throws asio::system_error Thrown on failure. */ template std::size_t send_to(const ConstBufferSequence& buffers, const endpoint_type& destination, socket_base::message_flags flags) { asio::error_code ec; std::size_t s = this->service.send_to( this->implementation, buffers, destination, flags, ec); asio::detail::throw_error(ec); return s; } /// Send raw data to the specified endpoint. /** * This function is used to send raw data to the specified remote endpoint. * The function call will block until the data has been sent successfully or * an error occurs. * * @param buffers One or more data buffers to be sent to the remote endpoint. * * @param destination The remote endpoint to which the data will be sent. * * @param flags Flags specifying how the send call is to be made. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes sent. */ template std::size_t send_to(const ConstBufferSequence& buffers, const endpoint_type& destination, socket_base::message_flags flags, asio::error_code& ec) { return this->service.send_to(this->implementation, buffers, destination, flags, ec); } /// Start an asynchronous send. /** * This function is used to asynchronously send raw data to the specified * remote endpoint. The function call always returns immediately. * * @param buffers One or more data buffers to be sent to the remote endpoint. * Although the buffers object may be copied as necessary, ownership of the * underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param destination The remote endpoint to which the data will be sent. * Copies will be made of the endpoint as required. * * @param handler The handler to be called when the send operation completes. * Copies will be made of the handler as required. The function signature of * the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes sent. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @par Example * To send a single data buffer use the @ref buffer function as follows: * @code * asio::ip::udp::endpoint destination( * asio::ip::address::from_string("1.2.3.4"), 12345); * socket.async_send_to( * asio::buffer(data, size), destination, handler); * @endcode * See the @ref buffer documentation for information on sending multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template void async_send_to(const ConstBufferSequence& buffers, const endpoint_type& destination, WriteHandler handler) { this->service.async_send_to(this->implementation, buffers, destination, 0, handler); } /// Start an asynchronous send. /** * This function is used to asynchronously send raw data to the specified * remote endpoint. The function call always returns immediately. * * @param buffers One or more data buffers to be sent to the remote endpoint. * Although the buffers object may be copied as necessary, ownership of the * underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param flags Flags specifying how the send call is to be made. * * @param destination The remote endpoint to which the data will be sent. * Copies will be made of the endpoint as required. * * @param handler The handler to be called when the send operation completes. * Copies will be made of the handler as required. The function signature of * the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes sent. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). */ template void async_send_to(const ConstBufferSequence& buffers, const endpoint_type& destination, socket_base::message_flags flags, WriteHandler handler) { this->service.async_send_to(this->implementation, buffers, destination, flags, handler); } /// Receive some data on a connected socket. /** * This function is used to receive data on the raw socket. The function * call will block until data has been received successfully or an error * occurs. * * @param buffers One or more buffers into which the data will be received. * * @returns The number of bytes received. * * @throws asio::system_error Thrown on failure. * * @note The receive operation can only be used with a connected socket. Use * the receive_from function to receive data on an unconnected raw * socket. * * @par Example * To receive into a single data buffer use the @ref buffer function as * follows: * @code socket.receive(asio::buffer(data, size)); @endcode * See the @ref buffer documentation for information on receiving into * multiple buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template std::size_t receive(const MutableBufferSequence& buffers) { asio::error_code ec; std::size_t s = this->service.receive( this->implementation, buffers, 0, ec); asio::detail::throw_error(ec); return s; } /// Receive some data on a connected socket. /** * This function is used to receive data on the raw socket. The function * call will block until data has been received successfully or an error * occurs. * * @param buffers One or more buffers into which the data will be received. * * @param flags Flags specifying how the receive call is to be made. * * @returns The number of bytes received. * * @throws asio::system_error Thrown on failure. * * @note The receive operation can only be used with a connected socket. Use * the receive_from function to receive data on an unconnected raw * socket. */ template std::size_t receive(const MutableBufferSequence& buffers, socket_base::message_flags flags) { asio::error_code ec; std::size_t s = this->service.receive( this->implementation, buffers, flags, ec); asio::detail::throw_error(ec); return s; } /// Receive some data on a connected socket. /** * This function is used to receive data on the raw socket. The function * call will block until data has been received successfully or an error * occurs. * * @param buffers One or more buffers into which the data will be received. * * @param flags Flags specifying how the receive call is to be made. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes received. * * @note The receive operation can only be used with a connected socket. Use * the receive_from function to receive data on an unconnected raw * socket. */ template std::size_t receive(const MutableBufferSequence& buffers, socket_base::message_flags flags, asio::error_code& ec) { return this->service.receive(this->implementation, buffers, flags, ec); } /// Start an asynchronous receive on a connected socket. /** * This function is used to asynchronously receive data from the raw * socket. The function call always returns immediately. * * @param buffers One or more buffers into which the data will be received. * Although the buffers object may be copied as necessary, ownership of the * underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param handler The handler to be called when the receive operation * completes. Copies will be made of the handler as required. The function * signature of the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes received. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note The async_receive operation can only be used with a connected socket. * Use the async_receive_from function to receive data on an unconnected * raw socket. * * @par Example * To receive into a single data buffer use the @ref buffer function as * follows: * @code * socket.async_receive(asio::buffer(data, size), handler); * @endcode * See the @ref buffer documentation for information on receiving into * multiple buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template void async_receive(const MutableBufferSequence& buffers, ReadHandler handler) { this->service.async_receive(this->implementation, buffers, 0, handler); } /// Start an asynchronous receive on a connected socket. /** * This function is used to asynchronously receive data from the raw * socket. The function call always returns immediately. * * @param buffers One or more buffers into which the data will be received. * Although the buffers object may be copied as necessary, ownership of the * underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param flags Flags specifying how the receive call is to be made. * * @param handler The handler to be called when the receive operation * completes. Copies will be made of the handler as required. The function * signature of the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes received. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note The async_receive operation can only be used with a connected socket. * Use the async_receive_from function to receive data on an unconnected * raw socket. */ template void async_receive(const MutableBufferSequence& buffers, socket_base::message_flags flags, ReadHandler handler) { this->service.async_receive(this->implementation, buffers, flags, handler); } /// Receive raw data with the endpoint of the sender. /** * This function is used to receive raw data. The function call will block * until data has been received successfully or an error occurs. * * @param buffers One or more buffers into which the data will be received. * * @param sender_endpoint An endpoint object that receives the endpoint of * the remote sender of the data. * * @returns The number of bytes received. * * @throws asio::system_error Thrown on failure. * * @par Example * To receive into a single data buffer use the @ref buffer function as * follows: * @code * asio::ip::udp::endpoint sender_endpoint; * socket.receive_from( * asio::buffer(data, size), sender_endpoint); * @endcode * See the @ref buffer documentation for information on receiving into * multiple buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template std::size_t receive_from(const MutableBufferSequence& buffers, endpoint_type& sender_endpoint) { asio::error_code ec; std::size_t s = this->service.receive_from( this->implementation, buffers, sender_endpoint, 0, ec); asio::detail::throw_error(ec); return s; } /// Receive raw data with the endpoint of the sender. /** * This function is used to receive raw data. The function call will block * until data has been received successfully or an error occurs. * * @param buffers One or more buffers into which the data will be received. * * @param sender_endpoint An endpoint object that receives the endpoint of * the remote sender of the data. * * @param flags Flags specifying how the receive call is to be made. * * @returns The number of bytes received. * * @throws asio::system_error Thrown on failure. */ template std::size_t receive_from(const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, socket_base::message_flags flags) { asio::error_code ec; std::size_t s = this->service.receive_from( this->implementation, buffers, sender_endpoint, flags, ec); asio::detail::throw_error(ec); return s; } /// Receive raw data with the endpoint of the sender. /** * This function is used to receive raw data. The function call will block * until data has been received successfully or an error occurs. * * @param buffers One or more buffers into which the data will be received. * * @param sender_endpoint An endpoint object that receives the endpoint of * the remote sender of the data. * * @param flags Flags specifying how the receive call is to be made. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes received. */ template std::size_t receive_from(const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, socket_base::message_flags flags, asio::error_code& ec) { return this->service.receive_from(this->implementation, buffers, sender_endpoint, flags, ec); } /// Start an asynchronous receive. /** * This function is used to asynchronously receive raw data. The function * call always returns immediately. * * @param buffers One or more buffers into which the data will be received. * Although the buffers object may be copied as necessary, ownership of the * underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param sender_endpoint An endpoint object that receives the endpoint of * the remote sender of the data. Ownership of the sender_endpoint object * is retained by the caller, which must guarantee that it is valid until the * handler is called. * * @param handler The handler to be called when the receive operation * completes. Copies will be made of the handler as required. The function * signature of the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes received. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @par Example * To receive into a single data buffer use the @ref buffer function as * follows: * @code socket.async_receive_from( * asio::buffer(data, size), 0, sender_endpoint, handler); @endcode * See the @ref buffer documentation for information on receiving into * multiple buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template void async_receive_from(const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, ReadHandler handler) { this->service.async_receive_from(this->implementation, buffers, sender_endpoint, 0, handler); } /// Start an asynchronous receive. /** * This function is used to asynchronously receive raw data. The function * call always returns immediately. * * @param buffers One or more buffers into which the data will be received. * Although the buffers object may be copied as necessary, ownership of the * underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param sender_endpoint An endpoint object that receives the endpoint of * the remote sender of the data. Ownership of the sender_endpoint object * is retained by the caller, which must guarantee that it is valid until the * handler is called. * * @param flags Flags specifying how the receive call is to be made. * * @param handler The handler to be called when the receive operation * completes. Copies will be made of the handler as required. The function * signature of the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes received. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). */ template void async_receive_from(const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, socket_base::message_flags flags, ReadHandler handler) { this->service.async_receive_from(this->implementation, buffers, sender_endpoint, flags, handler); } }; } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_BASIC_RAW_SOCKET_HPP percona-xtradb-cluster-galera/asio/asio/basic_serial_port.hpp0000644000000000000000000005166212247075736024747 0ustar rootroot00000000000000// // basic_serial_port.hpp // ~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_BASIC_SERIAL_PORT_HPP #define ASIO_BASIC_SERIAL_PORT_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_SERIAL_PORT) \ || defined(GENERATING_DOCUMENTATION) #include #include "asio/basic_io_object.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/serial_port_base.hpp" #include "asio/serial_port_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { /// Provides serial port functionality. /** * The basic_serial_port class template provides functionality that is common * to all serial ports. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. */ template class basic_serial_port : public basic_io_object, public serial_port_base { public: /// The native representation of a serial port. typedef typename SerialPortService::native_type native_type; /// A basic_serial_port is always the lowest layer. typedef basic_serial_port lowest_layer_type; /// Construct a basic_serial_port without opening it. /** * This constructor creates a serial port without opening it. * * @param io_service The io_service object that the serial port will use to * dispatch handlers for any asynchronous operations performed on the port. */ explicit basic_serial_port(asio::io_service& io_service) : basic_io_object(io_service) { } /// Construct and open a basic_serial_port. /** * This constructor creates and opens a serial port for the specified device * name. * * @param io_service The io_service object that the serial port will use to * dispatch handlers for any asynchronous operations performed on the port. * * @param device The platform-specific device name for this serial * port. */ explicit basic_serial_port(asio::io_service& io_service, const char* device) : basic_io_object(io_service) { asio::error_code ec; this->service.open(this->implementation, device, ec); asio::detail::throw_error(ec); } /// Construct and open a basic_serial_port. /** * This constructor creates and opens a serial port for the specified device * name. * * @param io_service The io_service object that the serial port will use to * dispatch handlers for any asynchronous operations performed on the port. * * @param device The platform-specific device name for this serial * port. */ explicit basic_serial_port(asio::io_service& io_service, const std::string& device) : basic_io_object(io_service) { asio::error_code ec; this->service.open(this->implementation, device, ec); asio::detail::throw_error(ec); } /// Construct a basic_serial_port on an existing native serial port. /** * This constructor creates a serial port object to hold an existing native * serial port. * * @param io_service The io_service object that the serial port will use to * dispatch handlers for any asynchronous operations performed on the port. * * @param native_serial_port A native serial port. * * @throws asio::system_error Thrown on failure. */ basic_serial_port(asio::io_service& io_service, const native_type& native_serial_port) : basic_io_object(io_service) { asio::error_code ec; this->service.assign(this->implementation, native_serial_port, ec); asio::detail::throw_error(ec); } /// Get a reference to the lowest layer. /** * This function returns a reference to the lowest layer in a stack of * layers. Since a basic_serial_port cannot contain any further layers, it * simply returns a reference to itself. * * @return A reference to the lowest layer in the stack of layers. Ownership * is not transferred to the caller. */ lowest_layer_type& lowest_layer() { return *this; } /// Get a const reference to the lowest layer. /** * This function returns a const reference to the lowest layer in a stack of * layers. Since a basic_serial_port cannot contain any further layers, it * simply returns a reference to itself. * * @return A const reference to the lowest layer in the stack of layers. * Ownership is not transferred to the caller. */ const lowest_layer_type& lowest_layer() const { return *this; } /// Open the serial port using the specified device name. /** * This function opens the serial port for the specified device name. * * @param device The platform-specific device name. * * @throws asio::system_error Thrown on failure. */ void open(const std::string& device) { asio::error_code ec; this->service.open(this->implementation, device, ec); asio::detail::throw_error(ec); } /// Open the serial port using the specified device name. /** * This function opens the serial port using the given platform-specific * device name. * * @param device The platform-specific device name. * * @param ec Set the indicate what error occurred, if any. */ asio::error_code open(const std::string& device, asio::error_code& ec) { return this->service.open(this->implementation, device, ec); } /// Assign an existing native serial port to the serial port. /* * This function opens the serial port to hold an existing native serial port. * * @param native_serial_port A native serial port. * * @throws asio::system_error Thrown on failure. */ void assign(const native_type& native_serial_port) { asio::error_code ec; this->service.assign(this->implementation, native_serial_port, ec); asio::detail::throw_error(ec); } /// Assign an existing native serial port to the serial port. /* * This function opens the serial port to hold an existing native serial port. * * @param native_serial_port A native serial port. * * @param ec Set to indicate what error occurred, if any. */ asio::error_code assign(const native_type& native_serial_port, asio::error_code& ec) { return this->service.assign(this->implementation, native_serial_port, ec); } /// Determine whether the serial port is open. bool is_open() const { return this->service.is_open(this->implementation); } /// Close the serial port. /** * This function is used to close the serial port. Any asynchronous read or * write operations will be cancelled immediately, and will complete with the * asio::error::operation_aborted error. * * @throws asio::system_error Thrown on failure. */ void close() { asio::error_code ec; this->service.close(this->implementation, ec); asio::detail::throw_error(ec); } /// Close the serial port. /** * This function is used to close the serial port. Any asynchronous read or * write operations will be cancelled immediately, and will complete with the * asio::error::operation_aborted error. * * @param ec Set to indicate what error occurred, if any. */ asio::error_code close(asio::error_code& ec) { return this->service.close(this->implementation, ec); } /// Get the native serial port representation. /** * This function may be used to obtain the underlying representation of the * serial port. This is intended to allow access to native serial port * functionality that is not otherwise provided. */ native_type native() { return this->service.native(this->implementation); } /// Cancel all asynchronous operations associated with the serial port. /** * This function causes all outstanding asynchronous read or write operations * to finish immediately, and the handlers for cancelled operations will be * passed the asio::error::operation_aborted error. * * @throws asio::system_error Thrown on failure. */ void cancel() { asio::error_code ec; this->service.cancel(this->implementation, ec); asio::detail::throw_error(ec); } /// Cancel all asynchronous operations associated with the serial port. /** * This function causes all outstanding asynchronous read or write operations * to finish immediately, and the handlers for cancelled operations will be * passed the asio::error::operation_aborted error. * * @param ec Set to indicate what error occurred, if any. */ asio::error_code cancel(asio::error_code& ec) { return this->service.cancel(this->implementation, ec); } /// Send a break sequence to the serial port. /** * This function causes a break sequence of platform-specific duration to be * sent out the serial port. * * @throws asio::system_error Thrown on failure. */ void send_break() { asio::error_code ec; this->service.send_break(this->implementation, ec); asio::detail::throw_error(ec); } /// Send a break sequence to the serial port. /** * This function causes a break sequence of platform-specific duration to be * sent out the serial port. * * @param ec Set to indicate what error occurred, if any. */ asio::error_code send_break(asio::error_code& ec) { return this->service.send_break(this->implementation, ec); } /// Set an option on the serial port. /** * This function is used to set an option on the serial port. * * @param option The option value to be set on the serial port. * * @throws asio::system_error Thrown on failure. * * @sa SettableSerialPortOption @n * asio::serial_port_base::baud_rate @n * asio::serial_port_base::flow_control @n * asio::serial_port_base::parity @n * asio::serial_port_base::stop_bits @n * asio::serial_port_base::character_size */ template void set_option(const SettableSerialPortOption& option) { asio::error_code ec; this->service.set_option(this->implementation, option, ec); asio::detail::throw_error(ec); } /// Set an option on the serial port. /** * This function is used to set an option on the serial port. * * @param option The option value to be set on the serial port. * * @param ec Set to indicate what error occurred, if any. * * @sa SettableSerialPortOption @n * asio::serial_port_base::baud_rate @n * asio::serial_port_base::flow_control @n * asio::serial_port_base::parity @n * asio::serial_port_base::stop_bits @n * asio::serial_port_base::character_size */ template asio::error_code set_option(const SettableSerialPortOption& option, asio::error_code& ec) { return this->service.set_option(this->implementation, option, ec); } /// Get an option from the serial port. /** * This function is used to get the current value of an option on the serial * port. * * @param option The option value to be obtained from the serial port. * * @throws asio::system_error Thrown on failure. * * @sa GettableSerialPortOption @n * asio::serial_port_base::baud_rate @n * asio::serial_port_base::flow_control @n * asio::serial_port_base::parity @n * asio::serial_port_base::stop_bits @n * asio::serial_port_base::character_size */ template void get_option(GettableSerialPortOption& option) { asio::error_code ec; this->service.get_option(this->implementation, option, ec); asio::detail::throw_error(ec); } /// Get an option from the serial port. /** * This function is used to get the current value of an option on the serial * port. * * @param option The option value to be obtained from the serial port. * * @param ec Set to indicate what error occured, if any. * * @sa GettableSerialPortOption @n * asio::serial_port_base::baud_rate @n * asio::serial_port_base::flow_control @n * asio::serial_port_base::parity @n * asio::serial_port_base::stop_bits @n * asio::serial_port_base::character_size */ template asio::error_code get_option(GettableSerialPortOption& option, asio::error_code& ec) { return this->service.get_option(this->implementation, option, ec); } /// Write some data to the serial port. /** * This function is used to write data to the serial port. The function call * will block until one or more bytes of the data has been written * successfully, or until an error occurs. * * @param buffers One or more data buffers to be written to the serial port. * * @returns The number of bytes written. * * @throws asio::system_error Thrown on failure. An error code of * asio::error::eof indicates that the connection was closed by the * peer. * * @note The write_some operation may not transmit all of the data to the * peer. Consider using the @ref write function if you need to ensure that * all data is written before the blocking operation completes. * * @par Example * To write a single data buffer use the @ref buffer function as follows: * @code * serial_port.write_some(asio::buffer(data, size)); * @endcode * See the @ref buffer documentation for information on writing multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template std::size_t write_some(const ConstBufferSequence& buffers) { asio::error_code ec; std::size_t s = this->service.write_some(this->implementation, buffers, ec); asio::detail::throw_error(ec); return s; } /// Write some data to the serial port. /** * This function is used to write data to the serial port. The function call * will block until one or more bytes of the data has been written * successfully, or until an error occurs. * * @param buffers One or more data buffers to be written to the serial port. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes written. Returns 0 if an error occurred. * * @note The write_some operation may not transmit all of the data to the * peer. Consider using the @ref write function if you need to ensure that * all data is written before the blocking operation completes. */ template std::size_t write_some(const ConstBufferSequence& buffers, asio::error_code& ec) { return this->service.write_some(this->implementation, buffers, ec); } /// Start an asynchronous write. /** * This function is used to asynchronously write data to the serial port. * The function call always returns immediately. * * @param buffers One or more data buffers to be written to the serial port. * Although the buffers object may be copied as necessary, ownership of the * underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param handler The handler to be called when the write operation completes. * Copies will be made of the handler as required. The function signature of * the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes written. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note The write operation may not transmit all of the data to the peer. * Consider using the @ref async_write function if you need to ensure that all * data is written before the asynchronous operation completes. * * @par Example * To write a single data buffer use the @ref buffer function as follows: * @code * serial_port.async_write_some(asio::buffer(data, size), handler); * @endcode * See the @ref buffer documentation for information on writing multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template void async_write_some(const ConstBufferSequence& buffers, WriteHandler handler) { this->service.async_write_some(this->implementation, buffers, handler); } /// Read some data from the serial port. /** * This function is used to read data from the serial port. The function * call will block until one or more bytes of data has been read successfully, * or until an error occurs. * * @param buffers One or more buffers into which the data will be read. * * @returns The number of bytes read. * * @throws asio::system_error Thrown on failure. An error code of * asio::error::eof indicates that the connection was closed by the * peer. * * @note The read_some operation may not read all of the requested number of * bytes. Consider using the @ref read function if you need to ensure that * the requested amount of data is read before the blocking operation * completes. * * @par Example * To read into a single data buffer use the @ref buffer function as follows: * @code * serial_port.read_some(asio::buffer(data, size)); * @endcode * See the @ref buffer documentation for information on reading into multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template std::size_t read_some(const MutableBufferSequence& buffers) { asio::error_code ec; std::size_t s = this->service.read_some(this->implementation, buffers, ec); asio::detail::throw_error(ec); return s; } /// Read some data from the serial port. /** * This function is used to read data from the serial port. The function * call will block until one or more bytes of data has been read successfully, * or until an error occurs. * * @param buffers One or more buffers into which the data will be read. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes read. Returns 0 if an error occurred. * * @note The read_some operation may not read all of the requested number of * bytes. Consider using the @ref read function if you need to ensure that * the requested amount of data is read before the blocking operation * completes. */ template std::size_t read_some(const MutableBufferSequence& buffers, asio::error_code& ec) { return this->service.read_some(this->implementation, buffers, ec); } /// Start an asynchronous read. /** * This function is used to asynchronously read data from the serial port. * The function call always returns immediately. * * @param buffers One or more buffers into which the data will be read. * Although the buffers object may be copied as necessary, ownership of the * underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param handler The handler to be called when the read operation completes. * Copies will be made of the handler as required. The function signature of * the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes read. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note The read operation may not read all of the requested number of bytes. * Consider using the @ref async_read function if you need to ensure that the * requested amount of data is read before the asynchronous operation * completes. * * @par Example * To read into a single data buffer use the @ref buffer function as follows: * @code * serial_port.async_read_some(asio::buffer(data, size), handler); * @endcode * See the @ref buffer documentation for information on reading into multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template void async_read_some(const MutableBufferSequence& buffers, ReadHandler handler) { this->service.async_read_some(this->implementation, buffers, handler); } }; } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_SERIAL_PORT) // || defined(GENERATING_DOCUMENTATION) #endif // ASIO_BASIC_SERIAL_PORT_HPP percona-xtradb-cluster-galera/asio/asio/basic_socket.hpp0000644000000000000000000010054312247075736023705 0ustar rootroot00000000000000// // basic_socket.hpp // ~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_BASIC_SOCKET_HPP #define ASIO_BASIC_SOCKET_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/basic_io_object.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/socket_base.hpp" #include "asio/detail/push_options.hpp" namespace asio { /// Provides socket functionality. /** * The basic_socket class template provides functionality that is common to both * stream-oriented and datagram-oriented sockets. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. */ template class basic_socket : public basic_io_object, public socket_base { public: /// The native representation of a socket. typedef typename SocketService::native_type native_type; /// The protocol type. typedef Protocol protocol_type; /// The endpoint type. typedef typename Protocol::endpoint endpoint_type; /// A basic_socket is always the lowest layer. typedef basic_socket lowest_layer_type; /// Construct a basic_socket without opening it. /** * This constructor creates a socket without opening it. * * @param io_service The io_service object that the socket will use to * dispatch handlers for any asynchronous operations performed on the socket. */ explicit basic_socket(asio::io_service& io_service) : basic_io_object(io_service) { } /// Construct and open a basic_socket. /** * This constructor creates and opens a socket. * * @param io_service The io_service object that the socket will use to * dispatch handlers for any asynchronous operations performed on the socket. * * @param protocol An object specifying protocol parameters to be used. * * @throws asio::system_error Thrown on failure. */ basic_socket(asio::io_service& io_service, const protocol_type& protocol) : basic_io_object(io_service) { asio::error_code ec; this->service.open(this->implementation, protocol, ec); asio::detail::throw_error(ec); } /// Construct a basic_socket, opening it and binding it to the given local /// endpoint. /** * This constructor creates a socket and automatically opens it bound to the * specified endpoint on the local machine. The protocol used is the protocol * associated with the given endpoint. * * @param io_service The io_service object that the socket will use to * dispatch handlers for any asynchronous operations performed on the socket. * * @param endpoint An endpoint on the local machine to which the socket will * be bound. * * @throws asio::system_error Thrown on failure. */ basic_socket(asio::io_service& io_service, const endpoint_type& endpoint) : basic_io_object(io_service) { asio::error_code ec; this->service.open(this->implementation, endpoint.protocol(), ec); asio::detail::throw_error(ec); this->service.bind(this->implementation, endpoint, ec); asio::detail::throw_error(ec); } /// Construct a basic_socket on an existing native socket. /** * This constructor creates a socket object to hold an existing native socket. * * @param io_service The io_service object that the socket will use to * dispatch handlers for any asynchronous operations performed on the socket. * * @param protocol An object specifying protocol parameters to be used. * * @param native_socket A native socket. * * @throws asio::system_error Thrown on failure. */ basic_socket(asio::io_service& io_service, const protocol_type& protocol, const native_type& native_socket) : basic_io_object(io_service) { asio::error_code ec; this->service.assign(this->implementation, protocol, native_socket, ec); asio::detail::throw_error(ec); } /// Get a reference to the lowest layer. /** * This function returns a reference to the lowest layer in a stack of * layers. Since a basic_socket cannot contain any further layers, it simply * returns a reference to itself. * * @return A reference to the lowest layer in the stack of layers. Ownership * is not transferred to the caller. */ lowest_layer_type& lowest_layer() { return *this; } /// Get a const reference to the lowest layer. /** * This function returns a const reference to the lowest layer in a stack of * layers. Since a basic_socket cannot contain any further layers, it simply * returns a reference to itself. * * @return A const reference to the lowest layer in the stack of layers. * Ownership is not transferred to the caller. */ const lowest_layer_type& lowest_layer() const { return *this; } /// Open the socket using the specified protocol. /** * This function opens the socket so that it will use the specified protocol. * * @param protocol An object specifying protocol parameters to be used. * * @throws asio::system_error Thrown on failure. * * @par Example * @code * asio::ip::tcp::socket socket(io_service); * socket.open(asio::ip::tcp::v4()); * @endcode */ void open(const protocol_type& protocol = protocol_type()) { asio::error_code ec; this->service.open(this->implementation, protocol, ec); asio::detail::throw_error(ec); } /// Open the socket using the specified protocol. /** * This function opens the socket so that it will use the specified protocol. * * @param protocol An object specifying which protocol is to be used. * * @param ec Set to indicate what error occurred, if any. * * @par Example * @code * asio::ip::tcp::socket socket(io_service); * asio::error_code ec; * socket.open(asio::ip::tcp::v4(), ec); * if (ec) * { * // An error occurred. * } * @endcode */ asio::error_code open(const protocol_type& protocol, asio::error_code& ec) { return this->service.open(this->implementation, protocol, ec); } /// Assign an existing native socket to the socket. /* * This function opens the socket to hold an existing native socket. * * @param protocol An object specifying which protocol is to be used. * * @param native_socket A native socket. * * @throws asio::system_error Thrown on failure. */ void assign(const protocol_type& protocol, const native_type& native_socket) { asio::error_code ec; this->service.assign(this->implementation, protocol, native_socket, ec); asio::detail::throw_error(ec); } /// Assign an existing native socket to the socket. /* * This function opens the socket to hold an existing native socket. * * @param protocol An object specifying which protocol is to be used. * * @param native_socket A native socket. * * @param ec Set to indicate what error occurred, if any. */ asio::error_code assign(const protocol_type& protocol, const native_type& native_socket, asio::error_code& ec) { return this->service.assign(this->implementation, protocol, native_socket, ec); } /// Determine whether the socket is open. bool is_open() const { return this->service.is_open(this->implementation); } /// Close the socket. /** * This function is used to close the socket. Any asynchronous send, receive * or connect operations will be cancelled immediately, and will complete * with the asio::error::operation_aborted error. * * @throws asio::system_error Thrown on failure. * * @note For portable behaviour with respect to graceful closure of a * connected socket, call shutdown() before closing the socket. */ void close() { asio::error_code ec; this->service.close(this->implementation, ec); asio::detail::throw_error(ec); } /// Close the socket. /** * This function is used to close the socket. Any asynchronous send, receive * or connect operations will be cancelled immediately, and will complete * with the asio::error::operation_aborted error. * * @param ec Set to indicate what error occurred, if any. * * @par Example * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::error_code ec; * socket.close(ec); * if (ec) * { * // An error occurred. * } * @endcode * * @note For portable behaviour with respect to graceful closure of a * connected socket, call shutdown() before closing the socket. */ asio::error_code close(asio::error_code& ec) { return this->service.close(this->implementation, ec); } /// Get the native socket representation. /** * This function may be used to obtain the underlying representation of the * socket. This is intended to allow access to native socket functionality * that is not otherwise provided. */ native_type native() { return this->service.native(this->implementation); } /// Cancel all asynchronous operations associated with the socket. /** * This function causes all outstanding asynchronous connect, send and receive * operations to finish immediately, and the handlers for cancelled operations * will be passed the asio::error::operation_aborted error. * * @throws asio::system_error Thrown on failure. * * @note Calls to cancel() will always fail with * asio::error::operation_not_supported when run on Windows XP, Windows * Server 2003, and earlier versions of Windows, unless * ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has * two issues that should be considered before enabling its use: * * @li It will only cancel asynchronous operations that were initiated in the * current thread. * * @li It can appear to complete without error, but the request to cancel the * unfinished operations may be silently ignored by the operating system. * Whether it works or not seems to depend on the drivers that are installed. * * For portable cancellation, consider using one of the following * alternatives: * * @li Disable asio's I/O completion port backend by defining * ASIO_DISABLE_IOCP. * * @li Use the close() function to simultaneously cancel the outstanding * operations and close the socket. * * When running on Windows Vista, Windows Server 2008, and later, the * CancelIoEx function is always used. This function does not have the * problems described above. */ #if defined(BOOST_MSVC) && (BOOST_MSVC >= 1400) \ && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600) \ && !defined(ASIO_ENABLE_CANCELIO) __declspec(deprecated("By default, this function always fails with " "operation_not_supported when used on Windows XP, Windows Server 2003, " "or earlier. Consult documentation for details.")) #endif void cancel() { asio::error_code ec; this->service.cancel(this->implementation, ec); asio::detail::throw_error(ec); } /// Cancel all asynchronous operations associated with the socket. /** * This function causes all outstanding asynchronous connect, send and receive * operations to finish immediately, and the handlers for cancelled operations * will be passed the asio::error::operation_aborted error. * * @param ec Set to indicate what error occurred, if any. * * @note Calls to cancel() will always fail with * asio::error::operation_not_supported when run on Windows XP, Windows * Server 2003, and earlier versions of Windows, unless * ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has * two issues that should be considered before enabling its use: * * @li It will only cancel asynchronous operations that were initiated in the * current thread. * * @li It can appear to complete without error, but the request to cancel the * unfinished operations may be silently ignored by the operating system. * Whether it works or not seems to depend on the drivers that are installed. * * For portable cancellation, consider using one of the following * alternatives: * * @li Disable asio's I/O completion port backend by defining * ASIO_DISABLE_IOCP. * * @li Use the close() function to simultaneously cancel the outstanding * operations and close the socket. * * When running on Windows Vista, Windows Server 2008, and later, the * CancelIoEx function is always used. This function does not have the * problems described above. */ #if defined(BOOST_MSVC) && (BOOST_MSVC >= 1400) \ && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600) \ && !defined(ASIO_ENABLE_CANCELIO) __declspec(deprecated("By default, this function always fails with " "operation_not_supported when used on Windows XP, Windows Server 2003, " "or earlier. Consult documentation for details.")) #endif asio::error_code cancel(asio::error_code& ec) { return this->service.cancel(this->implementation, ec); } /// Determine whether the socket is at the out-of-band data mark. /** * This function is used to check whether the socket input is currently * positioned at the out-of-band data mark. * * @return A bool indicating whether the socket is at the out-of-band data * mark. * * @throws asio::system_error Thrown on failure. */ bool at_mark() const { asio::error_code ec; bool b = this->service.at_mark(this->implementation, ec); asio::detail::throw_error(ec); return b; } /// Determine whether the socket is at the out-of-band data mark. /** * This function is used to check whether the socket input is currently * positioned at the out-of-band data mark. * * @param ec Set to indicate what error occurred, if any. * * @return A bool indicating whether the socket is at the out-of-band data * mark. */ bool at_mark(asio::error_code& ec) const { return this->service.at_mark(this->implementation, ec); } /// Determine the number of bytes available for reading. /** * This function is used to determine the number of bytes that may be read * without blocking. * * @return The number of bytes that may be read without blocking, or 0 if an * error occurs. * * @throws asio::system_error Thrown on failure. */ std::size_t available() const { asio::error_code ec; std::size_t s = this->service.available(this->implementation, ec); asio::detail::throw_error(ec); return s; } /// Determine the number of bytes available for reading. /** * This function is used to determine the number of bytes that may be read * without blocking. * * @param ec Set to indicate what error occurred, if any. * * @return The number of bytes that may be read without blocking, or 0 if an * error occurs. */ std::size_t available(asio::error_code& ec) const { return this->service.available(this->implementation, ec); } /// Bind the socket to the given local endpoint. /** * This function binds the socket to the specified endpoint on the local * machine. * * @param endpoint An endpoint on the local machine to which the socket will * be bound. * * @throws asio::system_error Thrown on failure. * * @par Example * @code * asio::ip::tcp::socket socket(io_service); * socket.open(asio::ip::tcp::v4()); * socket.bind(asio::ip::tcp::endpoint( * asio::ip::tcp::v4(), 12345)); * @endcode */ void bind(const endpoint_type& endpoint) { asio::error_code ec; this->service.bind(this->implementation, endpoint, ec); asio::detail::throw_error(ec); } /// Bind the socket to the given local endpoint. /** * This function binds the socket to the specified endpoint on the local * machine. * * @param endpoint An endpoint on the local machine to which the socket will * be bound. * * @param ec Set to indicate what error occurred, if any. * * @par Example * @code * asio::ip::tcp::socket socket(io_service); * socket.open(asio::ip::tcp::v4()); * asio::error_code ec; * socket.bind(asio::ip::tcp::endpoint( * asio::ip::tcp::v4(), 12345), ec); * if (ec) * { * // An error occurred. * } * @endcode */ asio::error_code bind(const endpoint_type& endpoint, asio::error_code& ec) { return this->service.bind(this->implementation, endpoint, ec); } /// Connect the socket to the specified endpoint. /** * This function is used to connect a socket to the specified remote endpoint. * The function call will block until the connection is successfully made or * an error occurs. * * The socket is automatically opened if it is not already open. If the * connect fails, and the socket was automatically opened, the socket is * not returned to the closed state. * * @param peer_endpoint The remote endpoint to which the socket will be * connected. * * @throws asio::system_error Thrown on failure. * * @par Example * @code * asio::ip::tcp::socket socket(io_service); * asio::ip::tcp::endpoint endpoint( * asio::ip::address::from_string("1.2.3.4"), 12345); * socket.connect(endpoint); * @endcode */ void connect(const endpoint_type& peer_endpoint) { asio::error_code ec; if (!is_open()) { this->service.open(this->implementation, peer_endpoint.protocol(), ec); asio::detail::throw_error(ec); } this->service.connect(this->implementation, peer_endpoint, ec); asio::detail::throw_error(ec); } /// Connect the socket to the specified endpoint. /** * This function is used to connect a socket to the specified remote endpoint. * The function call will block until the connection is successfully made or * an error occurs. * * The socket is automatically opened if it is not already open. If the * connect fails, and the socket was automatically opened, the socket is * not returned to the closed state. * * @param peer_endpoint The remote endpoint to which the socket will be * connected. * * @param ec Set to indicate what error occurred, if any. * * @par Example * @code * asio::ip::tcp::socket socket(io_service); * asio::ip::tcp::endpoint endpoint( * asio::ip::address::from_string("1.2.3.4"), 12345); * asio::error_code ec; * socket.connect(endpoint, ec); * if (ec) * { * // An error occurred. * } * @endcode */ asio::error_code connect(const endpoint_type& peer_endpoint, asio::error_code& ec) { if (!is_open()) { if (this->service.open(this->implementation, peer_endpoint.protocol(), ec)) { return ec; } } return this->service.connect(this->implementation, peer_endpoint, ec); } /// Start an asynchronous connect. /** * This function is used to asynchronously connect a socket to the specified * remote endpoint. The function call always returns immediately. * * The socket is automatically opened if it is not already open. If the * connect fails, and the socket was automatically opened, the socket is * not returned to the closed state. * * @param peer_endpoint The remote endpoint to which the socket will be * connected. Copies will be made of the endpoint object as required. * * @param handler The handler to be called when the connection operation * completes. Copies will be made of the handler as required. The function * signature of the handler must be: * @code void handler( * const asio::error_code& error // Result of operation * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @par Example * @code * void connect_handler(const asio::error_code& error) * { * if (!error) * { * // Connect succeeded. * } * } * * ... * * asio::ip::tcp::socket socket(io_service); * asio::ip::tcp::endpoint endpoint( * asio::ip::address::from_string("1.2.3.4"), 12345); * socket.async_connect(endpoint, connect_handler); * @endcode */ template void async_connect(const endpoint_type& peer_endpoint, ConnectHandler handler) { if (!is_open()) { asio::error_code ec; if (this->service.open(this->implementation, peer_endpoint.protocol(), ec)) { this->get_io_service().post( asio::detail::bind_handler(handler, ec)); return; } } this->service.async_connect(this->implementation, peer_endpoint, handler); } /// Set an option on the socket. /** * This function is used to set an option on the socket. * * @param option The new option value to be set on the socket. * * @throws asio::system_error Thrown on failure. * * @sa SettableSocketOption @n * asio::socket_base::broadcast @n * asio::socket_base::do_not_route @n * asio::socket_base::keep_alive @n * asio::socket_base::linger @n * asio::socket_base::receive_buffer_size @n * asio::socket_base::receive_low_watermark @n * asio::socket_base::reuse_address @n * asio::socket_base::send_buffer_size @n * asio::socket_base::send_low_watermark @n * asio::ip::multicast::join_group @n * asio::ip::multicast::leave_group @n * asio::ip::multicast::enable_loopback @n * asio::ip::multicast::outbound_interface @n * asio::ip::multicast::hops @n * asio::ip::tcp::no_delay * * @par Example * Setting the IPPROTO_TCP/TCP_NODELAY option: * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::ip::tcp::no_delay option(true); * socket.set_option(option); * @endcode */ template void set_option(const SettableSocketOption& option) { asio::error_code ec; this->service.set_option(this->implementation, option, ec); asio::detail::throw_error(ec); } /// Set an option on the socket. /** * This function is used to set an option on the socket. * * @param option The new option value to be set on the socket. * * @param ec Set to indicate what error occurred, if any. * * @sa SettableSocketOption @n * asio::socket_base::broadcast @n * asio::socket_base::do_not_route @n * asio::socket_base::keep_alive @n * asio::socket_base::linger @n * asio::socket_base::receive_buffer_size @n * asio::socket_base::receive_low_watermark @n * asio::socket_base::reuse_address @n * asio::socket_base::send_buffer_size @n * asio::socket_base::send_low_watermark @n * asio::ip::multicast::join_group @n * asio::ip::multicast::leave_group @n * asio::ip::multicast::enable_loopback @n * asio::ip::multicast::outbound_interface @n * asio::ip::multicast::hops @n * asio::ip::tcp::no_delay * * @par Example * Setting the IPPROTO_TCP/TCP_NODELAY option: * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::ip::tcp::no_delay option(true); * asio::error_code ec; * socket.set_option(option, ec); * if (ec) * { * // An error occurred. * } * @endcode */ template asio::error_code set_option(const SettableSocketOption& option, asio::error_code& ec) { return this->service.set_option(this->implementation, option, ec); } /// Get an option from the socket. /** * This function is used to get the current value of an option on the socket. * * @param option The option value to be obtained from the socket. * * @throws asio::system_error Thrown on failure. * * @sa GettableSocketOption @n * asio::socket_base::broadcast @n * asio::socket_base::do_not_route @n * asio::socket_base::keep_alive @n * asio::socket_base::linger @n * asio::socket_base::receive_buffer_size @n * asio::socket_base::receive_low_watermark @n * asio::socket_base::reuse_address @n * asio::socket_base::send_buffer_size @n * asio::socket_base::send_low_watermark @n * asio::ip::multicast::join_group @n * asio::ip::multicast::leave_group @n * asio::ip::multicast::enable_loopback @n * asio::ip::multicast::outbound_interface @n * asio::ip::multicast::hops @n * asio::ip::tcp::no_delay * * @par Example * Getting the value of the SOL_SOCKET/SO_KEEPALIVE option: * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::ip::tcp::socket::keep_alive option; * socket.get_option(option); * bool is_set = option.get(); * @endcode */ template void get_option(GettableSocketOption& option) const { asio::error_code ec; this->service.get_option(this->implementation, option, ec); asio::detail::throw_error(ec); } /// Get an option from the socket. /** * This function is used to get the current value of an option on the socket. * * @param option The option value to be obtained from the socket. * * @param ec Set to indicate what error occurred, if any. * * @sa GettableSocketOption @n * asio::socket_base::broadcast @n * asio::socket_base::do_not_route @n * asio::socket_base::keep_alive @n * asio::socket_base::linger @n * asio::socket_base::receive_buffer_size @n * asio::socket_base::receive_low_watermark @n * asio::socket_base::reuse_address @n * asio::socket_base::send_buffer_size @n * asio::socket_base::send_low_watermark @n * asio::ip::multicast::join_group @n * asio::ip::multicast::leave_group @n * asio::ip::multicast::enable_loopback @n * asio::ip::multicast::outbound_interface @n * asio::ip::multicast::hops @n * asio::ip::tcp::no_delay * * @par Example * Getting the value of the SOL_SOCKET/SO_KEEPALIVE option: * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::ip::tcp::socket::keep_alive option; * asio::error_code ec; * socket.get_option(option, ec); * if (ec) * { * // An error occurred. * } * bool is_set = option.get(); * @endcode */ template asio::error_code get_option(GettableSocketOption& option, asio::error_code& ec) const { return this->service.get_option(this->implementation, option, ec); } /// Perform an IO control command on the socket. /** * This function is used to execute an IO control command on the socket. * * @param command The IO control command to be performed on the socket. * * @throws asio::system_error Thrown on failure. * * @sa IoControlCommand @n * asio::socket_base::bytes_readable @n * asio::socket_base::non_blocking_io * * @par Example * Getting the number of bytes ready to read: * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::ip::tcp::socket::bytes_readable command; * socket.io_control(command); * std::size_t bytes_readable = command.get(); * @endcode */ template void io_control(IoControlCommand& command) { asio::error_code ec; this->service.io_control(this->implementation, command, ec); asio::detail::throw_error(ec); } /// Perform an IO control command on the socket. /** * This function is used to execute an IO control command on the socket. * * @param command The IO control command to be performed on the socket. * * @param ec Set to indicate what error occurred, if any. * * @sa IoControlCommand @n * asio::socket_base::bytes_readable @n * asio::socket_base::non_blocking_io * * @par Example * Getting the number of bytes ready to read: * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::ip::tcp::socket::bytes_readable command; * asio::error_code ec; * socket.io_control(command, ec); * if (ec) * { * // An error occurred. * } * std::size_t bytes_readable = command.get(); * @endcode */ template asio::error_code io_control(IoControlCommand& command, asio::error_code& ec) { return this->service.io_control(this->implementation, command, ec); } /// Get the local endpoint of the socket. /** * This function is used to obtain the locally bound endpoint of the socket. * * @returns An object that represents the local endpoint of the socket. * * @throws asio::system_error Thrown on failure. * * @par Example * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::ip::tcp::endpoint endpoint = socket.local_endpoint(); * @endcode */ endpoint_type local_endpoint() const { asio::error_code ec; endpoint_type ep = this->service.local_endpoint(this->implementation, ec); asio::detail::throw_error(ec); return ep; } /// Get the local endpoint of the socket. /** * This function is used to obtain the locally bound endpoint of the socket. * * @param ec Set to indicate what error occurred, if any. * * @returns An object that represents the local endpoint of the socket. * Returns a default-constructed endpoint object if an error occurred. * * @par Example * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::error_code ec; * asio::ip::tcp::endpoint endpoint = socket.local_endpoint(ec); * if (ec) * { * // An error occurred. * } * @endcode */ endpoint_type local_endpoint(asio::error_code& ec) const { return this->service.local_endpoint(this->implementation, ec); } /// Get the remote endpoint of the socket. /** * This function is used to obtain the remote endpoint of the socket. * * @returns An object that represents the remote endpoint of the socket. * * @throws asio::system_error Thrown on failure. * * @par Example * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::ip::tcp::endpoint endpoint = socket.remote_endpoint(); * @endcode */ endpoint_type remote_endpoint() const { asio::error_code ec; endpoint_type ep = this->service.remote_endpoint(this->implementation, ec); asio::detail::throw_error(ec); return ep; } /// Get the remote endpoint of the socket. /** * This function is used to obtain the remote endpoint of the socket. * * @param ec Set to indicate what error occurred, if any. * * @returns An object that represents the remote endpoint of the socket. * Returns a default-constructed endpoint object if an error occurred. * * @par Example * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::error_code ec; * asio::ip::tcp::endpoint endpoint = socket.remote_endpoint(ec); * if (ec) * { * // An error occurred. * } * @endcode */ endpoint_type remote_endpoint(asio::error_code& ec) const { return this->service.remote_endpoint(this->implementation, ec); } /// Disable sends or receives on the socket. /** * This function is used to disable send operations, receive operations, or * both. * * @param what Determines what types of operation will no longer be allowed. * * @throws asio::system_error Thrown on failure. * * @par Example * Shutting down the send side of the socket: * @code * asio::ip::tcp::socket socket(io_service); * ... * socket.shutdown(asio::ip::tcp::socket::shutdown_send); * @endcode */ void shutdown(shutdown_type what) { asio::error_code ec; this->service.shutdown(this->implementation, what, ec); asio::detail::throw_error(ec); } /// Disable sends or receives on the socket. /** * This function is used to disable send operations, receive operations, or * both. * * @param what Determines what types of operation will no longer be allowed. * * @param ec Set to indicate what error occurred, if any. * * @par Example * Shutting down the send side of the socket: * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::error_code ec; * socket.shutdown(asio::ip::tcp::socket::shutdown_send, ec); * if (ec) * { * // An error occurred. * } * @endcode */ asio::error_code shutdown(shutdown_type what, asio::error_code& ec) { return this->service.shutdown(this->implementation, what, ec); } protected: /// Protected destructor to prevent deletion through this type. ~basic_socket() { } }; } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_BASIC_SOCKET_HPP percona-xtradb-cluster-galera/asio/asio/basic_socket_acceptor.hpp0000644000000000000000000006215612247075736025574 0ustar rootroot00000000000000// // basic_socket_acceptor.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_BASIC_SOCKET_ACCEPTOR_HPP #define ASIO_BASIC_SOCKET_ACCEPTOR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/basic_io_object.hpp" #include "asio/basic_socket.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/socket_acceptor_service.hpp" #include "asio/socket_base.hpp" #include "asio/detail/push_options.hpp" namespace asio { /// Provides the ability to accept new connections. /** * The basic_socket_acceptor class template is used for accepting new socket * connections. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. * * @par Example * Opening a socket acceptor with the SO_REUSEADDR option enabled: * @code * asio::ip::tcp::acceptor acceptor(io_service); * asio::ip::tcp::endpoint endpoint(asio::ip::tcp::v4(), port); * acceptor.open(endpoint.protocol()); * acceptor.set_option(asio::ip::tcp::acceptor::reuse_address(true)); * acceptor.bind(endpoint); * acceptor.listen(); * @endcode */ template > class basic_socket_acceptor : public basic_io_object, public socket_base { public: /// The native representation of an acceptor. typedef typename SocketAcceptorService::native_type native_type; /// The protocol type. typedef Protocol protocol_type; /// The endpoint type. typedef typename Protocol::endpoint endpoint_type; /// Construct an acceptor without opening it. /** * This constructor creates an acceptor without opening it to listen for new * connections. The open() function must be called before the acceptor can * accept new socket connections. * * @param io_service The io_service object that the acceptor will use to * dispatch handlers for any asynchronous operations performed on the * acceptor. */ explicit basic_socket_acceptor(asio::io_service& io_service) : basic_io_object(io_service) { } /// Construct an open acceptor. /** * This constructor creates an acceptor and automatically opens it. * * @param io_service The io_service object that the acceptor will use to * dispatch handlers for any asynchronous operations performed on the * acceptor. * * @param protocol An object specifying protocol parameters to be used. * * @throws asio::system_error Thrown on failure. */ basic_socket_acceptor(asio::io_service& io_service, const protocol_type& protocol) : basic_io_object(io_service) { asio::error_code ec; this->service.open(this->implementation, protocol, ec); asio::detail::throw_error(ec); } /// Construct an acceptor opened on the given endpoint. /** * This constructor creates an acceptor and automatically opens it to listen * for new connections on the specified endpoint. * * @param io_service The io_service object that the acceptor will use to * dispatch handlers for any asynchronous operations performed on the * acceptor. * * @param endpoint An endpoint on the local machine on which the acceptor * will listen for new connections. * * @param reuse_addr Whether the constructor should set the socket option * socket_base::reuse_address. * * @throws asio::system_error Thrown on failure. * * @note This constructor is equivalent to the following code: * @code * basic_socket_acceptor acceptor(io_service); * acceptor.open(endpoint.protocol()); * if (reuse_addr) * acceptor.set_option(socket_base::reuse_address(true)); * acceptor.bind(endpoint); * acceptor.listen(listen_backlog); * @endcode */ basic_socket_acceptor(asio::io_service& io_service, const endpoint_type& endpoint, bool reuse_addr = true) : basic_io_object(io_service) { asio::error_code ec; this->service.open(this->implementation, endpoint.protocol(), ec); asio::detail::throw_error(ec); if (reuse_addr) { this->service.set_option(this->implementation, socket_base::reuse_address(true), ec); asio::detail::throw_error(ec); } this->service.bind(this->implementation, endpoint, ec); asio::detail::throw_error(ec); this->service.listen(this->implementation, socket_base::max_connections, ec); asio::detail::throw_error(ec); } /// Construct a basic_socket_acceptor on an existing native acceptor. /** * This constructor creates an acceptor object to hold an existing native * acceptor. * * @param io_service The io_service object that the acceptor will use to * dispatch handlers for any asynchronous operations performed on the * acceptor. * * @param protocol An object specifying protocol parameters to be used. * * @param native_acceptor A native acceptor. * * @throws asio::system_error Thrown on failure. */ basic_socket_acceptor(asio::io_service& io_service, const protocol_type& protocol, const native_type& native_acceptor) : basic_io_object(io_service) { asio::error_code ec; this->service.assign(this->implementation, protocol, native_acceptor, ec); asio::detail::throw_error(ec); } /// Open the acceptor using the specified protocol. /** * This function opens the socket acceptor so that it will use the specified * protocol. * * @param protocol An object specifying which protocol is to be used. * * @throws asio::system_error Thrown on failure. * * @par Example * @code * asio::ip::tcp::acceptor acceptor(io_service); * acceptor.open(asio::ip::tcp::v4()); * @endcode */ void open(const protocol_type& protocol = protocol_type()) { asio::error_code ec; this->service.open(this->implementation, protocol, ec); asio::detail::throw_error(ec); } /// Open the acceptor using the specified protocol. /** * This function opens the socket acceptor so that it will use the specified * protocol. * * @param protocol An object specifying which protocol is to be used. * * @param ec Set to indicate what error occurred, if any. * * @par Example * @code * asio::ip::tcp::acceptor acceptor(io_service); * asio::error_code ec; * acceptor.open(asio::ip::tcp::v4(), ec); * if (ec) * { * // An error occurred. * } * @endcode */ asio::error_code open(const protocol_type& protocol, asio::error_code& ec) { return this->service.open(this->implementation, protocol, ec); } /// Assigns an existing native acceptor to the acceptor. /* * This function opens the acceptor to hold an existing native acceptor. * * @param protocol An object specifying which protocol is to be used. * * @param native_acceptor A native acceptor. * * @throws asio::system_error Thrown on failure. */ void assign(const protocol_type& protocol, const native_type& native_acceptor) { asio::error_code ec; this->service.assign(this->implementation, protocol, native_acceptor, ec); asio::detail::throw_error(ec); } /// Assigns an existing native acceptor to the acceptor. /* * This function opens the acceptor to hold an existing native acceptor. * * @param protocol An object specifying which protocol is to be used. * * @param native_acceptor A native acceptor. * * @param ec Set to indicate what error occurred, if any. */ asio::error_code assign(const protocol_type& protocol, const native_type& native_acceptor, asio::error_code& ec) { return this->service.assign(this->implementation, protocol, native_acceptor, ec); } /// Determine whether the acceptor is open. bool is_open() const { return this->service.is_open(this->implementation); } /// Bind the acceptor to the given local endpoint. /** * This function binds the socket acceptor to the specified endpoint on the * local machine. * * @param endpoint An endpoint on the local machine to which the socket * acceptor will be bound. * * @throws asio::system_error Thrown on failure. * * @par Example * @code * asio::ip::tcp::acceptor acceptor(io_service); * acceptor.open(asio::ip::tcp::v4()); * acceptor.bind(asio::ip::tcp::endpoint(12345)); * @endcode */ void bind(const endpoint_type& endpoint) { asio::error_code ec; this->service.bind(this->implementation, endpoint, ec); asio::detail::throw_error(ec); } /// Bind the acceptor to the given local endpoint. /** * This function binds the socket acceptor to the specified endpoint on the * local machine. * * @param endpoint An endpoint on the local machine to which the socket * acceptor will be bound. * * @param ec Set to indicate what error occurred, if any. * * @par Example * @code * asio::ip::tcp::acceptor acceptor(io_service); * acceptor.open(asio::ip::tcp::v4()); * asio::error_code ec; * acceptor.bind(asio::ip::tcp::endpoint(12345), ec); * if (ec) * { * // An error occurred. * } * @endcode */ asio::error_code bind(const endpoint_type& endpoint, asio::error_code& ec) { return this->service.bind(this->implementation, endpoint, ec); } /// Place the acceptor into the state where it will listen for new /// connections. /** * This function puts the socket acceptor into the state where it may accept * new connections. * * @param backlog The maximum length of the queue of pending connections. * * @throws asio::system_error Thrown on failure. */ void listen(int backlog = socket_base::max_connections) { asio::error_code ec; this->service.listen(this->implementation, backlog, ec); asio::detail::throw_error(ec); } /// Place the acceptor into the state where it will listen for new /// connections. /** * This function puts the socket acceptor into the state where it may accept * new connections. * * @param backlog The maximum length of the queue of pending connections. * * @param ec Set to indicate what error occurred, if any. * * @par Example * @code * asio::ip::tcp::acceptor acceptor(io_service); * ... * asio::error_code ec; * acceptor.listen(asio::socket_base::max_connections, ec); * if (ec) * { * // An error occurred. * } * @endcode */ asio::error_code listen(int backlog, asio::error_code& ec) { return this->service.listen(this->implementation, backlog, ec); } /// Close the acceptor. /** * This function is used to close the acceptor. Any asynchronous accept * operations will be cancelled immediately. * * A subsequent call to open() is required before the acceptor can again be * used to again perform socket accept operations. * * @throws asio::system_error Thrown on failure. */ void close() { asio::error_code ec; this->service.close(this->implementation, ec); asio::detail::throw_error(ec); } /// Close the acceptor. /** * This function is used to close the acceptor. Any asynchronous accept * operations will be cancelled immediately. * * A subsequent call to open() is required before the acceptor can again be * used to again perform socket accept operations. * * @param ec Set to indicate what error occurred, if any. * * @par Example * @code * asio::ip::tcp::acceptor acceptor(io_service); * ... * asio::error_code ec; * acceptor.close(ec); * if (ec) * { * // An error occurred. * } * @endcode */ asio::error_code close(asio::error_code& ec) { return this->service.close(this->implementation, ec); } /// Get the native acceptor representation. /** * This function may be used to obtain the underlying representation of the * acceptor. This is intended to allow access to native acceptor functionality * that is not otherwise provided. */ native_type native() { return this->service.native(this->implementation); } /// Cancel all asynchronous operations associated with the acceptor. /** * This function causes all outstanding asynchronous connect, send and receive * operations to finish immediately, and the handlers for cancelled operations * will be passed the asio::error::operation_aborted error. * * @throws asio::system_error Thrown on failure. */ void cancel() { asio::error_code ec; this->service.cancel(this->implementation, ec); asio::detail::throw_error(ec); } /// Cancel all asynchronous operations associated with the acceptor. /** * This function causes all outstanding asynchronous connect, send and receive * operations to finish immediately, and the handlers for cancelled operations * will be passed the asio::error::operation_aborted error. * * @param ec Set to indicate what error occurred, if any. */ asio::error_code cancel(asio::error_code& ec) { return this->service.cancel(this->implementation, ec); } /// Set an option on the acceptor. /** * This function is used to set an option on the acceptor. * * @param option The new option value to be set on the acceptor. * * @throws asio::system_error Thrown on failure. * * @sa SettableSocketOption @n * asio::socket_base::reuse_address * asio::socket_base::enable_connection_aborted * * @par Example * Setting the SOL_SOCKET/SO_REUSEADDR option: * @code * asio::ip::tcp::acceptor acceptor(io_service); * ... * asio::ip::tcp::acceptor::reuse_address option(true); * acceptor.set_option(option); * @endcode */ template void set_option(const SettableSocketOption& option) { asio::error_code ec; this->service.set_option(this->implementation, option, ec); asio::detail::throw_error(ec); } /// Set an option on the acceptor. /** * This function is used to set an option on the acceptor. * * @param option The new option value to be set on the acceptor. * * @param ec Set to indicate what error occurred, if any. * * @sa SettableSocketOption @n * asio::socket_base::reuse_address * asio::socket_base::enable_connection_aborted * * @par Example * Setting the SOL_SOCKET/SO_REUSEADDR option: * @code * asio::ip::tcp::acceptor acceptor(io_service); * ... * asio::ip::tcp::acceptor::reuse_address option(true); * asio::error_code ec; * acceptor.set_option(option, ec); * if (ec) * { * // An error occurred. * } * @endcode */ template asio::error_code set_option(const SettableSocketOption& option, asio::error_code& ec) { return this->service.set_option(this->implementation, option, ec); } /// Get an option from the acceptor. /** * This function is used to get the current value of an option on the * acceptor. * * @param option The option value to be obtained from the acceptor. * * @throws asio::system_error Thrown on failure. * * @sa GettableSocketOption @n * asio::socket_base::reuse_address * * @par Example * Getting the value of the SOL_SOCKET/SO_REUSEADDR option: * @code * asio::ip::tcp::acceptor acceptor(io_service); * ... * asio::ip::tcp::acceptor::reuse_address option; * acceptor.get_option(option); * bool is_set = option.get(); * @endcode */ template void get_option(GettableSocketOption& option) { asio::error_code ec; this->service.get_option(this->implementation, option, ec); asio::detail::throw_error(ec); } /// Get an option from the acceptor. /** * This function is used to get the current value of an option on the * acceptor. * * @param option The option value to be obtained from the acceptor. * * @param ec Set to indicate what error occurred, if any. * * @sa GettableSocketOption @n * asio::socket_base::reuse_address * * @par Example * Getting the value of the SOL_SOCKET/SO_REUSEADDR option: * @code * asio::ip::tcp::acceptor acceptor(io_service); * ... * asio::ip::tcp::acceptor::reuse_address option; * asio::error_code ec; * acceptor.get_option(option, ec); * if (ec) * { * // An error occurred. * } * bool is_set = option.get(); * @endcode */ template asio::error_code get_option(GettableSocketOption& option, asio::error_code& ec) { return this->service.get_option(this->implementation, option, ec); } /// Get the local endpoint of the acceptor. /** * This function is used to obtain the locally bound endpoint of the acceptor. * * @returns An object that represents the local endpoint of the acceptor. * * @throws asio::system_error Thrown on failure. * * @par Example * @code * asio::ip::tcp::acceptor acceptor(io_service); * ... * asio::ip::tcp::endpoint endpoint = acceptor.local_endpoint(); * @endcode */ endpoint_type local_endpoint() const { asio::error_code ec; endpoint_type ep = this->service.local_endpoint(this->implementation, ec); asio::detail::throw_error(ec); return ep; } /// Get the local endpoint of the acceptor. /** * This function is used to obtain the locally bound endpoint of the acceptor. * * @param ec Set to indicate what error occurred, if any. * * @returns An object that represents the local endpoint of the acceptor. * Returns a default-constructed endpoint object if an error occurred and the * error handler did not throw an exception. * * @par Example * @code * asio::ip::tcp::acceptor acceptor(io_service); * ... * asio::error_code ec; * asio::ip::tcp::endpoint endpoint = acceptor.local_endpoint(ec); * if (ec) * { * // An error occurred. * } * @endcode */ endpoint_type local_endpoint(asio::error_code& ec) const { return this->service.local_endpoint(this->implementation, ec); } /// Accept a new connection. /** * This function is used to accept a new connection from a peer into the * given socket. The function call will block until a new connection has been * accepted successfully or an error occurs. * * @param peer The socket into which the new connection will be accepted. * * @throws asio::system_error Thrown on failure. * * @par Example * @code * asio::ip::tcp::acceptor acceptor(io_service); * ... * asio::ip::tcp::socket socket(io_service); * acceptor.accept(socket); * @endcode */ template void accept(basic_socket& peer) { asio::error_code ec; this->service.accept(this->implementation, peer, 0, ec); asio::detail::throw_error(ec); } /// Accept a new connection. /** * This function is used to accept a new connection from a peer into the * given socket. The function call will block until a new connection has been * accepted successfully or an error occurs. * * @param peer The socket into which the new connection will be accepted. * * @param ec Set to indicate what error occurred, if any. * * @par Example * @code * asio::ip::tcp::acceptor acceptor(io_service); * ... * asio::ip::tcp::soocket socket(io_service); * asio::error_code ec; * acceptor.accept(socket, ec); * if (ec) * { * // An error occurred. * } * @endcode */ template asio::error_code accept( basic_socket& peer, asio::error_code& ec) { return this->service.accept(this->implementation, peer, 0, ec); } /// Start an asynchronous accept. /** * This function is used to asynchronously accept a new connection into a * socket. The function call always returns immediately. * * @param peer The socket into which the new connection will be accepted. * Ownership of the peer object is retained by the caller, which must * guarantee that it is valid until the handler is called. * * @param handler The handler to be called when the accept operation * completes. Copies will be made of the handler as required. The function * signature of the handler must be: * @code void handler( * const asio::error_code& error // Result of operation. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @par Example * @code * void accept_handler(const asio::error_code& error) * { * if (!error) * { * // Accept succeeded. * } * } * * ... * * asio::ip::tcp::acceptor acceptor(io_service); * ... * asio::ip::tcp::socket socket(io_service); * acceptor.async_accept(socket, accept_handler); * @endcode */ template void async_accept(basic_socket& peer, AcceptHandler handler) { this->service.async_accept(this->implementation, peer, 0, handler); } /// Accept a new connection and obtain the endpoint of the peer /** * This function is used to accept a new connection from a peer into the * given socket, and additionally provide the endpoint of the remote peer. * The function call will block until a new connection has been accepted * successfully or an error occurs. * * @param peer The socket into which the new connection will be accepted. * * @param peer_endpoint An endpoint object which will receive the endpoint of * the remote peer. * * @throws asio::system_error Thrown on failure. * * @par Example * @code * asio::ip::tcp::acceptor acceptor(io_service); * ... * asio::ip::tcp::socket socket(io_service); * asio::ip::tcp::endpoint endpoint; * acceptor.accept(socket, endpoint); * @endcode */ template void accept(basic_socket& peer, endpoint_type& peer_endpoint) { asio::error_code ec; this->service.accept(this->implementation, peer, &peer_endpoint, ec); asio::detail::throw_error(ec); } /// Accept a new connection and obtain the endpoint of the peer /** * This function is used to accept a new connection from a peer into the * given socket, and additionally provide the endpoint of the remote peer. * The function call will block until a new connection has been accepted * successfully or an error occurs. * * @param peer The socket into which the new connection will be accepted. * * @param peer_endpoint An endpoint object which will receive the endpoint of * the remote peer. * * @param ec Set to indicate what error occurred, if any. * * @par Example * @code * asio::ip::tcp::acceptor acceptor(io_service); * ... * asio::ip::tcp::socket socket(io_service); * asio::ip::tcp::endpoint endpoint; * asio::error_code ec; * acceptor.accept(socket, endpoint, ec); * if (ec) * { * // An error occurred. * } * @endcode */ template asio::error_code accept( basic_socket& peer, endpoint_type& peer_endpoint, asio::error_code& ec) { return this->service.accept(this->implementation, peer, &peer_endpoint, ec); } /// Start an asynchronous accept. /** * This function is used to asynchronously accept a new connection into a * socket, and additionally obtain the endpoint of the remote peer. The * function call always returns immediately. * * @param peer The socket into which the new connection will be accepted. * Ownership of the peer object is retained by the caller, which must * guarantee that it is valid until the handler is called. * * @param peer_endpoint An endpoint object into which the endpoint of the * remote peer will be written. Ownership of the peer_endpoint object is * retained by the caller, which must guarantee that it is valid until the * handler is called. * * @param handler The handler to be called when the accept operation * completes. Copies will be made of the handler as required. The function * signature of the handler must be: * @code void handler( * const asio::error_code& error // Result of operation. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). */ template void async_accept(basic_socket& peer, endpoint_type& peer_endpoint, AcceptHandler handler) { this->service.async_accept(this->implementation, peer, &peer_endpoint, handler); } }; } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_BASIC_SOCKET_ACCEPTOR_HPP percona-xtradb-cluster-galera/asio/asio/basic_socket_iostream.hpp0000644000000000000000000001136412247075736025612 0ustar rootroot00000000000000// // basic_socket_iostream.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_BASIC_SOCKET_IOSTREAM_HPP #define ASIO_BASIC_SOCKET_IOSTREAM_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(BOOST_NO_IOSTREAM) #include #include #include #include #include #include "asio/basic_socket_streambuf.hpp" #include "asio/stream_socket_service.hpp" #if !defined(ASIO_SOCKET_IOSTREAM_MAX_ARITY) #define ASIO_SOCKET_IOSTREAM_MAX_ARITY 5 #endif // !defined(ASIO_SOCKET_IOSTREAM_MAX_ARITY) // A macro that should expand to: // template // explicit basic_socket_iostream(T1 x1, ..., Tn xn) // : basic_iostream(&this->boost::base_from_member< // basic_socket_streambuf >::member) // { // if (rdbuf()->connect(x1, ..., xn) == 0) // this->setstate(std::ios_base::failbit); // } // This macro should only persist within this file. #define ASIO_PRIVATE_CTR_DEF(z, n, data) \ template \ explicit basic_socket_iostream(BOOST_PP_ENUM_BINARY_PARAMS(n, T, x)) \ : std::basic_iostream(&this->boost::base_from_member< \ basic_socket_streambuf >::member) \ { \ tie(this); \ if (rdbuf()->connect(BOOST_PP_ENUM_PARAMS(n, x)) == 0) \ this->setstate(std::ios_base::failbit); \ } \ /**/ // A macro that should expand to: // template // void connect(T1 x1, ..., Tn xn) // { // if (rdbuf()->connect(x1, ..., xn) == 0) // this->setstate(std::ios_base::failbit); // } // This macro should only persist within this file. #define ASIO_PRIVATE_CONNECT_DEF(z, n, data) \ template \ void connect(BOOST_PP_ENUM_BINARY_PARAMS(n, T, x)) \ { \ if (rdbuf()->connect(BOOST_PP_ENUM_PARAMS(n, x)) == 0) \ this->setstate(std::ios_base::failbit); \ } \ /**/ #include "asio/detail/push_options.hpp" namespace asio { /// Iostream interface for a socket. template > class basic_socket_iostream : public boost::base_from_member< basic_socket_streambuf >, public std::basic_iostream { public: /// Construct a basic_socket_iostream without establishing a connection. basic_socket_iostream() : std::basic_iostream(&this->boost::base_from_member< basic_socket_streambuf >::member) { tie(this); } #if defined(GENERATING_DOCUMENTATION) /// Establish a connection to an endpoint corresponding to a resolver query. /** * This constructor automatically establishes a connection based on the * supplied resolver query parameters. The arguments are used to construct * a resolver query object. */ template explicit basic_socket_iostream(T1 t1, ..., TN tn); #else BOOST_PP_REPEAT_FROM_TO( 1, BOOST_PP_INC(ASIO_SOCKET_IOSTREAM_MAX_ARITY), ASIO_PRIVATE_CTR_DEF, _ ) #endif #if defined(GENERATING_DOCUMENTATION) /// Establish a connection to an endpoint corresponding to a resolver query. /** * This function automatically establishes a connection based on the supplied * resolver query parameters. The arguments are used to construct a resolver * query object. */ template void connect(T1 t1, ..., TN tn); #else BOOST_PP_REPEAT_FROM_TO( 1, BOOST_PP_INC(ASIO_SOCKET_IOSTREAM_MAX_ARITY), ASIO_PRIVATE_CONNECT_DEF, _ ) #endif /// Close the connection. void close() { if (rdbuf()->close() == 0) this->setstate(std::ios_base::failbit); } /// Return a pointer to the underlying streambuf. basic_socket_streambuf* rdbuf() const { return const_cast*>( &this->boost::base_from_member< basic_socket_streambuf >::member); } }; } // namespace asio #include "asio/detail/pop_options.hpp" #undef ASIO_PRIVATE_CTR_DEF #undef ASIO_PRIVATE_CONNECT_DEF #endif // defined(BOOST_NO_IOSTREAM) #endif // ASIO_BASIC_SOCKET_IOSTREAM_HPP percona-xtradb-cluster-galera/asio/asio/basic_socket_streambuf.hpp0000644000000000000000000002010312247075736025746 0ustar rootroot00000000000000// // basic_socket_streambuf.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_BASIC_SOCKET_STREAMBUF_HPP #define ASIO_BASIC_SOCKET_STREAMBUF_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(BOOST_NO_IOSTREAM) #include #include #include #include #include #include #include #include "asio/basic_socket.hpp" #include "asio/detail/throw_error.hpp" #include "asio/io_service.hpp" #include "asio/stream_socket_service.hpp" #if !defined(ASIO_SOCKET_STREAMBUF_MAX_ARITY) #define ASIO_SOCKET_STREAMBUF_MAX_ARITY 5 #endif // !defined(ASIO_SOCKET_STREAMBUF_MAX_ARITY) // A macro that should expand to: // template // basic_socket_streambuf* connect( // T1 x1, ..., Tn xn) // { // init_buffers(); // asio::error_code ec; // this->basic_socket::close(ec); // typedef typename Protocol::resolver resolver_type; // typedef typename resolver_type::query resolver_query; // resolver_query query(x1, ..., xn); // resolve_and_connect(query, ec); // return !ec ? this : 0; // } // This macro should only persist within this file. #define ASIO_PRIVATE_CONNECT_DEF( z, n, data ) \ template \ basic_socket_streambuf* connect( \ BOOST_PP_ENUM_BINARY_PARAMS(n, T, x)) \ { \ init_buffers(); \ asio::error_code ec; \ this->basic_socket::close(ec); \ typedef typename Protocol::resolver resolver_type; \ typedef typename resolver_type::query resolver_query; \ resolver_query query(BOOST_PP_ENUM_PARAMS(n, x)); \ resolve_and_connect(query, ec); \ return !ec ? this : 0; \ } \ /**/ #include "asio/detail/push_options.hpp" namespace asio { /// Iostream streambuf for a socket. template > class basic_socket_streambuf : public std::streambuf, private boost::base_from_member, public basic_socket { public: /// The endpoint type. typedef typename Protocol::endpoint endpoint_type; /// Construct a basic_socket_streambuf without establishing a connection. basic_socket_streambuf() : basic_socket( boost::base_from_member::member), unbuffered_(false) { init_buffers(); } /// Destructor flushes buffered data. virtual ~basic_socket_streambuf() { if (pptr() != pbase()) overflow(traits_type::eof()); } /// Establish a connection. /** * This function establishes a connection to the specified endpoint. * * @return \c this if a connection was successfully established, a null * pointer otherwise. */ basic_socket_streambuf* connect( const endpoint_type& endpoint) { init_buffers(); asio::error_code ec; this->basic_socket::close(ec); this->basic_socket::connect(endpoint, ec); return !ec ? this : 0; } #if defined(GENERATING_DOCUMENTATION) /// Establish a connection. /** * This function automatically establishes a connection based on the supplied * resolver query parameters. The arguments are used to construct a resolver * query object. * * @return \c this if a connection was successfully established, a null * pointer otherwise. */ template basic_socket_streambuf* connect( T1 t1, ..., TN tn); #else BOOST_PP_REPEAT_FROM_TO( 1, BOOST_PP_INC(ASIO_SOCKET_STREAMBUF_MAX_ARITY), ASIO_PRIVATE_CONNECT_DEF, _ ) #endif /// Close the connection. /** * @return \c this if a connection was successfully established, a null * pointer otherwise. */ basic_socket_streambuf* close() { asio::error_code ec; sync(); this->basic_socket::close(ec); if (!ec) init_buffers(); return !ec ? this : 0; } protected: int_type underflow() { if (gptr() == egptr()) { asio::error_code ec; std::size_t bytes_transferred = this->service.receive( this->implementation, asio::buffer(asio::buffer(get_buffer_) + putback_max), 0, ec); if (ec) return traits_type::eof(); setg(get_buffer_.begin(), get_buffer_.begin() + putback_max, get_buffer_.begin() + putback_max + bytes_transferred); return traits_type::to_int_type(*gptr()); } else { return traits_type::eof(); } } int_type overflow(int_type c) { if (unbuffered_) { if (traits_type::eq_int_type(c, traits_type::eof())) { // Nothing to do. return traits_type::not_eof(c); } else { // Send the single character immediately. asio::error_code ec; char_type ch = traits_type::to_char_type(c); this->service.send(this->implementation, asio::buffer(&ch, sizeof(char_type)), 0, ec); if (ec) return traits_type::eof(); return c; } } else { // Send all data in the output buffer. asio::const_buffer buffer = asio::buffer(pbase(), pptr() - pbase()); while (asio::buffer_size(buffer) > 0) { asio::error_code ec; std::size_t bytes_transferred = this->service.send( this->implementation, asio::buffer(buffer), 0, ec); if (ec) return traits_type::eof(); buffer = buffer + bytes_transferred; } setp(put_buffer_.begin(), put_buffer_.end()); // If the new character is eof then our work here is done. if (traits_type::eq_int_type(c, traits_type::eof())) return traits_type::not_eof(c); // Add the new character to the output buffer. *pptr() = traits_type::to_char_type(c); pbump(1); return c; } } int sync() { return overflow(traits_type::eof()); } std::streambuf* setbuf(char_type* s, std::streamsize n) { if (pptr() == pbase() && s == 0 && n == 0) { unbuffered_ = true; setp(0, 0); return this; } return 0; } private: void init_buffers() { setg(get_buffer_.begin(), get_buffer_.begin() + putback_max, get_buffer_.begin() + putback_max); if (unbuffered_) setp(0, 0); else setp(put_buffer_.begin(), put_buffer_.end()); } template void resolve_and_connect(const ResolverQuery& query, asio::error_code& ec) { typedef typename Protocol::resolver resolver_type; typedef typename resolver_type::iterator iterator_type; resolver_type resolver( boost::base_from_member::member); iterator_type i = resolver.resolve(query, ec); if (!ec) { iterator_type end; ec = asio::error::host_not_found; while (ec && i != end) { this->basic_socket::close(); this->basic_socket::connect(*i, ec); ++i; } } } enum { putback_max = 8 }; enum { buffer_size = 512 }; boost::array get_buffer_; boost::array put_buffer_; bool unbuffered_; }; } // namespace asio #include "asio/detail/pop_options.hpp" #undef ASIO_PRIVATE_CONNECT_DEF #endif // !defined(BOOST_NO_IOSTREAM) #endif // ASIO_BASIC_SOCKET_STREAMBUF_HPP percona-xtradb-cluster-galera/asio/asio/basic_stream_socket.hpp0000644000000000000000000006736612247075736025277 0ustar rootroot00000000000000// // basic_stream_socket.hpp // ~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_BASIC_STREAM_SOCKET_HPP #define ASIO_BASIC_STREAM_SOCKET_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/basic_socket.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/stream_socket_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { /// Provides stream-oriented socket functionality. /** * The basic_stream_socket class template provides asynchronous and blocking * stream-oriented socket functionality. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. * * @par Concepts: * AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. */ template > class basic_stream_socket : public basic_socket { public: /// The native representation of a socket. typedef typename StreamSocketService::native_type native_type; /// The protocol type. typedef Protocol protocol_type; /// The endpoint type. typedef typename Protocol::endpoint endpoint_type; /// Construct a basic_stream_socket without opening it. /** * This constructor creates a stream socket without opening it. The socket * needs to be opened and then connected or accepted before data can be sent * or received on it. * * @param io_service The io_service object that the stream socket will use to * dispatch handlers for any asynchronous operations performed on the socket. */ explicit basic_stream_socket(asio::io_service& io_service) : basic_socket(io_service) { } /// Construct and open a basic_stream_socket. /** * This constructor creates and opens a stream socket. The socket needs to be * connected or accepted before data can be sent or received on it. * * @param io_service The io_service object that the stream socket will use to * dispatch handlers for any asynchronous operations performed on the socket. * * @param protocol An object specifying protocol parameters to be used. * * @throws asio::system_error Thrown on failure. */ basic_stream_socket(asio::io_service& io_service, const protocol_type& protocol) : basic_socket(io_service, protocol) { } /// Construct a basic_stream_socket, opening it and binding it to the given /// local endpoint. /** * This constructor creates a stream socket and automatically opens it bound * to the specified endpoint on the local machine. The protocol used is the * protocol associated with the given endpoint. * * @param io_service The io_service object that the stream socket will use to * dispatch handlers for any asynchronous operations performed on the socket. * * @param endpoint An endpoint on the local machine to which the stream * socket will be bound. * * @throws asio::system_error Thrown on failure. */ basic_stream_socket(asio::io_service& io_service, const endpoint_type& endpoint) : basic_socket(io_service, endpoint) { } /// Construct a basic_stream_socket on an existing native socket. /** * This constructor creates a stream socket object to hold an existing native * socket. * * @param io_service The io_service object that the stream socket will use to * dispatch handlers for any asynchronous operations performed on the socket. * * @param protocol An object specifying protocol parameters to be used. * * @param native_socket The new underlying socket implementation. * * @throws asio::system_error Thrown on failure. */ basic_stream_socket(asio::io_service& io_service, const protocol_type& protocol, const native_type& native_socket) : basic_socket( io_service, protocol, native_socket) { } /// Send some data on the socket. /** * This function is used to send data on the stream socket. The function * call will block until one or more bytes of the data has been sent * successfully, or an until error occurs. * * @param buffers One or more data buffers to be sent on the socket. * * @returns The number of bytes sent. * * @throws asio::system_error Thrown on failure. * * @note The send operation may not transmit all of the data to the peer. * Consider using the @ref write function if you need to ensure that all data * is written before the blocking operation completes. * * @par Example * To send a single data buffer use the @ref buffer function as follows: * @code * socket.send(asio::buffer(data, size)); * @endcode * See the @ref buffer documentation for information on sending multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template std::size_t send(const ConstBufferSequence& buffers) { asio::error_code ec; std::size_t s = this->service.send( this->implementation, buffers, 0, ec); asio::detail::throw_error(ec); return s; } /// Send some data on the socket. /** * This function is used to send data on the stream socket. The function * call will block until one or more bytes of the data has been sent * successfully, or an until error occurs. * * @param buffers One or more data buffers to be sent on the socket. * * @param flags Flags specifying how the send call is to be made. * * @returns The number of bytes sent. * * @throws asio::system_error Thrown on failure. * * @note The send operation may not transmit all of the data to the peer. * Consider using the @ref write function if you need to ensure that all data * is written before the blocking operation completes. * * @par Example * To send a single data buffer use the @ref buffer function as follows: * @code * socket.send(asio::buffer(data, size), 0); * @endcode * See the @ref buffer documentation for information on sending multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template std::size_t send(const ConstBufferSequence& buffers, socket_base::message_flags flags) { asio::error_code ec; std::size_t s = this->service.send( this->implementation, buffers, flags, ec); asio::detail::throw_error(ec); return s; } /// Send some data on the socket. /** * This function is used to send data on the stream socket. The function * call will block until one or more bytes of the data has been sent * successfully, or an until error occurs. * * @param buffers One or more data buffers to be sent on the socket. * * @param flags Flags specifying how the send call is to be made. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes sent. Returns 0 if an error occurred. * * @note The send operation may not transmit all of the data to the peer. * Consider using the @ref write function if you need to ensure that all data * is written before the blocking operation completes. */ template std::size_t send(const ConstBufferSequence& buffers, socket_base::message_flags flags, asio::error_code& ec) { return this->service.send(this->implementation, buffers, flags, ec); } /// Start an asynchronous send. /** * This function is used to asynchronously send data on the stream socket. * The function call always returns immediately. * * @param buffers One or more data buffers to be sent on the socket. Although * the buffers object may be copied as necessary, ownership of the underlying * memory blocks is retained by the caller, which must guarantee that they * remain valid until the handler is called. * * @param handler The handler to be called when the send operation completes. * Copies will be made of the handler as required. The function signature of * the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes sent. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note The send operation may not transmit all of the data to the peer. * Consider using the @ref async_write function if you need to ensure that all * data is written before the asynchronous operation completes. * * @par Example * To send a single data buffer use the @ref buffer function as follows: * @code * socket.async_send(asio::buffer(data, size), handler); * @endcode * See the @ref buffer documentation for information on sending multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template void async_send(const ConstBufferSequence& buffers, WriteHandler handler) { this->service.async_send(this->implementation, buffers, 0, handler); } /// Start an asynchronous send. /** * This function is used to asynchronously send data on the stream socket. * The function call always returns immediately. * * @param buffers One or more data buffers to be sent on the socket. Although * the buffers object may be copied as necessary, ownership of the underlying * memory blocks is retained by the caller, which must guarantee that they * remain valid until the handler is called. * * @param flags Flags specifying how the send call is to be made. * * @param handler The handler to be called when the send operation completes. * Copies will be made of the handler as required. The function signature of * the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes sent. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note The send operation may not transmit all of the data to the peer. * Consider using the @ref async_write function if you need to ensure that all * data is written before the asynchronous operation completes. * * @par Example * To send a single data buffer use the @ref buffer function as follows: * @code * socket.async_send(asio::buffer(data, size), 0, handler); * @endcode * See the @ref buffer documentation for information on sending multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template void async_send(const ConstBufferSequence& buffers, socket_base::message_flags flags, WriteHandler handler) { this->service.async_send(this->implementation, buffers, flags, handler); } /// Receive some data on the socket. /** * This function is used to receive data on the stream socket. The function * call will block until one or more bytes of data has been received * successfully, or until an error occurs. * * @param buffers One or more buffers into which the data will be received. * * @returns The number of bytes received. * * @throws asio::system_error Thrown on failure. An error code of * asio::error::eof indicates that the connection was closed by the * peer. * * @note The receive operation may not receive all of the requested number of * bytes. Consider using the @ref read function if you need to ensure that the * requested amount of data is read before the blocking operation completes. * * @par Example * To receive into a single data buffer use the @ref buffer function as * follows: * @code * socket.receive(asio::buffer(data, size)); * @endcode * See the @ref buffer documentation for information on receiving into * multiple buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template std::size_t receive(const MutableBufferSequence& buffers) { asio::error_code ec; std::size_t s = this->service.receive(this->implementation, buffers, 0, ec); asio::detail::throw_error(ec); return s; } /// Receive some data on the socket. /** * This function is used to receive data on the stream socket. The function * call will block until one or more bytes of data has been received * successfully, or until an error occurs. * * @param buffers One or more buffers into which the data will be received. * * @param flags Flags specifying how the receive call is to be made. * * @returns The number of bytes received. * * @throws asio::system_error Thrown on failure. An error code of * asio::error::eof indicates that the connection was closed by the * peer. * * @note The receive operation may not receive all of the requested number of * bytes. Consider using the @ref read function if you need to ensure that the * requested amount of data is read before the blocking operation completes. * * @par Example * To receive into a single data buffer use the @ref buffer function as * follows: * @code * socket.receive(asio::buffer(data, size), 0); * @endcode * See the @ref buffer documentation for information on receiving into * multiple buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template std::size_t receive(const MutableBufferSequence& buffers, socket_base::message_flags flags) { asio::error_code ec; std::size_t s = this->service.receive( this->implementation, buffers, flags, ec); asio::detail::throw_error(ec); return s; } /// Receive some data on a connected socket. /** * This function is used to receive data on the stream socket. The function * call will block until one or more bytes of data has been received * successfully, or until an error occurs. * * @param buffers One or more buffers into which the data will be received. * * @param flags Flags specifying how the receive call is to be made. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes received. Returns 0 if an error occurred. * * @note The receive operation may not receive all of the requested number of * bytes. Consider using the @ref read function if you need to ensure that the * requested amount of data is read before the blocking operation completes. */ template std::size_t receive(const MutableBufferSequence& buffers, socket_base::message_flags flags, asio::error_code& ec) { return this->service.receive(this->implementation, buffers, flags, ec); } /// Start an asynchronous receive. /** * This function is used to asynchronously receive data from the stream * socket. The function call always returns immediately. * * @param buffers One or more buffers into which the data will be received. * Although the buffers object may be copied as necessary, ownership of the * underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param handler The handler to be called when the receive operation * completes. Copies will be made of the handler as required. The function * signature of the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes received. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note The receive operation may not receive all of the requested number of * bytes. Consider using the @ref async_read function if you need to ensure * that the requested amount of data is received before the asynchronous * operation completes. * * @par Example * To receive into a single data buffer use the @ref buffer function as * follows: * @code * socket.async_receive(asio::buffer(data, size), handler); * @endcode * See the @ref buffer documentation for information on receiving into * multiple buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template void async_receive(const MutableBufferSequence& buffers, ReadHandler handler) { this->service.async_receive(this->implementation, buffers, 0, handler); } /// Start an asynchronous receive. /** * This function is used to asynchronously receive data from the stream * socket. The function call always returns immediately. * * @param buffers One or more buffers into which the data will be received. * Although the buffers object may be copied as necessary, ownership of the * underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param flags Flags specifying how the receive call is to be made. * * @param handler The handler to be called when the receive operation * completes. Copies will be made of the handler as required. The function * signature of the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes received. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note The receive operation may not receive all of the requested number of * bytes. Consider using the @ref async_read function if you need to ensure * that the requested amount of data is received before the asynchronous * operation completes. * * @par Example * To receive into a single data buffer use the @ref buffer function as * follows: * @code * socket.async_receive(asio::buffer(data, size), 0, handler); * @endcode * See the @ref buffer documentation for information on receiving into * multiple buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template void async_receive(const MutableBufferSequence& buffers, socket_base::message_flags flags, ReadHandler handler) { this->service.async_receive(this->implementation, buffers, flags, handler); } /// Write some data to the socket. /** * This function is used to write data to the stream socket. The function call * will block until one or more bytes of the data has been written * successfully, or until an error occurs. * * @param buffers One or more data buffers to be written to the socket. * * @returns The number of bytes written. * * @throws asio::system_error Thrown on failure. An error code of * asio::error::eof indicates that the connection was closed by the * peer. * * @note The write_some operation may not transmit all of the data to the * peer. Consider using the @ref write function if you need to ensure that * all data is written before the blocking operation completes. * * @par Example * To write a single data buffer use the @ref buffer function as follows: * @code * socket.write_some(asio::buffer(data, size)); * @endcode * See the @ref buffer documentation for information on writing multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template std::size_t write_some(const ConstBufferSequence& buffers) { asio::error_code ec; std::size_t s = this->service.send(this->implementation, buffers, 0, ec); asio::detail::throw_error(ec); return s; } /// Write some data to the socket. /** * This function is used to write data to the stream socket. The function call * will block until one or more bytes of the data has been written * successfully, or until an error occurs. * * @param buffers One or more data buffers to be written to the socket. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes written. Returns 0 if an error occurred. * * @note The write_some operation may not transmit all of the data to the * peer. Consider using the @ref write function if you need to ensure that * all data is written before the blocking operation completes. */ template std::size_t write_some(const ConstBufferSequence& buffers, asio::error_code& ec) { return this->service.send(this->implementation, buffers, 0, ec); } /// Start an asynchronous write. /** * This function is used to asynchronously write data to the stream socket. * The function call always returns immediately. * * @param buffers One or more data buffers to be written to the socket. * Although the buffers object may be copied as necessary, ownership of the * underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param handler The handler to be called when the write operation completes. * Copies will be made of the handler as required. The function signature of * the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes written. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note The write operation may not transmit all of the data to the peer. * Consider using the @ref async_write function if you need to ensure that all * data is written before the asynchronous operation completes. * * @par Example * To write a single data buffer use the @ref buffer function as follows: * @code * socket.async_write_some(asio::buffer(data, size), handler); * @endcode * See the @ref buffer documentation for information on writing multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template void async_write_some(const ConstBufferSequence& buffers, WriteHandler handler) { this->service.async_send(this->implementation, buffers, 0, handler); } /// Read some data from the socket. /** * This function is used to read data from the stream socket. The function * call will block until one or more bytes of data has been read successfully, * or until an error occurs. * * @param buffers One or more buffers into which the data will be read. * * @returns The number of bytes read. * * @throws asio::system_error Thrown on failure. An error code of * asio::error::eof indicates that the connection was closed by the * peer. * * @note The read_some operation may not read all of the requested number of * bytes. Consider using the @ref read function if you need to ensure that * the requested amount of data is read before the blocking operation * completes. * * @par Example * To read into a single data buffer use the @ref buffer function as follows: * @code * socket.read_some(asio::buffer(data, size)); * @endcode * See the @ref buffer documentation for information on reading into multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template std::size_t read_some(const MutableBufferSequence& buffers) { asio::error_code ec; std::size_t s = this->service.receive(this->implementation, buffers, 0, ec); asio::detail::throw_error(ec); return s; } /// Read some data from the socket. /** * This function is used to read data from the stream socket. The function * call will block until one or more bytes of data has been read successfully, * or until an error occurs. * * @param buffers One or more buffers into which the data will be read. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes read. Returns 0 if an error occurred. * * @note The read_some operation may not read all of the requested number of * bytes. Consider using the @ref read function if you need to ensure that * the requested amount of data is read before the blocking operation * completes. */ template std::size_t read_some(const MutableBufferSequence& buffers, asio::error_code& ec) { return this->service.receive(this->implementation, buffers, 0, ec); } /// Start an asynchronous read. /** * This function is used to asynchronously read data from the stream socket. * The function call always returns immediately. * * @param buffers One or more buffers into which the data will be read. * Although the buffers object may be copied as necessary, ownership of the * underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param handler The handler to be called when the read operation completes. * Copies will be made of the handler as required. The function signature of * the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes read. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note The read operation may not read all of the requested number of bytes. * Consider using the @ref async_read function if you need to ensure that the * requested amount of data is read before the asynchronous operation * completes. * * @par Example * To read into a single data buffer use the @ref buffer function as follows: * @code * socket.async_read_some(asio::buffer(data, size), handler); * @endcode * See the @ref buffer documentation for information on reading into multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template void async_read_some(const MutableBufferSequence& buffers, ReadHandler handler) { this->service.async_receive(this->implementation, buffers, 0, handler); } }; } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_BASIC_STREAM_SOCKET_HPP percona-xtradb-cluster-galera/asio/asio/basic_streambuf.hpp0000644000000000000000000002557612247075736024421 0ustar rootroot00000000000000// // basic_streambuf.hpp // ~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_BASIC_STREAMBUF_HPP #define ASIO_BASIC_STREAMBUF_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(BOOST_NO_IOSTREAM) #include #include #include #include #include #include #include #include "asio/basic_streambuf_fwd.hpp" #include "asio/buffer.hpp" #include "asio/detail/noncopyable.hpp" #include "asio/detail/push_options.hpp" namespace asio { /// Automatically resizable buffer class based on std::streambuf. /** * The @c basic_streambuf class is derived from @c std::streambuf to associate * the streambuf's input and output sequences with one or more character * arrays. These character arrays are internal to the @c basic_streambuf * object, but direct access to the array elements is provided to permit them * to be used efficiently with I/O operations. Characters written to the output * sequence of a @c basic_streambuf object are appended to the input sequence * of the same object. * * The @c basic_streambuf class's public interface is intended to permit the * following implementation strategies: * * @li A single contiguous character array, which is reallocated as necessary * to accommodate changes in the size of the character sequence. This is the * implementation approach currently used in Asio. * * @li A sequence of one or more character arrays, where each array is of the * same size. Additional character array objects are appended to the sequence * to accommodate changes in the size of the character sequence. * * @li A sequence of one or more character arrays of varying sizes. Additional * character array objects are appended to the sequence to accommodate changes * in the size of the character sequence. * * The constructor for basic_streambuf accepts a @c size_t argument specifying * the maximum of the sum of the sizes of the input sequence and output * sequence. During the lifetime of the @c basic_streambuf object, the following * invariant holds: * @code size() <= max_size()@endcode * Any member function that would, if successful, cause the invariant to be * violated shall throw an exception of class @c std::length_error. * * The constructor for @c basic_streambuf takes an Allocator argument. A copy * of this argument is used for any memory allocation performed, by the * constructor and by all member functions, during the lifetime of each @c * basic_streambuf object. * * @par Examples * Writing directly from an streambuf to a socket: * @code * asio::streambuf b; * std::ostream os(&b); * os << "Hello, World!\n"; * * // try sending some data in input sequence * size_t n = sock.send(b.data()); * * b.consume(n); // sent data is removed from input sequence * @endcode * * Reading from a socket directly into a streambuf: * @code * asio::streambuf b; * * // reserve 512 bytes in output sequence * asio::streambuf::mutable_buffers_type bufs = b.prepare(512); * * size_t n = sock.receive(bufs); * * // received data is "committed" from output sequence to input sequence * b.commit(n); * * std::istream is(&b); * std::string s; * is >> s; * @endcode */ #if defined(GENERATING_DOCUMENTATION) template > #else template #endif class basic_streambuf : public std::streambuf, private noncopyable { public: #if defined(GENERATING_DOCUMENTATION) /// The type used to represent the input sequence as a list of buffers. typedef implementation_defined const_buffers_type; /// The type used to represent the output sequence as a list of buffers. typedef implementation_defined mutable_buffers_type; #else typedef asio::const_buffers_1 const_buffers_type; typedef asio::mutable_buffers_1 mutable_buffers_type; #endif /// Construct a basic_streambuf object. /** * Constructs a streambuf with the specified maximum size. The initial size * of the streambuf's input sequence is 0. */ explicit basic_streambuf( std::size_t max_size = (std::numeric_limits::max)(), const Allocator& allocator = Allocator()) : max_size_(max_size), buffer_(allocator) { std::size_t pend = (std::min)(max_size_, buffer_delta); buffer_.resize((std::max)(pend, 1)); setg(&buffer_[0], &buffer_[0], &buffer_[0]); setp(&buffer_[0], &buffer_[0] + pend); } /// Get the size of the input sequence. /** * @returns The size of the input sequence. The value is equal to that * calculated for @c s in the following code: * @code * size_t s = 0; * const_buffers_type bufs = data(); * const_buffers_type::const_iterator i = bufs.begin(); * while (i != bufs.end()) * { * const_buffer buf(*i++); * s += buffer_size(buf); * } * @endcode */ std::size_t size() const { return pptr() - gptr(); } /// Get the maximum size of the basic_streambuf. /** * @returns The allowed maximum of the sum of the sizes of the input sequence * and output sequence. */ std::size_t max_size() const { return max_size_; } /// Get a list of buffers that represents the input sequence. /** * @returns An object of type @c const_buffers_type that satisfies * ConstBufferSequence requirements, representing all character arrays in the * input sequence. * * @note The returned object is invalidated by any @c basic_streambuf member * function that modifies the input sequence or output sequence. */ const_buffers_type data() const { return asio::buffer(asio::const_buffer(gptr(), (pptr() - gptr()) * sizeof(char_type))); } /// Get a list of buffers that represents the output sequence, with the given /// size. /** * Ensures that the output sequence can accommodate @c n characters, * reallocating character array objects as necessary. * * @returns An object of type @c mutable_buffers_type that satisfies * MutableBufferSequence requirements, representing character array objects * at the start of the output sequence such that the sum of the buffer sizes * is @c n. * * @throws std::length_error If size() + n > max_size(). * * @note The returned object is invalidated by any @c basic_streambuf member * function that modifies the input sequence or output sequence. */ mutable_buffers_type prepare(std::size_t n) { reserve(n); return asio::buffer(asio::mutable_buffer( pptr(), n * sizeof(char_type))); } /// Move characters from the output sequence to the input sequence. /** * Appends @c n characters from the start of the output sequence to the input * sequence. The beginning of the output sequence is advanced by @c n * characters. * * Requires a preceding call prepare(x) where x >= n, and * no intervening operations that modify the input or output sequence. * * @throws std::length_error If @c n is greater than the size of the output * sequence. */ void commit(std::size_t n) { if (pptr() + n > epptr()) n = epptr() - pptr(); pbump(static_cast(n)); setg(eback(), gptr(), pptr()); } /// Remove characters from the input sequence. /** * Removes @c n characters from the beginning of the input sequence. * * @throws std::length_error If n > size(). */ void consume(std::size_t n) { if (gptr() + n > pptr()) n = pptr() - gptr(); gbump(static_cast(n)); } protected: enum { buffer_delta = 128 }; /// Override std::streambuf behaviour. /** * Behaves according to the specification of @c std::streambuf::underflow(). */ int_type underflow() { if (gptr() < pptr()) { setg(&buffer_[0], gptr(), pptr()); return traits_type::to_int_type(*gptr()); } else { return traits_type::eof(); } } /// Override std::streambuf behaviour. /** * Behaves according to the specification of @c std::streambuf::overflow(), * with the specialisation that @c std::length_error is thrown if appending * the character to the input sequence would require the condition * size() > max_size() to be true. */ int_type overflow(int_type c) { if (!traits_type::eq_int_type(c, traits_type::eof())) { if (pptr() == epptr()) { std::size_t buffer_size = pptr() - gptr(); if (buffer_size < max_size_ && max_size_ - buffer_size < buffer_delta) { reserve(max_size_ - buffer_size); } else { reserve(buffer_delta); } } *pptr() = traits_type::to_char_type(c); pbump(1); return c; } return traits_type::not_eof(c); } void reserve(std::size_t n) { // Get current stream positions as offsets. std::size_t gnext = gptr() - &buffer_[0]; std::size_t pnext = pptr() - &buffer_[0]; std::size_t pend = epptr() - &buffer_[0]; // Check if there is already enough space in the put area. if (n <= pend - pnext) { return; } // Shift existing contents of get area to start of buffer. if (gnext > 0) { pnext -= gnext; std::memmove(&buffer_[0], &buffer_[0] + gnext, pnext); } // Ensure buffer is large enough to hold at least the specified size. if (n > pend - pnext) { if (n <= max_size_ && pnext <= max_size_ - n) { pend = pnext + n; buffer_.resize((std::max)(pend, 1)); } else { std::length_error ex("asio::streambuf too long"); boost::throw_exception(ex); } } // Update stream positions. setg(&buffer_[0], &buffer_[0], &buffer_[0] + pnext); setp(&buffer_[0] + pnext, &buffer_[0] + pend); } private: std::size_t max_size_; std::vector buffer_; // Helper function to get the preferred size for reading data. friend std::size_t read_size_helper( basic_streambuf& sb, std::size_t max_size) { return std::min( std::max(512, sb.buffer_.capacity() - sb.size()), std::min(max_size, sb.max_size() - sb.size())); } }; // Helper function to get the preferred size for reading data. Used for any // user-provided specialisations of basic_streambuf. template inline std::size_t read_size_helper( basic_streambuf& sb, std::size_t max_size) { return std::min(512, std::min(max_size, sb.max_size() - sb.size())); } } // namespace asio #include "asio/detail/pop_options.hpp" #endif // !defined(BOOST_NO_IOSTREAM) #endif // ASIO_BASIC_STREAMBUF_HPP percona-xtradb-cluster-galera/asio/asio/basic_streambuf_fwd.hpp0000644000000000000000000000137512247075736025250 0ustar rootroot00000000000000// // basic_streambuf_fwd.hpp // ~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_BASIC_STREAMBUF_FWD_HPP #define ASIO_BASIC_STREAMBUF_FWD_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(BOOST_NO_IOSTREAM) #include namespace asio { template > class basic_streambuf; } // namespace asio #endif // !defined(BOOST_NO_IOSTREAM) #endif // ASIO_BASIC_STREAMBUF_FWD_HPP percona-xtradb-cluster-galera/asio/asio/buffer.hpp0000644000000000000000000007516112247075736022534 0ustar rootroot00000000000000// // buffer.hpp // ~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_BUFFER_HPP #define ASIO_BUFFER_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include #include #include "asio/detail/array_fwd.hpp" #if defined(BOOST_MSVC) # if defined(_HAS_ITERATOR_DEBUGGING) && (_HAS_ITERATOR_DEBUGGING != 0) # if !defined(ASIO_DISABLE_BUFFER_DEBUGGING) # define ASIO_ENABLE_BUFFER_DEBUGGING # endif // !defined(ASIO_DISABLE_BUFFER_DEBUGGING) # endif // defined(_HAS_ITERATOR_DEBUGGING) #endif // defined(BOOST_MSVC) #if defined(__GNUC__) # if defined(_GLIBCXX_DEBUG) # if !defined(ASIO_DISABLE_BUFFER_DEBUGGING) # define ASIO_ENABLE_BUFFER_DEBUGGING # endif // !defined(ASIO_DISABLE_BUFFER_DEBUGGING) # endif // defined(_GLIBCXX_DEBUG) #endif // defined(__GNUC__) #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) # include #endif // ASIO_ENABLE_BUFFER_DEBUGGING #if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582)) \ || BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x590)) # include #endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582)) // || BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x590)) #include "asio/detail/push_options.hpp" namespace asio { class mutable_buffer; class const_buffer; namespace detail { void* buffer_cast_helper(const mutable_buffer&); const void* buffer_cast_helper(const const_buffer&); std::size_t buffer_size_helper(const mutable_buffer&); std::size_t buffer_size_helper(const const_buffer&); } // namespace detail /// Holds a buffer that can be modified. /** * The mutable_buffer class provides a safe representation of a buffer that can * be modified. It does not own the underlying data, and so is cheap to copy or * assign. */ class mutable_buffer { public: /// Construct an empty buffer. mutable_buffer() : data_(0), size_(0) { } /// Construct a buffer to represent a given memory range. mutable_buffer(void* data, std::size_t size) : data_(data), size_(size) { } #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) mutable_buffer(void* data, std::size_t size, boost::function debug_check) : data_(data), size_(size), debug_check_(debug_check) { } const boost::function& get_debug_check() const { return debug_check_; } #endif // ASIO_ENABLE_BUFFER_DEBUGGING private: friend void* asio::detail::buffer_cast_helper( const mutable_buffer& b); friend std::size_t asio::detail::buffer_size_helper( const mutable_buffer& b); void* data_; std::size_t size_; #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) boost::function debug_check_; #endif // ASIO_ENABLE_BUFFER_DEBUGGING }; namespace detail { inline void* buffer_cast_helper(const mutable_buffer& b) { #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) if (b.size_ && b.debug_check_) b.debug_check_(); #endif // ASIO_ENABLE_BUFFER_DEBUGGING return b.data_; } inline std::size_t buffer_size_helper(const mutable_buffer& b) { return b.size_; } } // namespace detail /// Cast a non-modifiable buffer to a specified pointer to POD type. /** * @relates mutable_buffer */ template inline PointerToPodType buffer_cast(const mutable_buffer& b) { return static_cast(detail::buffer_cast_helper(b)); } /// Get the number of bytes in a non-modifiable buffer. /** * @relates mutable_buffer */ inline std::size_t buffer_size(const mutable_buffer& b) { return detail::buffer_size_helper(b); } /// Create a new modifiable buffer that is offset from the start of another. /** * @relates mutable_buffer */ inline mutable_buffer operator+(const mutable_buffer& b, std::size_t start) { if (start > buffer_size(b)) return mutable_buffer(); char* new_data = buffer_cast(b) + start; std::size_t new_size = buffer_size(b) - start; return mutable_buffer(new_data, new_size #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) , b.get_debug_check() #endif // ASIO_ENABLE_BUFFER_DEBUGGING ); } /// Create a new modifiable buffer that is offset from the start of another. /** * @relates mutable_buffer */ inline mutable_buffer operator+(std::size_t start, const mutable_buffer& b) { if (start > buffer_size(b)) return mutable_buffer(); char* new_data = buffer_cast(b) + start; std::size_t new_size = buffer_size(b) - start; return mutable_buffer(new_data, new_size #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) , b.get_debug_check() #endif // ASIO_ENABLE_BUFFER_DEBUGGING ); } /// Adapts a single modifiable buffer so that it meets the requirements of the /// MutableBufferSequence concept. class mutable_buffers_1 : public mutable_buffer { public: /// The type for each element in the list of buffers. typedef mutable_buffer value_type; /// A random-access iterator type that may be used to read elements. typedef const mutable_buffer* const_iterator; /// Construct to represent a given memory range. mutable_buffers_1(void* data, std::size_t size) : mutable_buffer(data, size) { } /// Construct to represent a single modifiable buffer. explicit mutable_buffers_1(const mutable_buffer& b) : mutable_buffer(b) { } /// Get a random-access iterator to the first element. const_iterator begin() const { return this; } /// Get a random-access iterator for one past the last element. const_iterator end() const { return begin() + 1; } }; /// Holds a buffer that cannot be modified. /** * The const_buffer class provides a safe representation of a buffer that cannot * be modified. It does not own the underlying data, and so is cheap to copy or * assign. */ class const_buffer { public: /// Construct an empty buffer. const_buffer() : data_(0), size_(0) { } /// Construct a buffer to represent a given memory range. const_buffer(const void* data, std::size_t size) : data_(data), size_(size) { } /// Construct a non-modifiable buffer from a modifiable one. const_buffer(const mutable_buffer& b) : data_(asio::detail::buffer_cast_helper(b)), size_(asio::detail::buffer_size_helper(b)) #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) , debug_check_(b.get_debug_check()) #endif // ASIO_ENABLE_BUFFER_DEBUGGING { } #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) const_buffer(const void* data, std::size_t size, boost::function debug_check) : data_(data), size_(size), debug_check_(debug_check) { } const boost::function& get_debug_check() const { return debug_check_; } #endif // ASIO_ENABLE_BUFFER_DEBUGGING private: friend const void* asio::detail::buffer_cast_helper( const const_buffer& b); friend std::size_t asio::detail::buffer_size_helper( const const_buffer& b); const void* data_; std::size_t size_; #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) boost::function debug_check_; #endif // ASIO_ENABLE_BUFFER_DEBUGGING }; namespace detail { inline const void* buffer_cast_helper(const const_buffer& b) { #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) if (b.size_ && b.debug_check_) b.debug_check_(); #endif // ASIO_ENABLE_BUFFER_DEBUGGING return b.data_; } inline std::size_t buffer_size_helper(const const_buffer& b) { return b.size_; } } // namespace detail /// Cast a non-modifiable buffer to a specified pointer to POD type. /** * @relates const_buffer */ template inline PointerToPodType buffer_cast(const const_buffer& b) { return static_cast(detail::buffer_cast_helper(b)); } /// Get the number of bytes in a non-modifiable buffer. /** * @relates const_buffer */ inline std::size_t buffer_size(const const_buffer& b) { return detail::buffer_size_helper(b); } /// Create a new non-modifiable buffer that is offset from the start of another. /** * @relates const_buffer */ inline const_buffer operator+(const const_buffer& b, std::size_t start) { if (start > buffer_size(b)) return const_buffer(); const char* new_data = buffer_cast(b) + start; std::size_t new_size = buffer_size(b) - start; return const_buffer(new_data, new_size #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) , b.get_debug_check() #endif // ASIO_ENABLE_BUFFER_DEBUGGING ); } /// Create a new non-modifiable buffer that is offset from the start of another. /** * @relates const_buffer */ inline const_buffer operator+(std::size_t start, const const_buffer& b) { if (start > buffer_size(b)) return const_buffer(); const char* new_data = buffer_cast(b) + start; std::size_t new_size = buffer_size(b) - start; return const_buffer(new_data, new_size #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) , b.get_debug_check() #endif // ASIO_ENABLE_BUFFER_DEBUGGING ); } /// Adapts a single non-modifiable buffer so that it meets the requirements of /// the ConstBufferSequence concept. class const_buffers_1 : public const_buffer { public: /// The type for each element in the list of buffers. typedef const_buffer value_type; /// A random-access iterator type that may be used to read elements. typedef const const_buffer* const_iterator; /// Construct to represent a given memory range. const_buffers_1(const void* data, std::size_t size) : const_buffer(data, size) { } /// Construct to represent a single non-modifiable buffer. explicit const_buffers_1(const const_buffer& b) : const_buffer(b) { } /// Get a random-access iterator to the first element. const_iterator begin() const { return this; } /// Get a random-access iterator for one past the last element. const_iterator end() const { return begin() + 1; } }; /// An implementation of both the ConstBufferSequence and MutableBufferSequence /// concepts to represent a null buffer sequence. class null_buffers { public: /// The type for each element in the list of buffers. typedef mutable_buffer value_type; /// A random-access iterator type that may be used to read elements. typedef const mutable_buffer* const_iterator; /// Get a random-access iterator to the first element. const_iterator begin() const { return &buf_; } /// Get a random-access iterator for one past the last element. const_iterator end() const { return &buf_; } private: mutable_buffer buf_; }; #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) namespace detail { template class buffer_debug_check { public: buffer_debug_check(Iterator iter) : iter_(iter) { } ~buffer_debug_check() { #if BOOST_WORKAROUND(BOOST_MSVC, == 1400) // MSVC 8's string iterator checking may crash in a std::string::iterator // object's destructor when the iterator points to an already-destroyed // std::string object, unless the iterator is cleared first. iter_ = Iterator(); #endif // BOOST_WORKAROUND(BOOST_MSVC, == 1400) } void operator()() { *iter_; } private: Iterator iter_; }; } // namespace detail #endif // ASIO_ENABLE_BUFFER_DEBUGGING /** @defgroup buffer asio::buffer * * @brief The asio::buffer function is used to create a buffer object to * represent raw memory, an array of POD elements, a vector of POD elements, * or a std::string. * * A buffer object represents a contiguous region of memory as a 2-tuple * consisting of a pointer and size in bytes. A tuple of the form {void*, * size_t} specifies a mutable (modifiable) region of memory. Similarly, a * tuple of the form {const void*, size_t} specifies a const * (non-modifiable) region of memory. These two forms correspond to the classes * mutable_buffer and const_buffer, respectively. To mirror C++'s conversion * rules, a mutable_buffer is implicitly convertible to a const_buffer, and the * opposite conversion is not permitted. * * The simplest use case involves reading or writing a single buffer of a * specified size: * * @code sock.send(asio::buffer(data, size)); @endcode * * In the above example, the return value of asio::buffer meets the * requirements of the ConstBufferSequence concept so that it may be directly * passed to the socket's write function. A buffer created for modifiable * memory also meets the requirements of the MutableBufferSequence concept. * * An individual buffer may be created from a builtin array, std::vector or * boost::array of POD elements. This helps prevent buffer overruns by * automatically determining the size of the buffer: * * @code char d1[128]; * size_t bytes_transferred = sock.receive(asio::buffer(d1)); * * std::vector d2(128); * bytes_transferred = sock.receive(asio::buffer(d2)); * * boost::array d3; * bytes_transferred = sock.receive(asio::buffer(d3)); @endcode * * In all three cases above, the buffers created are exactly 128 bytes long. * Note that a vector is @e never automatically resized when creating or using * a buffer. The buffer size is determined using the vector's size() * member function, and not its capacity. * * @par Accessing Buffer Contents * * The contents of a buffer may be accessed using the asio::buffer_size * and asio::buffer_cast functions: * * @code asio::mutable_buffer b1 = ...; * std::size_t s1 = asio::buffer_size(b1); * unsigned char* p1 = asio::buffer_cast(b1); * * asio::const_buffer b2 = ...; * std::size_t s2 = asio::buffer_size(b2); * const void* p2 = asio::buffer_cast(b2); @endcode * * The asio::buffer_cast function permits violations of type safety, so * uses of it in application code should be carefully considered. * * @par Buffer Invalidation * * A buffer object does not have any ownership of the memory it refers to. It * is the responsibility of the application to ensure the memory region remains * valid until it is no longer required for an I/O operation. When the memory * is no longer available, the buffer is said to have been invalidated. * * For the asio::buffer overloads that accept an argument of type * std::vector, the buffer objects returned are invalidated by any vector * operation that also invalidates all references, pointers and iterators * referring to the elements in the sequence (C++ Std, 23.2.4) * * For the asio::buffer overloads that accept an argument of type * std::string, the buffer objects returned are invalidated according to the * rules defined for invalidation of references, pointers and iterators * referring to elements of the sequence (C++ Std, 21.3). * * @par Buffer Arithmetic * * Buffer objects may be manipulated using simple arithmetic in a safe way * which helps prevent buffer overruns. Consider an array initialised as * follows: * * @code boost::array a = { 'a', 'b', 'c', 'd', 'e' }; @endcode * * A buffer object @c b1 created using: * * @code b1 = asio::buffer(a); @endcode * * represents the entire array, { 'a', 'b', 'c', 'd', 'e' }. An * optional second argument to the asio::buffer function may be used to * limit the size, in bytes, of the buffer: * * @code b2 = asio::buffer(a, 3); @endcode * * such that @c b2 represents the data { 'a', 'b', 'c' }. Even if the * size argument exceeds the actual size of the array, the size of the buffer * object created will be limited to the array size. * * An offset may be applied to an existing buffer to create a new one: * * @code b3 = b1 + 2; @endcode * * where @c b3 will set to represent { 'c', 'd', 'e' }. If the offset * exceeds the size of the existing buffer, the newly created buffer will be * empty. * * Both an offset and size may be specified to create a buffer that corresponds * to a specific range of bytes within an existing buffer: * * @code b4 = asio::buffer(b1 + 1, 3); @endcode * * so that @c b4 will refer to the bytes { 'b', 'c', 'd' }. * * @par Buffers and Scatter-Gather I/O * * To read or write using multiple buffers (i.e. scatter-gather I/O), multiple * buffer objects may be assigned into a container that supports the * MutableBufferSequence (for read) or ConstBufferSequence (for write) concepts: * * @code * char d1[128]; * std::vector d2(128); * boost::array d3; * * boost::array bufs1 = { * asio::buffer(d1), * asio::buffer(d2), * asio::buffer(d3) }; * bytes_transferred = sock.receive(bufs1); * * std::vector bufs2; * bufs2.push_back(asio::buffer(d1)); * bufs2.push_back(asio::buffer(d2)); * bufs2.push_back(asio::buffer(d3)); * bytes_transferred = sock.send(bufs2); @endcode */ /*@{*/ /// Create a new modifiable buffer from an existing buffer. /** * @returns mutable_buffers_1(b). */ inline mutable_buffers_1 buffer(const mutable_buffer& b) { return mutable_buffers_1(b); } /// Create a new modifiable buffer from an existing buffer. /** * @returns A mutable_buffers_1 value equivalent to: * @code mutable_buffers_1( * buffer_cast(b), * min(buffer_size(b), max_size_in_bytes)); @endcode */ inline mutable_buffers_1 buffer(const mutable_buffer& b, std::size_t max_size_in_bytes) { return mutable_buffers_1( mutable_buffer(buffer_cast(b), buffer_size(b) < max_size_in_bytes ? buffer_size(b) : max_size_in_bytes #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) , b.get_debug_check() #endif // ASIO_ENABLE_BUFFER_DEBUGGING )); } /// Create a new non-modifiable buffer from an existing buffer. /** * @returns const_buffers_1(b). */ inline const_buffers_1 buffer(const const_buffer& b) { return const_buffers_1(b); } /// Create a new non-modifiable buffer from an existing buffer. /** * @returns A const_buffers_1 value equivalent to: * @code const_buffers_1( * buffer_cast(b), * min(buffer_size(b), max_size_in_bytes)); @endcode */ inline const_buffers_1 buffer(const const_buffer& b, std::size_t max_size_in_bytes) { return const_buffers_1( const_buffer(buffer_cast(b), buffer_size(b) < max_size_in_bytes ? buffer_size(b) : max_size_in_bytes #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) , b.get_debug_check() #endif // ASIO_ENABLE_BUFFER_DEBUGGING )); } /// Create a new modifiable buffer that represents the given memory range. /** * @returns mutable_buffers_1(data, size_in_bytes). */ inline mutable_buffers_1 buffer(void* data, std::size_t size_in_bytes) { return mutable_buffers_1(mutable_buffer(data, size_in_bytes)); } /// Create a new non-modifiable buffer that represents the given memory range. /** * @returns const_buffers_1(data, size_in_bytes). */ inline const_buffers_1 buffer(const void* data, std::size_t size_in_bytes) { return const_buffers_1(const_buffer(data, size_in_bytes)); } /// Create a new modifiable buffer that represents the given POD array. /** * @returns A mutable_buffers_1 value equivalent to: * @code mutable_buffers_1( * static_cast(data), * N * sizeof(PodType)); @endcode */ template inline mutable_buffers_1 buffer(PodType (&data)[N]) { return mutable_buffers_1(mutable_buffer(data, N * sizeof(PodType))); } /// Create a new modifiable buffer that represents the given POD array. /** * @returns A mutable_buffers_1 value equivalent to: * @code mutable_buffers_1( * static_cast(data), * min(N * sizeof(PodType), max_size_in_bytes)); @endcode */ template inline mutable_buffers_1 buffer(PodType (&data)[N], std::size_t max_size_in_bytes) { return mutable_buffers_1( mutable_buffer(data, N * sizeof(PodType) < max_size_in_bytes ? N * sizeof(PodType) : max_size_in_bytes)); } /// Create a new non-modifiable buffer that represents the given POD array. /** * @returns A const_buffers_1 value equivalent to: * @code const_buffers_1( * static_cast(data), * N * sizeof(PodType)); @endcode */ template inline const_buffers_1 buffer(const PodType (&data)[N]) { return const_buffers_1(const_buffer(data, N * sizeof(PodType))); } /// Create a new non-modifiable buffer that represents the given POD array. /** * @returns A const_buffers_1 value equivalent to: * @code const_buffers_1( * static_cast(data), * min(N * sizeof(PodType), max_size_in_bytes)); @endcode */ template inline const_buffers_1 buffer(const PodType (&data)[N], std::size_t max_size_in_bytes) { return const_buffers_1( const_buffer(data, N * sizeof(PodType) < max_size_in_bytes ? N * sizeof(PodType) : max_size_in_bytes)); } #if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582)) \ || BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x590)) // Borland C++ and Sun Studio think the overloads: // // unspecified buffer(boost::array& array ...); // // and // // unspecified buffer(boost::array& array ...); // // are ambiguous. This will be worked around by using a buffer_types traits // class that contains typedefs for the appropriate buffer and container // classes, based on whether PodType is const or non-const. namespace detail { template struct buffer_types_base; template <> struct buffer_types_base { typedef mutable_buffer buffer_type; typedef mutable_buffers_1 container_type; }; template <> struct buffer_types_base { typedef const_buffer buffer_type; typedef const_buffers_1 container_type; }; template struct buffer_types : public buffer_types_base::value> { }; } // namespace detail template inline typename detail::buffer_types::container_type buffer(boost::array& data) { typedef typename asio::detail::buffer_types::buffer_type buffer_type; typedef typename asio::detail::buffer_types::container_type container_type; return container_type( buffer_type(data.c_array(), data.size() * sizeof(PodType))); } template inline typename detail::buffer_types::container_type buffer(boost::array& data, std::size_t max_size_in_bytes) { typedef typename asio::detail::buffer_types::buffer_type buffer_type; typedef typename asio::detail::buffer_types::container_type container_type; return container_type( buffer_type(data.c_array(), data.size() * sizeof(PodType) < max_size_in_bytes ? data.size() * sizeof(PodType) : max_size_in_bytes)); } #else // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582)) // || BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x590)) /// Create a new modifiable buffer that represents the given POD array. /** * @returns A mutable_buffers_1 value equivalent to: * @code mutable_buffers_1( * data.data(), * data.size() * sizeof(PodType)); @endcode */ template inline mutable_buffers_1 buffer(boost::array& data) { return mutable_buffers_1( mutable_buffer(data.c_array(), data.size() * sizeof(PodType))); } /// Create a new modifiable buffer that represents the given POD array. /** * @returns A mutable_buffers_1 value equivalent to: * @code mutable_buffers_1( * data.data(), * min(data.size() * sizeof(PodType), max_size_in_bytes)); @endcode */ template inline mutable_buffers_1 buffer(boost::array& data, std::size_t max_size_in_bytes) { return mutable_buffers_1( mutable_buffer(data.c_array(), data.size() * sizeof(PodType) < max_size_in_bytes ? data.size() * sizeof(PodType) : max_size_in_bytes)); } /// Create a new non-modifiable buffer that represents the given POD array. /** * @returns A const_buffers_1 value equivalent to: * @code const_buffers_1( * data.data(), * data.size() * sizeof(PodType)); @endcode */ template inline const_buffers_1 buffer(boost::array& data) { return const_buffers_1( const_buffer(data.data(), data.size() * sizeof(PodType))); } /// Create a new non-modifiable buffer that represents the given POD array. /** * @returns A const_buffers_1 value equivalent to: * @code const_buffers_1( * data.data(), * min(data.size() * sizeof(PodType), max_size_in_bytes)); @endcode */ template inline const_buffers_1 buffer(boost::array& data, std::size_t max_size_in_bytes) { return const_buffers_1( const_buffer(data.data(), data.size() * sizeof(PodType) < max_size_in_bytes ? data.size() * sizeof(PodType) : max_size_in_bytes)); } #endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582)) // || BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x590)) /// Create a new non-modifiable buffer that represents the given POD array. /** * @returns A const_buffers_1 value equivalent to: * @code const_buffers_1( * data.data(), * data.size() * sizeof(PodType)); @endcode */ template inline const_buffers_1 buffer(const boost::array& data) { return const_buffers_1( const_buffer(data.data(), data.size() * sizeof(PodType))); } /// Create a new non-modifiable buffer that represents the given POD array. /** * @returns A const_buffers_1 value equivalent to: * @code const_buffers_1( * data.data(), * min(data.size() * sizeof(PodType), max_size_in_bytes)); @endcode */ template inline const_buffers_1 buffer(const boost::array& data, std::size_t max_size_in_bytes) { return const_buffers_1( const_buffer(data.data(), data.size() * sizeof(PodType) < max_size_in_bytes ? data.size() * sizeof(PodType) : max_size_in_bytes)); } /// Create a new modifiable buffer that represents the given POD vector. /** * @returns A mutable_buffers_1 value equivalent to: * @code mutable_buffers_1( * data.size() ? &data[0] : 0, * data.size() * sizeof(PodType)); @endcode * * @note The buffer is invalidated by any vector operation that would also * invalidate iterators. */ template inline mutable_buffers_1 buffer(std::vector& data) { return mutable_buffers_1( mutable_buffer(data.size() ? &data[0] : 0, data.size() * sizeof(PodType) #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) , detail::buffer_debug_check< typename std::vector::iterator >(data.begin()) #endif // ASIO_ENABLE_BUFFER_DEBUGGING )); } /// Create a new modifiable buffer that represents the given POD vector. /** * @returns A mutable_buffers_1 value equivalent to: * @code mutable_buffers_1( * data.size() ? &data[0] : 0, * min(data.size() * sizeof(PodType), max_size_in_bytes)); @endcode * * @note The buffer is invalidated by any vector operation that would also * invalidate iterators. */ template inline mutable_buffers_1 buffer(std::vector& data, std::size_t max_size_in_bytes) { return mutable_buffers_1( mutable_buffer(data.size() ? &data[0] : 0, data.size() * sizeof(PodType) < max_size_in_bytes ? data.size() * sizeof(PodType) : max_size_in_bytes #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) , detail::buffer_debug_check< typename std::vector::iterator >(data.begin()) #endif // ASIO_ENABLE_BUFFER_DEBUGGING )); } /// Create a new non-modifiable buffer that represents the given POD vector. /** * @returns A const_buffers_1 value equivalent to: * @code const_buffers_1( * data.size() ? &data[0] : 0, * data.size() * sizeof(PodType)); @endcode * * @note The buffer is invalidated by any vector operation that would also * invalidate iterators. */ template inline const_buffers_1 buffer( const std::vector& data) { return const_buffers_1( const_buffer(data.size() ? &data[0] : 0, data.size() * sizeof(PodType) #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) , detail::buffer_debug_check< typename std::vector::const_iterator >(data.begin()) #endif // ASIO_ENABLE_BUFFER_DEBUGGING )); } /// Create a new non-modifiable buffer that represents the given POD vector. /** * @returns A const_buffers_1 value equivalent to: * @code const_buffers_1( * data.size() ? &data[0] : 0, * min(data.size() * sizeof(PodType), max_size_in_bytes)); @endcode * * @note The buffer is invalidated by any vector operation that would also * invalidate iterators. */ template inline const_buffers_1 buffer( const std::vector& data, std::size_t max_size_in_bytes) { return const_buffers_1( const_buffer(data.size() ? &data[0] : 0, data.size() * sizeof(PodType) < max_size_in_bytes ? data.size() * sizeof(PodType) : max_size_in_bytes #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) , detail::buffer_debug_check< typename std::vector::const_iterator >(data.begin()) #endif // ASIO_ENABLE_BUFFER_DEBUGGING )); } /// Create a new non-modifiable buffer that represents the given string. /** * @returns const_buffers_1(data.data(), data.size()). * * @note The buffer is invalidated by any non-const operation called on the * given string object. */ inline const_buffers_1 buffer(const std::string& data) { return const_buffers_1(const_buffer(data.data(), data.size() #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) , detail::buffer_debug_check(data.begin()) #endif // ASIO_ENABLE_BUFFER_DEBUGGING )); } /// Create a new non-modifiable buffer that represents the given string. /** * @returns A const_buffers_1 value equivalent to: * @code const_buffers_1( * data.data(), * min(data.size(), max_size_in_bytes)); @endcode * * @note The buffer is invalidated by any non-const operation called on the * given string object. */ inline const_buffers_1 buffer(const std::string& data, std::size_t max_size_in_bytes) { return const_buffers_1( const_buffer(data.data(), data.size() < max_size_in_bytes ? data.size() : max_size_in_bytes #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) , detail::buffer_debug_check(data.begin()) #endif // ASIO_ENABLE_BUFFER_DEBUGGING )); } /*@}*/ } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_BUFFER_HPP percona-xtradb-cluster-galera/asio/asio/buffered_read_stream.hpp0000644000000000000000000003267012247075736025411 0ustar rootroot00000000000000// // buffered_read_stream.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_BUFFERED_READ_STREAM_HPP #define ASIO_BUFFERED_READ_STREAM_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include #include "asio/buffered_read_stream_fwd.hpp" #include "asio/buffer.hpp" #include "asio/detail/bind_handler.hpp" #include "asio/detail/buffer_resize_guard.hpp" #include "asio/detail/buffered_stream_storage.hpp" #include "asio/detail/noncopyable.hpp" #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { /// Adds buffering to the read-related operations of a stream. /** * The buffered_read_stream class template can be used to add buffering to the * synchronous and asynchronous read operations of a stream. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. * * @par Concepts: * AsyncReadStream, AsyncWriteStream, Stream, Sync_Read_Stream, SyncWriteStream. */ template class buffered_read_stream : private noncopyable { public: /// The type of the next layer. typedef typename boost::remove_reference::type next_layer_type; /// The type of the lowest layer. typedef typename next_layer_type::lowest_layer_type lowest_layer_type; #if defined(GENERATING_DOCUMENTATION) /// The default buffer size. static const std::size_t default_buffer_size = implementation_defined; #else BOOST_STATIC_CONSTANT(std::size_t, default_buffer_size = 1024); #endif /// Construct, passing the specified argument to initialise the next layer. template explicit buffered_read_stream(Arg& a) : next_layer_(a), storage_(default_buffer_size) { } /// Construct, passing the specified argument to initialise the next layer. template buffered_read_stream(Arg& a, std::size_t buffer_size) : next_layer_(a), storage_(buffer_size) { } /// Get a reference to the next layer. next_layer_type& next_layer() { return next_layer_; } /// Get a reference to the lowest layer. lowest_layer_type& lowest_layer() { return next_layer_.lowest_layer(); } /// Get a const reference to the lowest layer. const lowest_layer_type& lowest_layer() const { return next_layer_.lowest_layer(); } /// (Deprecated: use get_io_service().) Get the io_service associated with /// the object. asio::io_service& io_service() { return next_layer_.get_io_service(); } /// Get the io_service associated with the object. asio::io_service& get_io_service() { return next_layer_.get_io_service(); } /// Close the stream. void close() { next_layer_.close(); } /// Close the stream. asio::error_code close(asio::error_code& ec) { return next_layer_.close(ec); } /// Write the given data to the stream. Returns the number of bytes written. /// Throws an exception on failure. template std::size_t write_some(const ConstBufferSequence& buffers) { return next_layer_.write_some(buffers); } /// Write the given data to the stream. Returns the number of bytes written, /// or 0 if an error occurred. template std::size_t write_some(const ConstBufferSequence& buffers, asio::error_code& ec) { return next_layer_.write_some(buffers, ec); } /// Start an asynchronous write. The data being written must be valid for the /// lifetime of the asynchronous operation. template void async_write_some(const ConstBufferSequence& buffers, WriteHandler handler) { next_layer_.async_write_some(buffers, handler); } /// Fill the buffer with some data. Returns the number of bytes placed in the /// buffer as a result of the operation. Throws an exception on failure. std::size_t fill() { detail::buffer_resize_guard resize_guard(storage_); std::size_t previous_size = storage_.size(); storage_.resize(storage_.capacity()); storage_.resize(previous_size + next_layer_.read_some(buffer( storage_.data() + previous_size, storage_.size() - previous_size))); resize_guard.commit(); return storage_.size() - previous_size; } /// Fill the buffer with some data. Returns the number of bytes placed in the /// buffer as a result of the operation, or 0 if an error occurred. std::size_t fill(asio::error_code& ec) { detail::buffer_resize_guard resize_guard(storage_); std::size_t previous_size = storage_.size(); storage_.resize(storage_.capacity()); storage_.resize(previous_size + next_layer_.read_some(buffer( storage_.data() + previous_size, storage_.size() - previous_size), ec)); resize_guard.commit(); return storage_.size() - previous_size; } template class fill_handler { public: fill_handler(asio::io_service& io_service, detail::buffered_stream_storage& storage, std::size_t previous_size, ReadHandler handler) : io_service_(io_service), storage_(storage), previous_size_(previous_size), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred) { storage_.resize(previous_size_ + bytes_transferred); io_service_.dispatch(detail::bind_handler( handler_, ec, bytes_transferred)); } private: asio::io_service& io_service_; detail::buffered_stream_storage& storage_; std::size_t previous_size_; ReadHandler handler_; }; /// Start an asynchronous fill. template void async_fill(ReadHandler handler) { std::size_t previous_size = storage_.size(); storage_.resize(storage_.capacity()); next_layer_.async_read_some( buffer( storage_.data() + previous_size, storage_.size() - previous_size), fill_handler(get_io_service(), storage_, previous_size, handler)); } /// Read some data from the stream. Returns the number of bytes read. Throws /// an exception on failure. template std::size_t read_some(const MutableBufferSequence& buffers) { typename MutableBufferSequence::const_iterator iter = buffers.begin(); typename MutableBufferSequence::const_iterator end = buffers.end(); size_t total_buffer_size = 0; for (; iter != end; ++iter) { asio::mutable_buffer buffer(*iter); total_buffer_size += asio::buffer_size(buffer); } if (total_buffer_size == 0) return 0; if (storage_.empty()) fill(); return copy(buffers); } /// Read some data from the stream. Returns the number of bytes read or 0 if /// an error occurred. template std::size_t read_some(const MutableBufferSequence& buffers, asio::error_code& ec) { ec = asio::error_code(); typename MutableBufferSequence::const_iterator iter = buffers.begin(); typename MutableBufferSequence::const_iterator end = buffers.end(); size_t total_buffer_size = 0; for (; iter != end; ++iter) { asio::mutable_buffer buffer(*iter); total_buffer_size += asio::buffer_size(buffer); } if (total_buffer_size == 0) return 0; if (storage_.empty() && !fill(ec)) return 0; return copy(buffers); } template class read_some_handler { public: read_some_handler(asio::io_service& io_service, detail::buffered_stream_storage& storage, const MutableBufferSequence& buffers, ReadHandler handler) : io_service_(io_service), storage_(storage), buffers_(buffers), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t) { if (ec || storage_.empty()) { std::size_t length = 0; io_service_.dispatch(detail::bind_handler(handler_, ec, length)); } else { using namespace std; // For memcpy. std::size_t bytes_avail = storage_.size(); std::size_t bytes_copied = 0; typename MutableBufferSequence::const_iterator iter = buffers_.begin(); typename MutableBufferSequence::const_iterator end = buffers_.end(); for (; iter != end && bytes_avail > 0; ++iter) { std::size_t max_length = buffer_size(*iter); std::size_t length = (max_length < bytes_avail) ? max_length : bytes_avail; memcpy(buffer_cast(*iter), storage_.data() + bytes_copied, length); bytes_copied += length; bytes_avail -= length; } storage_.consume(bytes_copied); io_service_.dispatch(detail::bind_handler(handler_, ec, bytes_copied)); } } private: asio::io_service& io_service_; detail::buffered_stream_storage& storage_; MutableBufferSequence buffers_; ReadHandler handler_; }; /// Start an asynchronous read. The buffer into which the data will be read /// must be valid for the lifetime of the asynchronous operation. template void async_read_some(const MutableBufferSequence& buffers, ReadHandler handler) { typename MutableBufferSequence::const_iterator iter = buffers.begin(); typename MutableBufferSequence::const_iterator end = buffers.end(); size_t total_buffer_size = 0; for (; iter != end; ++iter) { asio::mutable_buffer buffer(*iter); total_buffer_size += asio::buffer_size(buffer); } if (total_buffer_size == 0) { get_io_service().post(detail::bind_handler( handler, asio::error_code(), 0)); } else if (storage_.empty()) { async_fill(read_some_handler( get_io_service(), storage_, buffers, handler)); } else { std::size_t length = copy(buffers); get_io_service().post(detail::bind_handler( handler, asio::error_code(), length)); } } /// Peek at the incoming data on the stream. Returns the number of bytes read. /// Throws an exception on failure. template std::size_t peek(const MutableBufferSequence& buffers) { if (storage_.empty()) fill(); return peek_copy(buffers); } /// Peek at the incoming data on the stream. Returns the number of bytes read, /// or 0 if an error occurred. template std::size_t peek(const MutableBufferSequence& buffers, asio::error_code& ec) { ec = asio::error_code(); if (storage_.empty() && !fill(ec)) return 0; return peek_copy(buffers); } /// Determine the amount of data that may be read without blocking. std::size_t in_avail() { return storage_.size(); } /// Determine the amount of data that may be read without blocking. std::size_t in_avail(asio::error_code& ec) { ec = asio::error_code(); return storage_.size(); } private: /// Copy data out of the internal buffer to the specified target buffer. /// Returns the number of bytes copied. template std::size_t copy(const MutableBufferSequence& buffers) { using namespace std; // For memcpy. std::size_t bytes_avail = storage_.size(); std::size_t bytes_copied = 0; typename MutableBufferSequence::const_iterator iter = buffers.begin(); typename MutableBufferSequence::const_iterator end = buffers.end(); for (; iter != end && bytes_avail > 0; ++iter) { std::size_t max_length = buffer_size(*iter); std::size_t length = (max_length < bytes_avail) ? max_length : bytes_avail; memcpy(buffer_cast(*iter), storage_.data() + bytes_copied, length); bytes_copied += length; bytes_avail -= length; } storage_.consume(bytes_copied); return bytes_copied; } /// Copy data from the internal buffer to the specified target buffer, without /// removing the data from the internal buffer. Returns the number of bytes /// copied. template std::size_t peek_copy(const MutableBufferSequence& buffers) { using namespace std; // For memcpy. std::size_t bytes_avail = storage_.size(); std::size_t bytes_copied = 0; typename MutableBufferSequence::const_iterator iter = buffers.begin(); typename MutableBufferSequence::const_iterator end = buffers.end(); for (; iter != end && bytes_avail > 0; ++iter) { std::size_t max_length = buffer_size(*iter); std::size_t length = (max_length < bytes_avail) ? max_length : bytes_avail; memcpy(buffer_cast(*iter), storage_.data() + bytes_copied, length); bytes_copied += length; bytes_avail -= length; } return bytes_copied; } /// The next layer. Stream next_layer_; // The data in the buffer. detail::buffered_stream_storage storage_; }; } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_BUFFERED_READ_STREAM_HPP percona-xtradb-cluster-galera/asio/asio/buffered_read_stream_fwd.hpp0000644000000000000000000000120212247075736026234 0ustar rootroot00000000000000// // buffered_read_stream_fwd.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_BUFFERED_READ_STREAM_FWD_HPP #define ASIO_BUFFERED_READ_STREAM_FWD_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) namespace asio { template class buffered_read_stream; } // namespace asio #endif // ASIO_BUFFERED_READ_STREAM_FWD_HPP percona-xtradb-cluster-galera/asio/asio/buffered_stream.hpp0000644000000000000000000001631612247075736024415 0ustar rootroot00000000000000// // buffered_stream.hpp // ~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_BUFFERED_STREAM_HPP #define ASIO_BUFFERED_STREAM_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/buffered_read_stream.hpp" #include "asio/buffered_write_stream.hpp" #include "asio/buffered_stream_fwd.hpp" #include "asio/detail/noncopyable.hpp" #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { /// Adds buffering to the read- and write-related operations of a stream. /** * The buffered_stream class template can be used to add buffering to the * synchronous and asynchronous read and write operations of a stream. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. * * @par Concepts: * AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. */ template class buffered_stream : private noncopyable { public: /// The type of the next layer. typedef typename boost::remove_reference::type next_layer_type; /// The type of the lowest layer. typedef typename next_layer_type::lowest_layer_type lowest_layer_type; /// Construct, passing the specified argument to initialise the next layer. template explicit buffered_stream(Arg& a) : inner_stream_impl_(a), stream_impl_(inner_stream_impl_) { } /// Construct, passing the specified argument to initialise the next layer. template explicit buffered_stream(Arg& a, std::size_t read_buffer_size, std::size_t write_buffer_size) : inner_stream_impl_(a, write_buffer_size), stream_impl_(inner_stream_impl_, read_buffer_size) { } /// Get a reference to the next layer. next_layer_type& next_layer() { return stream_impl_.next_layer().next_layer(); } /// Get a reference to the lowest layer. lowest_layer_type& lowest_layer() { return stream_impl_.lowest_layer(); } /// Get a const reference to the lowest layer. const lowest_layer_type& lowest_layer() const { return stream_impl_.lowest_layer(); } /// (Deprecated: use get_io_service().) Get the io_service associated with /// the object. asio::io_service& io_service() { return stream_impl_.get_io_service(); } /// Get the io_service associated with the object. asio::io_service& get_io_service() { return stream_impl_.get_io_service(); } /// Close the stream. void close() { stream_impl_.close(); } /// Close the stream. asio::error_code close(asio::error_code& ec) { return stream_impl_.close(ec); } /// Flush all data from the buffer to the next layer. Returns the number of /// bytes written to the next layer on the last write operation. Throws an /// exception on failure. std::size_t flush() { return stream_impl_.next_layer().flush(); } /// Flush all data from the buffer to the next layer. Returns the number of /// bytes written to the next layer on the last write operation, or 0 if an /// error occurred. std::size_t flush(asio::error_code& ec) { return stream_impl_.next_layer().flush(ec); } /// Start an asynchronous flush. template void async_flush(WriteHandler handler) { return stream_impl_.next_layer().async_flush(handler); } /// Write the given data to the stream. Returns the number of bytes written. /// Throws an exception on failure. template std::size_t write_some(const ConstBufferSequence& buffers) { return stream_impl_.write_some(buffers); } /// Write the given data to the stream. Returns the number of bytes written, /// or 0 if an error occurred. template std::size_t write_some(const ConstBufferSequence& buffers, asio::error_code& ec) { return stream_impl_.write_some(buffers, ec); } /// Start an asynchronous write. The data being written must be valid for the /// lifetime of the asynchronous operation. template void async_write_some(const ConstBufferSequence& buffers, WriteHandler handler) { stream_impl_.async_write_some(buffers, handler); } /// Fill the buffer with some data. Returns the number of bytes placed in the /// buffer as a result of the operation. Throws an exception on failure. std::size_t fill() { return stream_impl_.fill(); } /// Fill the buffer with some data. Returns the number of bytes placed in the /// buffer as a result of the operation, or 0 if an error occurred. std::size_t fill(asio::error_code& ec) { return stream_impl_.fill(ec); } /// Start an asynchronous fill. template void async_fill(ReadHandler handler) { stream_impl_.async_fill(handler); } /// Read some data from the stream. Returns the number of bytes read. Throws /// an exception on failure. template std::size_t read_some(const MutableBufferSequence& buffers) { return stream_impl_.read_some(buffers); } /// Read some data from the stream. Returns the number of bytes read or 0 if /// an error occurred. template std::size_t read_some(const MutableBufferSequence& buffers, asio::error_code& ec) { return stream_impl_.read_some(buffers, ec); } /// Start an asynchronous read. The buffer into which the data will be read /// must be valid for the lifetime of the asynchronous operation. template void async_read_some(const MutableBufferSequence& buffers, ReadHandler handler) { stream_impl_.async_read_some(buffers, handler); } /// Peek at the incoming data on the stream. Returns the number of bytes read. /// Throws an exception on failure. template std::size_t peek(const MutableBufferSequence& buffers) { return stream_impl_.peek(buffers); } /// Peek at the incoming data on the stream. Returns the number of bytes read, /// or 0 if an error occurred. template std::size_t peek(const MutableBufferSequence& buffers, asio::error_code& ec) { return stream_impl_.peek(buffers, ec); } /// Determine the amount of data that may be read without blocking. std::size_t in_avail() { return stream_impl_.in_avail(); } /// Determine the amount of data that may be read without blocking. std::size_t in_avail(asio::error_code& ec) { return stream_impl_.in_avail(ec); } private: // The buffered write stream. typedef buffered_write_stream write_stream_type; write_stream_type inner_stream_impl_; // The buffered read stream. typedef buffered_read_stream read_stream_type; read_stream_type stream_impl_; }; } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_BUFFERED_STREAM_HPP percona-xtradb-cluster-galera/asio/asio/buffered_stream_fwd.hpp0000644000000000000000000000114412247075736025246 0ustar rootroot00000000000000// // buffered_stream_fwd.hpp // ~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_BUFFERED_STREAM_FWD_HPP #define ASIO_BUFFERED_STREAM_FWD_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) namespace asio { template class buffered_stream; } // namespace asio #endif // ASIO_BUFFERED_STREAM_FWD_HPP percona-xtradb-cluster-galera/asio/asio/buffered_write_stream.hpp0000644000000000000000000002776112247075736025635 0ustar rootroot00000000000000// // buffered_write_stream.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_BUFFERED_WRITE_STREAM_HPP #define ASIO_BUFFERED_WRITE_STREAM_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include #include "asio/buffered_write_stream_fwd.hpp" #include "asio/buffer.hpp" #include "asio/completion_condition.hpp" #include "asio/detail/bind_handler.hpp" #include "asio/detail/buffered_stream_storage.hpp" #include "asio/detail/noncopyable.hpp" #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/write.hpp" #include "asio/detail/push_options.hpp" namespace asio { /// Adds buffering to the write-related operations of a stream. /** * The buffered_write_stream class template can be used to add buffering to the * synchronous and asynchronous write operations of a stream. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. * * @par Concepts: * AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. */ template class buffered_write_stream : private noncopyable { public: /// The type of the next layer. typedef typename boost::remove_reference::type next_layer_type; /// The type of the lowest layer. typedef typename next_layer_type::lowest_layer_type lowest_layer_type; #if defined(GENERATING_DOCUMENTATION) /// The default buffer size. static const std::size_t default_buffer_size = implementation_defined; #else BOOST_STATIC_CONSTANT(std::size_t, default_buffer_size = 1024); #endif /// Construct, passing the specified argument to initialise the next layer. template explicit buffered_write_stream(Arg& a) : next_layer_(a), storage_(default_buffer_size) { } /// Construct, passing the specified argument to initialise the next layer. template buffered_write_stream(Arg& a, std::size_t buffer_size) : next_layer_(a), storage_(buffer_size) { } /// Get a reference to the next layer. next_layer_type& next_layer() { return next_layer_; } /// Get a reference to the lowest layer. lowest_layer_type& lowest_layer() { return next_layer_.lowest_layer(); } /// Get a const reference to the lowest layer. const lowest_layer_type& lowest_layer() const { return next_layer_.lowest_layer(); } /// (Deprecated: use get_io_service().) Get the io_service associated with /// the object. asio::io_service& io_service() { return next_layer_.get_io_service(); } /// Get the io_service associated with the object. asio::io_service& get_io_service() { return next_layer_.get_io_service(); } /// Close the stream. void close() { next_layer_.close(); } /// Close the stream. asio::error_code close(asio::error_code& ec) { return next_layer_.close(ec); } /// Flush all data from the buffer to the next layer. Returns the number of /// bytes written to the next layer on the last write operation. Throws an /// exception on failure. std::size_t flush() { std::size_t bytes_written = write(next_layer_, buffer(storage_.data(), storage_.size())); storage_.consume(bytes_written); return bytes_written; } /// Flush all data from the buffer to the next layer. Returns the number of /// bytes written to the next layer on the last write operation, or 0 if an /// error occurred. std::size_t flush(asio::error_code& ec) { std::size_t bytes_written = write(next_layer_, buffer(storage_.data(), storage_.size()), transfer_all(), ec); storage_.consume(bytes_written); return bytes_written; } template class flush_handler { public: flush_handler(asio::io_service& io_service, detail::buffered_stream_storage& storage, WriteHandler handler) : io_service_(io_service), storage_(storage), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t bytes_written) { storage_.consume(bytes_written); io_service_.dispatch(detail::bind_handler(handler_, ec, bytes_written)); } private: asio::io_service& io_service_; detail::buffered_stream_storage& storage_; WriteHandler handler_; }; /// Start an asynchronous flush. template void async_flush(WriteHandler handler) { async_write(next_layer_, buffer(storage_.data(), storage_.size()), flush_handler(get_io_service(), storage_, handler)); } /// Write the given data to the stream. Returns the number of bytes written. /// Throws an exception on failure. template std::size_t write_some(const ConstBufferSequence& buffers) { typename ConstBufferSequence::const_iterator iter = buffers.begin(); typename ConstBufferSequence::const_iterator end = buffers.end(); size_t total_buffer_size = 0; for (; iter != end; ++iter) { asio::const_buffer buffer(*iter); total_buffer_size += asio::buffer_size(buffer); } if (total_buffer_size == 0) return 0; if (storage_.size() == storage_.capacity()) flush(); return copy(buffers); } /// Write the given data to the stream. Returns the number of bytes written, /// or 0 if an error occurred and the error handler did not throw. template std::size_t write_some(const ConstBufferSequence& buffers, asio::error_code& ec) { ec = asio::error_code(); typename ConstBufferSequence::const_iterator iter = buffers.begin(); typename ConstBufferSequence::const_iterator end = buffers.end(); size_t total_buffer_size = 0; for (; iter != end; ++iter) { asio::const_buffer buffer(*iter); total_buffer_size += asio::buffer_size(buffer); } if (total_buffer_size == 0) return 0; if (storage_.size() == storage_.capacity() && !flush(ec)) return 0; return copy(buffers); } template class write_some_handler { public: write_some_handler(asio::io_service& io_service, detail::buffered_stream_storage& storage, const ConstBufferSequence& buffers, WriteHandler handler) : io_service_(io_service), storage_(storage), buffers_(buffers), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t) { if (ec) { std::size_t length = 0; io_service_.dispatch(detail::bind_handler(handler_, ec, length)); } else { using namespace std; // For memcpy. std::size_t orig_size = storage_.size(); std::size_t space_avail = storage_.capacity() - orig_size; std::size_t bytes_copied = 0; typename ConstBufferSequence::const_iterator iter = buffers_.begin(); typename ConstBufferSequence::const_iterator end = buffers_.end(); for (; iter != end && space_avail > 0; ++iter) { std::size_t bytes_avail = buffer_size(*iter); std::size_t length = (bytes_avail < space_avail) ? bytes_avail : space_avail; storage_.resize(orig_size + bytes_copied + length); memcpy(storage_.data() + orig_size + bytes_copied, buffer_cast(*iter), length); bytes_copied += length; space_avail -= length; } io_service_.dispatch(detail::bind_handler(handler_, ec, bytes_copied)); } } private: asio::io_service& io_service_; detail::buffered_stream_storage& storage_; ConstBufferSequence buffers_; WriteHandler handler_; }; /// Start an asynchronous write. The data being written must be valid for the /// lifetime of the asynchronous operation. template void async_write_some(const ConstBufferSequence& buffers, WriteHandler handler) { typename ConstBufferSequence::const_iterator iter = buffers.begin(); typename ConstBufferSequence::const_iterator end = buffers.end(); size_t total_buffer_size = 0; for (; iter != end; ++iter) { asio::const_buffer buffer(*iter); total_buffer_size += asio::buffer_size(buffer); } if (total_buffer_size == 0) { get_io_service().post(detail::bind_handler( handler, asio::error_code(), 0)); } else if (storage_.size() == storage_.capacity()) { async_flush(write_some_handler( get_io_service(), storage_, buffers, handler)); } else { std::size_t bytes_copied = copy(buffers); get_io_service().post(detail::bind_handler( handler, asio::error_code(), bytes_copied)); } } /// Read some data from the stream. Returns the number of bytes read. Throws /// an exception on failure. template std::size_t read_some(const MutableBufferSequence& buffers) { return next_layer_.read_some(buffers); } /// Read some data from the stream. Returns the number of bytes read or 0 if /// an error occurred. template std::size_t read_some(const MutableBufferSequence& buffers, asio::error_code& ec) { return next_layer_.read_some(buffers, ec); } /// Start an asynchronous read. The buffer into which the data will be read /// must be valid for the lifetime of the asynchronous operation. template void async_read_some(const MutableBufferSequence& buffers, ReadHandler handler) { next_layer_.async_read_some(buffers, handler); } /// Peek at the incoming data on the stream. Returns the number of bytes read. /// Throws an exception on failure. template std::size_t peek(const MutableBufferSequence& buffers) { return next_layer_.peek(buffers); } /// Peek at the incoming data on the stream. Returns the number of bytes read, /// or 0 if an error occurred. template std::size_t peek(const MutableBufferSequence& buffers, asio::error_code& ec) { return next_layer_.peek(buffers, ec); } /// Determine the amount of data that may be read without blocking. std::size_t in_avail() { return next_layer_.in_avail(); } /// Determine the amount of data that may be read without blocking. std::size_t in_avail(asio::error_code& ec) { return next_layer_.in_avail(ec); } private: /// Copy data into the internal buffer from the specified source buffer. /// Returns the number of bytes copied. template std::size_t copy(const ConstBufferSequence& buffers) { using namespace std; // For memcpy. std::size_t orig_size = storage_.size(); std::size_t space_avail = storage_.capacity() - orig_size; std::size_t bytes_copied = 0; typename ConstBufferSequence::const_iterator iter = buffers.begin(); typename ConstBufferSequence::const_iterator end = buffers.end(); for (; iter != end && space_avail > 0; ++iter) { std::size_t bytes_avail = buffer_size(*iter); std::size_t length = (bytes_avail < space_avail) ? bytes_avail : space_avail; storage_.resize(orig_size + bytes_copied + length); memcpy(storage_.data() + orig_size + bytes_copied, buffer_cast(*iter), length); bytes_copied += length; space_avail -= length; } return bytes_copied; } /// The next layer. Stream next_layer_; // The data in the buffer. detail::buffered_stream_storage storage_; }; } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_BUFFERED_WRITE_STREAM_HPP percona-xtradb-cluster-galera/asio/asio/buffered_write_stream_fwd.hpp0000644000000000000000000000121012247075736026452 0ustar rootroot00000000000000// // buffered_write_stream_fwd.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_BUFFERED_WRITE_STREAM_FWD_HPP #define ASIO_BUFFERED_WRITE_STREAM_FWD_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) namespace asio { template class buffered_write_stream; } // namespace asio #endif // ASIO_BUFFERED_WRITE_STREAM_FWD_HPP percona-xtradb-cluster-galera/asio/asio/buffers_iterator.hpp0000644000000000000000000002643212247075736024625 0ustar rootroot00000000000000// // buffers_iterator.hpp // ~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_BUFFERS_ITERATOR_HPP #define ASIO_BUFFERS_ITERATOR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include #include #include #include #include "asio/buffer.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template struct buffers_iterator_types_helper; template <> struct buffers_iterator_types_helper { typedef const_buffer buffer_type; template struct byte_type { typedef typename boost::add_const::type type; }; }; template <> struct buffers_iterator_types_helper { typedef mutable_buffer buffer_type; template struct byte_type { typedef ByteType type; }; }; template struct buffers_iterator_types { enum { is_mutable = boost::is_convertible< typename BufferSequence::value_type, mutable_buffer>::value }; typedef buffers_iterator_types_helper helper; typedef typename helper::buffer_type buffer_type; typedef typename helper::template byte_type::type byte_type; }; } /// A random access iterator over the bytes in a buffer sequence. template class buffers_iterator : public boost::iterator< std::random_access_iterator_tag, typename detail::buffers_iterator_types< BufferSequence, ByteType>::byte_type> { private: typedef typename detail::buffers_iterator_types< BufferSequence, ByteType>::buffer_type buffer_type; typedef typename detail::buffers_iterator_types< BufferSequence, ByteType>::byte_type byte_type; public: /// Default constructor. Creates an iterator in an undefined state. buffers_iterator() : current_buffer_(), current_buffer_position_(0), begin_(), current_(), end_(), position_(0) { } /// Construct an iterator representing the beginning of the buffers' data. static buffers_iterator begin(const BufferSequence& buffers) #if BOOST_WORKAROUND(__GNUC__, == 4) && BOOST_WORKAROUND(__GNUC_MINOR__, == 3) __attribute__ ((noinline)) #endif { buffers_iterator new_iter; new_iter.begin_ = buffers.begin(); new_iter.current_ = buffers.begin(); new_iter.end_ = buffers.end(); while (new_iter.current_ != new_iter.end_) { new_iter.current_buffer_ = *new_iter.current_; if (asio::buffer_size(new_iter.current_buffer_) > 0) break; ++new_iter.current_; } return new_iter; } /// Construct an iterator representing the end of the buffers' data. static buffers_iterator end(const BufferSequence& buffers) #if BOOST_WORKAROUND(__GNUC__, == 4) && BOOST_WORKAROUND(__GNUC_MINOR__, == 3) __attribute__ ((noinline)) #endif { buffers_iterator new_iter; new_iter.begin_ = buffers.begin(); new_iter.current_ = buffers.begin(); new_iter.end_ = buffers.end(); while (new_iter.current_ != new_iter.end_) { buffer_type buffer = *new_iter.current_; new_iter.position_ += asio::buffer_size(buffer); ++new_iter.current_; } return new_iter; } /// Dereference an iterator. byte_type& operator*() const { return dereference(); } /// Dereference an iterator. byte_type* operator->() const { return &dereference(); } /// Access an individual element. byte_type& operator[](std::ptrdiff_t difference) const { buffers_iterator tmp(*this); tmp.advance(difference); return *tmp; } /// Increment operator (prefix). buffers_iterator& operator++() { increment(); return *this; } /// Increment operator (postfix). buffers_iterator operator++(int) { buffers_iterator tmp(*this); ++*this; return tmp; } /// Decrement operator (prefix). buffers_iterator& operator--() { decrement(); return *this; } /// Decrement operator (postfix). buffers_iterator operator--(int) { buffers_iterator tmp(*this); --*this; return tmp; } /// Addition operator. buffers_iterator& operator+=(std::ptrdiff_t difference) { advance(difference); return *this; } /// Subtraction operator. buffers_iterator& operator-=(std::ptrdiff_t difference) { advance(-difference); return *this; } /// Addition operator. friend buffers_iterator operator+(const buffers_iterator& iter, std::ptrdiff_t difference) { buffers_iterator tmp(iter); tmp.advance(difference); return tmp; } /// Addition operator. friend buffers_iterator operator+(std::ptrdiff_t difference, const buffers_iterator& iter) { buffers_iterator tmp(iter); tmp.advance(difference); return tmp; } /// Subtraction operator. friend buffers_iterator operator-(const buffers_iterator& iter, std::ptrdiff_t difference) { buffers_iterator tmp(iter); tmp.advance(-difference); return tmp; } /// Subtraction operator. friend std::ptrdiff_t operator-(const buffers_iterator& a, const buffers_iterator& b) { return b.distance_to(a); } /// Test two iterators for equality. friend bool operator==(const buffers_iterator& a, const buffers_iterator& b) { return a.equal(b); } /// Test two iterators for inequality. friend bool operator!=(const buffers_iterator& a, const buffers_iterator& b) { return !a.equal(b); } /// Compare two iterators. friend bool operator<(const buffers_iterator& a, const buffers_iterator& b) { return a.distance_to(b) > 0; } /// Compare two iterators. friend bool operator<=(const buffers_iterator& a, const buffers_iterator& b) { return !(b < a); } /// Compare two iterators. friend bool operator>(const buffers_iterator& a, const buffers_iterator& b) { return b < a; } /// Compare two iterators. friend bool operator>=(const buffers_iterator& a, const buffers_iterator& b) { return !(a < b); } private: // Dereference the iterator. byte_type& dereference() const { return buffer_cast(current_buffer_)[current_buffer_position_]; } // Compare two iterators for equality. bool equal(const buffers_iterator& other) const { return position_ == other.position_; } // Increment the iterator. void increment() { BOOST_ASSERT(current_ != end_ && "iterator out of bounds"); ++position_; // Check if the increment can be satisfied by the current buffer. ++current_buffer_position_; if (current_buffer_position_ != asio::buffer_size(current_buffer_)) return; // Find the next non-empty buffer. ++current_; current_buffer_position_ = 0; while (current_ != end_) { current_buffer_ = *current_; if (asio::buffer_size(current_buffer_) > 0) return; ++current_; } } // Decrement the iterator. void decrement() { BOOST_ASSERT(position_ > 0 && "iterator out of bounds"); --position_; // Check if the decrement can be satisfied by the current buffer. if (current_buffer_position_ != 0) { --current_buffer_position_; return; } // Find the previous non-empty buffer. typename BufferSequence::const_iterator iter = current_; while (iter != begin_) { --iter; buffer_type buffer = *iter; std::size_t buffer_size = asio::buffer_size(buffer); if (buffer_size > 0) { current_ = iter; current_buffer_ = buffer; current_buffer_position_ = buffer_size - 1; return; } } } // Advance the iterator by the specified distance. void advance(std::ptrdiff_t n) { if (n > 0) { BOOST_ASSERT(current_ != end_ && "iterator out of bounds"); for (;;) { std::ptrdiff_t current_buffer_balance = asio::buffer_size(current_buffer_) - current_buffer_position_; // Check if the advance can be satisfied by the current buffer. if (current_buffer_balance > n) { position_ += n; current_buffer_position_ += n; return; } // Update position. n -= current_buffer_balance; position_ += current_buffer_balance; // Move to next buffer. If it is empty then it will be skipped on the // next iteration of this loop. if (++current_ == end_) { BOOST_ASSERT(n == 0 && "iterator out of bounds"); current_buffer_ = buffer_type(); current_buffer_position_ = 0; return; } current_buffer_ = *current_; current_buffer_position_ = 0; } } else if (n < 0) { std::size_t abs_n = -n; BOOST_ASSERT(position_ >= abs_n && "iterator out of bounds"); for (;;) { // Check if the advance can be satisfied by the current buffer. if (current_buffer_position_ >= abs_n) { position_ -= abs_n; current_buffer_position_ -= abs_n; return; } // Update position. abs_n -= current_buffer_position_; position_ -= current_buffer_position_; // Check if we've reached the beginning of the buffers. if (current_ == begin_) { BOOST_ASSERT(abs_n == 0 && "iterator out of bounds"); current_buffer_position_ = 0; return; } // Find the previous non-empty buffer. typename BufferSequence::const_iterator iter = current_; while (iter != begin_) { --iter; buffer_type buffer = *iter; std::size_t buffer_size = asio::buffer_size(buffer); if (buffer_size > 0) { current_ = iter; current_buffer_ = buffer; current_buffer_position_ = buffer_size; break; } } } } } // Determine the distance between two iterators. std::ptrdiff_t distance_to(const buffers_iterator& other) const { return other.position_ - position_; } buffer_type current_buffer_; std::size_t current_buffer_position_; typename BufferSequence::const_iterator begin_; typename BufferSequence::const_iterator current_; typename BufferSequence::const_iterator end_; std::size_t position_; }; /// Construct an iterator representing the beginning of the buffers' data. template inline buffers_iterator buffers_begin( const BufferSequence& buffers) { return buffers_iterator::begin(buffers); } /// Construct an iterator representing the end of the buffers' data. template inline buffers_iterator buffers_end( const BufferSequence& buffers) { return buffers_iterator::end(buffers); } } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_BUFFERS_ITERATOR_HPP percona-xtradb-cluster-galera/asio/asio/completion_condition.hpp0000644000000000000000000000754212247075736025500 0ustar rootroot00000000000000// // completion_condition.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_COMPLETION_CONDITION_HPP #define ASIO_COMPLETION_CONDITION_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/detail/push_options.hpp" namespace asio { namespace detail { // The default maximum number of bytes to transfer in a single operation. enum { default_max_transfer_size = 65536 }; // Adapt result of old-style completion conditions (which had a bool result // where true indicated that the operation was complete). inline std::size_t adapt_completion_condition_result(bool result) { return result ? 0 : default_max_transfer_size; } // Adapt result of current completion conditions (which have a size_t result // where 0 means the operation is complete, and otherwise the result is the // maximum number of bytes to transfer on the next underlying operation). inline std::size_t adapt_completion_condition_result(std::size_t result) { return result; } class transfer_all_t { public: typedef std::size_t result_type; template std::size_t operator()(const Error& err, std::size_t) { return !!err ? 0 : default_max_transfer_size; } }; class transfer_at_least_t { public: typedef std::size_t result_type; explicit transfer_at_least_t(std::size_t minimum) : minimum_(minimum) { } template std::size_t operator()(const Error& err, std::size_t bytes_transferred) { return (!!err || bytes_transferred >= minimum_) ? 0 : default_max_transfer_size; } private: std::size_t minimum_; }; } // namespace detail /** * @defgroup completion_condition Completion Condition Function Objects * * Function objects used for determining when a read or write operation should * complete. */ /*@{*/ /// Return a completion condition function object that indicates that a read or /// write operation should continue until all of the data has been transferred, /// or until an error occurs. /** * This function is used to create an object, of unspecified type, that meets * CompletionCondition requirements. * * @par Example * Reading until a buffer is full: * @code * boost::array buf; * asio::error_code ec; * std::size_t n = asio::read( * sock, asio::buffer(buf), * asio::transfer_all(), ec); * if (ec) * { * // An error occurred. * } * else * { * // n == 128 * } * @endcode */ #if defined(GENERATING_DOCUMENTATION) unspecified transfer_all(); #else inline detail::transfer_all_t transfer_all() { return detail::transfer_all_t(); } #endif /// Return a completion condition function object that indicates that a read or /// write operation should continue until a minimum number of bytes has been /// transferred, or until an error occurs. /** * This function is used to create an object, of unspecified type, that meets * CompletionCondition requirements. * * @par Example * Reading until a buffer is full or contains at least 64 bytes: * @code * boost::array buf; * asio::error_code ec; * std::size_t n = asio::read( * sock, asio::buffer(buf), * asio::transfer_at_least(64), ec); * if (ec) * { * // An error occurred. * } * else * { * // n >= 64 && n <= 128 * } * @endcode */ #if defined(GENERATING_DOCUMENTATION) unspecified transfer_at_least(std::size_t minimum); #else inline detail::transfer_at_least_t transfer_at_least(std::size_t minimum) { return detail::transfer_at_least_t(minimum); } #endif /*@}*/ } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_COMPLETION_CONDITION_HPP percona-xtradb-cluster-galera/asio/asio/datagram_socket_service.hpp0000644000000000000000000002246412247075736026131 0ustar rootroot00000000000000// // datagram_socket_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DATAGRAM_SOCKET_SERVICE_HPP #define ASIO_DATAGRAM_SOCKET_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/error.hpp" #include "asio/io_service.hpp" #if defined(ASIO_HAS_IOCP) # include "asio/detail/win_iocp_socket_service.hpp" #else # include "asio/detail/reactive_socket_service.hpp" #endif #include "asio/detail/push_options.hpp" namespace asio { /// Default service implementation for a datagram socket. template class datagram_socket_service #if defined(GENERATING_DOCUMENTATION) : public asio::io_service::service #else : public asio::detail::service_base > #endif { public: #if defined(GENERATING_DOCUMENTATION) /// The unique service identifier. static asio::io_service::id id; #endif /// The protocol type. typedef Protocol protocol_type; /// The endpoint type. typedef typename Protocol::endpoint endpoint_type; private: // The type of the platform-specific implementation. #if defined(ASIO_HAS_IOCP) typedef detail::win_iocp_socket_service service_impl_type; #else typedef detail::reactive_socket_service service_impl_type; #endif public: /// The type of a datagram socket. #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined implementation_type; #else typedef typename service_impl_type::implementation_type implementation_type; #endif /// The native socket type. #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined native_type; #else typedef typename service_impl_type::native_type native_type; #endif /// Construct a new datagram socket service for the specified io_service. explicit datagram_socket_service(asio::io_service& io_service) : asio::detail::service_base< datagram_socket_service >(io_service), service_impl_(io_service) { } /// Destroy all user-defined handler objects owned by the service. void shutdown_service() { service_impl_.shutdown_service(); } /// Construct a new datagram socket implementation. void construct(implementation_type& impl) { service_impl_.construct(impl); } /// Destroy a datagram socket implementation. void destroy(implementation_type& impl) { service_impl_.destroy(impl); } // Open a new datagram socket implementation. asio::error_code open(implementation_type& impl, const protocol_type& protocol, asio::error_code& ec) { if (protocol.type() == SOCK_DGRAM) service_impl_.open(impl, protocol, ec); else ec = asio::error::invalid_argument; return ec; } /// Assign an existing native socket to a datagram socket. asio::error_code assign(implementation_type& impl, const protocol_type& protocol, const native_type& native_socket, asio::error_code& ec) { return service_impl_.assign(impl, protocol, native_socket, ec); } /// Determine whether the socket is open. bool is_open(const implementation_type& impl) const { return service_impl_.is_open(impl); } /// Close a datagram socket implementation. asio::error_code close(implementation_type& impl, asio::error_code& ec) { return service_impl_.close(impl, ec); } /// Get the native socket implementation. native_type native(implementation_type& impl) { return service_impl_.native(impl); } /// Cancel all asynchronous operations associated with the socket. asio::error_code cancel(implementation_type& impl, asio::error_code& ec) { return service_impl_.cancel(impl, ec); } /// Determine whether the socket is at the out-of-band data mark. bool at_mark(const implementation_type& impl, asio::error_code& ec) const { return service_impl_.at_mark(impl, ec); } /// Determine the number of bytes available for reading. std::size_t available(const implementation_type& impl, asio::error_code& ec) const { return service_impl_.available(impl, ec); } // Bind the datagram socket to the specified local endpoint. asio::error_code bind(implementation_type& impl, const endpoint_type& endpoint, asio::error_code& ec) { return service_impl_.bind(impl, endpoint, ec); } /// Connect the datagram socket to the specified endpoint. asio::error_code connect(implementation_type& impl, const endpoint_type& peer_endpoint, asio::error_code& ec) { return service_impl_.connect(impl, peer_endpoint, ec); } /// Start an asynchronous connect. template void async_connect(implementation_type& impl, const endpoint_type& peer_endpoint, ConnectHandler handler) { service_impl_.async_connect(impl, peer_endpoint, handler); } /// Set a socket option. template asio::error_code set_option(implementation_type& impl, const SettableSocketOption& option, asio::error_code& ec) { return service_impl_.set_option(impl, option, ec); } /// Get a socket option. template asio::error_code get_option(const implementation_type& impl, GettableSocketOption& option, asio::error_code& ec) const { return service_impl_.get_option(impl, option, ec); } /// Perform an IO control command on the socket. template asio::error_code io_control(implementation_type& impl, IoControlCommand& command, asio::error_code& ec) { return service_impl_.io_control(impl, command, ec); } /// Get the local endpoint. endpoint_type local_endpoint(const implementation_type& impl, asio::error_code& ec) const { return service_impl_.local_endpoint(impl, ec); } /// Get the remote endpoint. endpoint_type remote_endpoint(const implementation_type& impl, asio::error_code& ec) const { return service_impl_.remote_endpoint(impl, ec); } /// Disable sends or receives on the socket. asio::error_code shutdown(implementation_type& impl, socket_base::shutdown_type what, asio::error_code& ec) { return service_impl_.shutdown(impl, what, ec); } /// Send the given data to the peer. template std::size_t send(implementation_type& impl, const ConstBufferSequence& buffers, socket_base::message_flags flags, asio::error_code& ec) { return service_impl_.send(impl, buffers, flags, ec); } /// Start an asynchronous send. template void async_send(implementation_type& impl, const ConstBufferSequence& buffers, socket_base::message_flags flags, WriteHandler handler) { service_impl_.async_send(impl, buffers, flags, handler); } /// Send a datagram to the specified endpoint. template std::size_t send_to(implementation_type& impl, const ConstBufferSequence& buffers, const endpoint_type& destination, socket_base::message_flags flags, asio::error_code& ec) { return service_impl_.send_to(impl, buffers, destination, flags, ec); } /// Start an asynchronous send. template void async_send_to(implementation_type& impl, const ConstBufferSequence& buffers, const endpoint_type& destination, socket_base::message_flags flags, WriteHandler handler) { service_impl_.async_send_to(impl, buffers, destination, flags, handler); } /// Receive some data from the peer. template std::size_t receive(implementation_type& impl, const MutableBufferSequence& buffers, socket_base::message_flags flags, asio::error_code& ec) { return service_impl_.receive(impl, buffers, flags, ec); } /// Start an asynchronous receive. template void async_receive(implementation_type& impl, const MutableBufferSequence& buffers, socket_base::message_flags flags, ReadHandler handler) { service_impl_.async_receive(impl, buffers, flags, handler); } /// Receive a datagram with the endpoint of the sender. template std::size_t receive_from(implementation_type& impl, const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, socket_base::message_flags flags, asio::error_code& ec) { return service_impl_.receive_from(impl, buffers, sender_endpoint, flags, ec); } /// Start an asynchronous receive that will get the endpoint of the sender. template void async_receive_from(implementation_type& impl, const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, socket_base::message_flags flags, ReadHandler handler) { service_impl_.async_receive_from(impl, buffers, sender_endpoint, flags, handler); } private: // The platform-specific implementation. service_impl_type service_impl_; }; } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DATAGRAM_SOCKET_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/deadline_timer.hpp0000644000000000000000000000167512247075736024227 0ustar rootroot00000000000000// // deadline_timer.hpp // ~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DEADLINE_TIMER_HPP #define ASIO_DEADLINE_TIMER_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/detail/socket_types.hpp" // Must come before posix_time. #include "asio/basic_deadline_timer.hpp" #include "asio/detail/push_options.hpp" #include #include "asio/detail/pop_options.hpp" namespace asio { /// Typedef for the typical usage of timer. Uses a UTC clock. typedef basic_deadline_timer deadline_timer; } // namespace asio #endif // ASIO_DEADLINE_TIMER_HPP percona-xtradb-cluster-galera/asio/asio/deadline_timer_service.hpp0000644000000000000000000000773212247075736025747 0ustar rootroot00000000000000// // deadline_timer_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DEADLINE_TIMER_SERVICE_HPP #define ASIO_DEADLINE_TIMER_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/detail/deadline_timer_service.hpp" #include "asio/io_service.hpp" #include "asio/time_traits.hpp" #include "asio/detail/push_options.hpp" namespace asio { /// Default service implementation for a timer. template > class deadline_timer_service #if defined(GENERATING_DOCUMENTATION) : public asio::io_service::service #else : public asio::detail::service_base< deadline_timer_service > #endif { public: #if defined(GENERATING_DOCUMENTATION) /// The unique service identifier. static asio::io_service::id id; #endif /// The time traits type. typedef TimeTraits traits_type; /// The time type. typedef typename traits_type::time_type time_type; /// The duration type. typedef typename traits_type::duration_type duration_type; private: // The type of the platform-specific implementation. typedef detail::deadline_timer_service service_impl_type; public: /// The implementation type of the deadline timer. #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined implementation_type; #else typedef typename service_impl_type::implementation_type implementation_type; #endif /// Construct a new timer service for the specified io_service. explicit deadline_timer_service(asio::io_service& io_service) : asio::detail::service_base< deadline_timer_service >(io_service), service_impl_(io_service) { } /// Destroy all user-defined handler objects owned by the service. void shutdown_service() { service_impl_.shutdown_service(); } /// Construct a new timer implementation. void construct(implementation_type& impl) { service_impl_.construct(impl); } /// Destroy a timer implementation. void destroy(implementation_type& impl) { service_impl_.destroy(impl); } /// Cancel any asynchronous wait operations associated with the timer. std::size_t cancel(implementation_type& impl, asio::error_code& ec) { return service_impl_.cancel(impl, ec); } /// Get the expiry time for the timer as an absolute time. time_type expires_at(const implementation_type& impl) const { return service_impl_.expires_at(impl); } /// Set the expiry time for the timer as an absolute time. std::size_t expires_at(implementation_type& impl, const time_type& expiry_time, asio::error_code& ec) { return service_impl_.expires_at(impl, expiry_time, ec); } /// Get the expiry time for the timer relative to now. duration_type expires_from_now(const implementation_type& impl) const { return service_impl_.expires_from_now(impl); } /// Set the expiry time for the timer relative to now. std::size_t expires_from_now(implementation_type& impl, const duration_type& expiry_time, asio::error_code& ec) { return service_impl_.expires_from_now(impl, expiry_time, ec); } // Perform a blocking wait on the timer. void wait(implementation_type& impl, asio::error_code& ec) { service_impl_.wait(impl, ec); } // Start an asynchronous wait on the timer. template void async_wait(implementation_type& impl, WaitHandler handler) { service_impl_.async_wait(impl, handler); } private: // The platform-specific implementation. service_impl_type service_impl_; }; } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DEADLINE_TIMER_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/detail/0000755000000000000000000000000012247075736022002 5ustar rootroot00000000000000percona-xtradb-cluster-galera/asio/asio/error.hpp0000644000000000000000000001555012247075736022410 0ustar rootroot00000000000000// // error.hpp // ~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_ERROR_HPP #define ASIO_ERROR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) # include #else # include # include #endif #if defined(GENERATING_DOCUMENTATION) /// INTERNAL ONLY. # define ASIO_NATIVE_ERROR(e) implementation_defined /// INTERNAL ONLY. # define ASIO_SOCKET_ERROR(e) implementation_defined /// INTERNAL ONLY. # define ASIO_NETDB_ERROR(e) implementation_defined /// INTERNAL ONLY. # define ASIO_GETADDRINFO_ERROR(e) implementation_defined /// INTERNAL ONLY. # define ASIO_WIN_OR_POSIX(e_win, e_posix) implementation_defined #elif defined(BOOST_WINDOWS) || defined(__CYGWIN__) # define ASIO_NATIVE_ERROR(e) e # define ASIO_SOCKET_ERROR(e) WSA ## e # define ASIO_NETDB_ERROR(e) WSA ## e # define ASIO_GETADDRINFO_ERROR(e) WSA ## e # define ASIO_WIN_OR_POSIX(e_win, e_posix) e_win #else # define ASIO_NATIVE_ERROR(e) e # define ASIO_SOCKET_ERROR(e) e # define ASIO_NETDB_ERROR(e) e # define ASIO_GETADDRINFO_ERROR(e) e # define ASIO_WIN_OR_POSIX(e_win, e_posix) e_posix #endif #include "asio/detail/push_options.hpp" namespace asio { namespace error { enum basic_errors { /// Permission denied. access_denied = ASIO_SOCKET_ERROR(EACCES), /// Address family not supported by protocol. address_family_not_supported = ASIO_SOCKET_ERROR(EAFNOSUPPORT), /// Address already in use. address_in_use = ASIO_SOCKET_ERROR(EADDRINUSE), /// Transport endpoint is already connected. already_connected = ASIO_SOCKET_ERROR(EISCONN), /// Operation already in progress. already_started = ASIO_SOCKET_ERROR(EALREADY), /// Broken pipe. broken_pipe = ASIO_WIN_OR_POSIX( ASIO_NATIVE_ERROR(ERROR_BROKEN_PIPE), ASIO_NATIVE_ERROR(EPIPE)), /// A connection has been aborted. connection_aborted = ASIO_SOCKET_ERROR(ECONNABORTED), /// Connection refused. connection_refused = ASIO_SOCKET_ERROR(ECONNREFUSED), /// Connection reset by peer. connection_reset = ASIO_SOCKET_ERROR(ECONNRESET), /// Bad file descriptor. bad_descriptor = ASIO_SOCKET_ERROR(EBADF), /// Bad address. fault = ASIO_SOCKET_ERROR(EFAULT), /// No route to host. host_unreachable = ASIO_SOCKET_ERROR(EHOSTUNREACH), /// Operation now in progress. in_progress = ASIO_SOCKET_ERROR(EINPROGRESS), /// Interrupted system call. interrupted = ASIO_SOCKET_ERROR(EINTR), /// Invalid argument. invalid_argument = ASIO_SOCKET_ERROR(EINVAL), /// Message too long. message_size = ASIO_SOCKET_ERROR(EMSGSIZE), /// The name was too long. name_too_long = ASIO_SOCKET_ERROR(ENAMETOOLONG), /// Network is down. network_down = ASIO_SOCKET_ERROR(ENETDOWN), /// Network dropped connection on reset. network_reset = ASIO_SOCKET_ERROR(ENETRESET), /// Network is unreachable. network_unreachable = ASIO_SOCKET_ERROR(ENETUNREACH), /// Too many open files. no_descriptors = ASIO_SOCKET_ERROR(EMFILE), /// No buffer space available. no_buffer_space = ASIO_SOCKET_ERROR(ENOBUFS), /// Cannot allocate memory. no_memory = ASIO_WIN_OR_POSIX( ASIO_NATIVE_ERROR(ERROR_OUTOFMEMORY), ASIO_NATIVE_ERROR(ENOMEM)), /// Operation not permitted. no_permission = ASIO_WIN_OR_POSIX( ASIO_NATIVE_ERROR(ERROR_ACCESS_DENIED), ASIO_NATIVE_ERROR(EPERM)), /// Protocol not available. no_protocol_option = ASIO_SOCKET_ERROR(ENOPROTOOPT), /// Transport endpoint is not connected. not_connected = ASIO_SOCKET_ERROR(ENOTCONN), /// Socket operation on non-socket. not_socket = ASIO_SOCKET_ERROR(ENOTSOCK), /// Operation cancelled. operation_aborted = ASIO_WIN_OR_POSIX( ASIO_NATIVE_ERROR(ERROR_OPERATION_ABORTED), ASIO_NATIVE_ERROR(ECANCELED)), /// Operation not supported. operation_not_supported = ASIO_SOCKET_ERROR(EOPNOTSUPP), /// Cannot send after transport endpoint shutdown. shut_down = ASIO_SOCKET_ERROR(ESHUTDOWN), /// Connection timed out. timed_out = ASIO_SOCKET_ERROR(ETIMEDOUT), /// Resource temporarily unavailable. try_again = ASIO_WIN_OR_POSIX( ASIO_NATIVE_ERROR(ERROR_RETRY), ASIO_NATIVE_ERROR(EAGAIN)), /// The socket is marked non-blocking and the requested operation would block. would_block = ASIO_SOCKET_ERROR(EWOULDBLOCK) }; enum netdb_errors { /// Host not found (authoritative). host_not_found = ASIO_NETDB_ERROR(HOST_NOT_FOUND), /// Host not found (non-authoritative). host_not_found_try_again = ASIO_NETDB_ERROR(TRY_AGAIN), /// The query is valid but does not have associated address data. no_data = ASIO_NETDB_ERROR(NO_DATA), /// A non-recoverable error occurred. no_recovery = ASIO_NETDB_ERROR(NO_RECOVERY) }; enum addrinfo_errors { /// The service is not supported for the given socket type. service_not_found = ASIO_WIN_OR_POSIX( ASIO_NATIVE_ERROR(WSATYPE_NOT_FOUND), ASIO_GETADDRINFO_ERROR(EAI_SERVICE)), /// The socket type is not supported. socket_type_not_supported = ASIO_WIN_OR_POSIX( ASIO_NATIVE_ERROR(WSAESOCKTNOSUPPORT), ASIO_GETADDRINFO_ERROR(EAI_SOCKTYPE)) }; enum misc_errors { /// Already open. already_open = 1, /// End of file or stream. eof, /// Element not found. not_found, /// The descriptor cannot fit into the select system call's fd_set. fd_set_failure }; enum ssl_errors { }; // boostify: error category definitions start here. } // namespace error } // namespace asio #include "asio/detail/pop_options.hpp" #include "asio/error_code.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace error { // boostify: error category definitions end here. inline asio::error_code make_error_code(basic_errors e) { return asio::error_code( static_cast(e), get_system_category()); } inline asio::error_code make_error_code(netdb_errors e) { return asio::error_code( static_cast(e), get_netdb_category()); } inline asio::error_code make_error_code(addrinfo_errors e) { return asio::error_code( static_cast(e), get_addrinfo_category()); } inline asio::error_code make_error_code(misc_errors e) { return asio::error_code( static_cast(e), get_misc_category()); } inline asio::error_code make_error_code(ssl_errors e) { return asio::error_code( static_cast(e), get_ssl_category()); } } // namespace error } // namespace asio #include "asio/detail/pop_options.hpp" #undef ASIO_NATIVE_ERROR #undef ASIO_SOCKET_ERROR #undef ASIO_NETDB_ERROR #undef ASIO_GETADDRINFO_ERROR #undef ASIO_WIN_OR_POSIX #if defined(ASIO_HEADER_ONLY) # include "asio/impl/error.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // ASIO_ERROR_HPP percona-xtradb-cluster-galera/asio/asio/error_code.hpp0000644000000000000000000000757312247075736023410 0ustar rootroot00000000000000// // error_code.hpp // ~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_ERROR_CODE_HPP #define ASIO_ERROR_CODE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #if defined(GENERATING_DOCUMENTATION) # define ASIO_WIN_OR_POSIX(e_win, e_posix) implementation_defined #elif defined(BOOST_WINDOWS) || defined(__CYGWIN__) # define ASIO_WIN_OR_POSIX(e_win, e_posix) e_win #else # define ASIO_WIN_OR_POSIX(e_win, e_posix) e_posix #endif #include "asio/detail/push_options.hpp" namespace asio { namespace error { /// Available error code categories. enum error_category { /// System error codes. system_category = ASIO_WIN_OR_POSIX(0, 0), /// Error codes from NetDB functions. netdb_category = ASIO_WIN_OR_POSIX(system_category, 1), /// Error codes from getaddrinfo. addrinfo_category = ASIO_WIN_OR_POSIX(system_category, 2), /// Miscellaneous error codes. misc_category = ASIO_WIN_OR_POSIX(3, 3), /// SSL error codes. ssl_category = ASIO_WIN_OR_POSIX(4, 4) }; // Category getters. inline error_category get_system_category() { return system_category; } inline error_category get_netdb_category() { return netdb_category; } inline error_category get_addrinfo_category() { return addrinfo_category; } inline error_category get_misc_category() { return misc_category; } inline error_category get_ssl_category() { return ssl_category; } } // namespace error /// Bring error category type into the asio namespace. typedef asio::error::error_category error_category; /// Class to represent an error code value. class error_code { public: /// The underlying representation of an error code. typedef int value_type; /// Default constructor. error_code() : value_(0), category_(error::system_category) { } /// Construct with specific error code and category. error_code(value_type v, error_category c) : value_(v), category_(c) { } /// Construct from an error code enum. template error_code(ErrorEnum e) { *this = make_error_code(e); } /// Get the error value. value_type value() const { return value_; } /// Get the error category. error_category category() const { return category_; } /// Get the message associated with the error. ASIO_DECL std::string message() const; struct unspecified_bool_type_t { }; typedef void (*unspecified_bool_type)(unspecified_bool_type_t); static void unspecified_bool_true(unspecified_bool_type_t) {} /// Operator returns non-null if there is a non-success error code. operator unspecified_bool_type() const { if (value_ == 0) return 0; else return &error_code::unspecified_bool_true; } /// Operator to test if the error represents success. bool operator!() const { return value_ == 0; } /// Equality operator to compare two error objects. friend bool operator==(const error_code& e1, const error_code& e2) { return e1.value_ == e2.value_ && e1.category_ == e2.category_; } /// Inequality operator to compare two error objects. friend bool operator!=(const error_code& e1, const error_code& e2) { return e1.value_ != e2.value_ || e1.category_ != e2.category_; } private: // The value associated with the error code. value_type value_; // The category associated with the error code. error_category category_; }; } // namespace asio #include "asio/detail/pop_options.hpp" #undef ASIO_WIN_OR_POSIX #if defined(ASIO_HEADER_ONLY) # include "asio/impl/error_code.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // ASIO_ERROR_CODE_HPP percona-xtradb-cluster-galera/asio/asio/handler_alloc_hook.hpp0000644000000000000000000000441312247075736025062 0ustar rootroot00000000000000// // handler_alloc_hook.hpp // ~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_HANDLER_ALLOC_HOOK_HPP #define ASIO_HANDLER_ALLOC_HOOK_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/detail/push_options.hpp" namespace asio { /// Default allocation function for handlers. /** * Asynchronous operations may need to allocate temporary objects. Since * asynchronous operations have a handler function object, these temporary * objects can be said to be associated with the handler. * * Implement asio_handler_allocate and asio_handler_deallocate for your own * handlers to provide custom allocation for these temporary objects. * * This default implementation is simply: * @code * return ::operator new(size); * @endcode * * @note All temporary objects associated with a handler will be deallocated * before the upcall to the handler is performed. This allows the same memory to * be reused for a subsequent asynchronous operation initiated by the handler. * * @par Example * @code * class my_handler; * * void* asio_handler_allocate(std::size_t size, my_handler* context) * { * return ::operator new(size); * } * * void asio_handler_deallocate(void* pointer, std::size_t size, * my_handler* context) * { * ::operator delete(pointer); * } * @endcode */ inline void* asio_handler_allocate(std::size_t size, ...) { return ::operator new(size); } /// Default deallocation function for handlers. /** * Implement asio_handler_allocate and asio_handler_deallocate for your own * handlers to provide custom allocation for the associated temporary objects. * * This default implementation is simply: * @code * ::operator delete(pointer); * @endcode * * @sa asio_handler_allocate. */ inline void asio_handler_deallocate(void* pointer, std::size_t size, ...) { (void)(size); ::operator delete(pointer); } } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_HANDLER_ALLOC_HOOK_HPP percona-xtradb-cluster-galera/asio/asio/handler_invoke_hook.hpp0000644000000000000000000000416012247075736025262 0ustar rootroot00000000000000// // handler_invoke_hook.hpp // ~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_HANDLER_INVOKE_HOOK_HPP #define ASIO_HANDLER_INVOKE_HOOK_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/detail/push_options.hpp" namespace asio { /// Default invoke function for handlers. /** * Completion handlers for asynchronous operations are invoked by the * io_service associated with the corresponding object (e.g. a socket or * deadline_timer). Certain guarantees are made on when the handler may be * invoked, in particular that a handler can only be invoked from a thread that * is currently calling @c run() on the corresponding io_service object. * Handlers may subsequently be invoked through other objects (such as * io_service::strand objects) that provide additional guarantees. * * When asynchronous operations are composed from other asynchronous * operations, all intermediate handlers should be invoked using the same * method as the final handler. This is required to ensure that user-defined * objects are not accessed in a way that may violate the guarantees. This * hooking function ensures that the invoked method used for the final handler * is accessible at each intermediate step. * * Implement asio_handler_invoke for your own handlers to specify a custom * invocation strategy. * * This default implementation is simply: * @code * function(); * @endcode * * @par Example * @code * class my_handler; * * template * void asio_handler_invoke(Function function, my_handler* context) * { * context->strand_.dispatch(function); * } * @endcode */ template inline void asio_handler_invoke(Function function, ...) { function(); } } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_HANDLER_INVOKE_HOOK_HPP percona-xtradb-cluster-galera/asio/asio/impl/0000755000000000000000000000000012247075736021501 5ustar rootroot00000000000000percona-xtradb-cluster-galera/asio/asio/io_service.hpp0000644000000000000000000005670612247075736023416 0ustar rootroot00000000000000// // io_service.hpp // ~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IO_SERVICE_HPP #define ASIO_IO_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include #include "asio/detail/noncopyable.hpp" #include "asio/detail/service_registry_fwd.hpp" #include "asio/detail/wrapped_handler.hpp" #include "asio/error_code.hpp" #if defined(ASIO_HAS_IOCP) # include "asio/detail/win_iocp_io_service_fwd.hpp" #else # include "asio/detail/task_io_service_fwd.hpp" #endif #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) # include "asio/detail/winsock_init.hpp" #elif defined(__sun) || defined(__QNX__) || defined(__hpux) || defined(_AIX) \ || defined(__osf__) # include "asio/detail/signal_init.hpp" #endif #include "asio/detail/push_options.hpp" namespace asio { class io_service; template Service& use_service(io_service& ios); template void add_service(io_service& ios, Service* svc); template bool has_service(io_service& ios); #if defined(ASIO_HAS_IOCP) namespace detail { typedef win_iocp_io_service io_service_impl; } #else namespace detail { typedef task_io_service io_service_impl; } #endif /// Provides core I/O functionality. /** * The io_service class provides the core I/O functionality for users of the * asynchronous I/O objects, including: * * @li asio::ip::tcp::socket * @li asio::ip::tcp::acceptor * @li asio::ip::udp::socket * @li asio::deadline_timer. * * The io_service class also includes facilities intended for developers of * custom asynchronous services. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Safe, with the exception that calling reset() while * there are unfinished run(), run_one(), poll() or poll_one() calls results in * undefined behaviour. * * @par Concepts: * Dispatcher. * * @par Synchronous and asynchronous operations * * Synchronous operations on I/O objects implicitly run the io_service object * for an individual operation. The io_service functions run(), run_one(), * poll() or poll_one() must be called for the io_service to perform * asynchronous operations on behalf of a C++ program. Notification that an * asynchronous operation has completed is delivered by invocation of the * associated handler. Handlers are invoked only by a thread that is currently * calling any overload of run(), run_one(), poll() or poll_one() for the * io_service. * * @par Effect of exceptions thrown from handlers * * If an exception is thrown from a handler, the exception is allowed to * propagate through the throwing thread's invocation of run(), run_one(), * poll() or poll_one(). No other threads that are calling any of these * functions are affected. It is then the responsibility of the application to * catch the exception. * * After the exception has been caught, the run(), run_one(), poll() or * poll_one() call may be restarted @em without the need for an intervening * call to reset(). This allows the thread to rejoin the io_service object's * thread pool without impacting any other threads in the pool. * * For example: * * @code * asio::io_service io_service; * ... * for (;;) * { * try * { * io_service.run(); * break; // run() exited normally * } * catch (my_exception& e) * { * // Deal with exception as appropriate. * } * } * @endcode * * @par Stopping the io_service from running out of work * * Some applications may need to prevent an io_service object's run() call from * returning when there is no more work to do. For example, the io_service may * be being run in a background thread that is launched prior to the * application's asynchronous operations. The run() call may be kept running by * creating an object of type asio::io_service::work: * * @code asio::io_service io_service; * asio::io_service::work work(io_service); * ... @endcode * * To effect a shutdown, the application will then need to call the io_service * object's stop() member function. This will cause the io_service run() call * to return as soon as possible, abandoning unfinished operations and without * permitting ready handlers to be dispatched. * * Alternatively, if the application requires that all operations and handlers * be allowed to finish normally, the work object may be explicitly destroyed. * * @code asio::io_service io_service; * auto_ptr work( * new asio::io_service::work(io_service)); * ... * work.reset(); // Allow run() to exit. @endcode * * @par The io_service class and I/O services * * Class io_service implements an extensible, type-safe, polymorphic set of I/O * services, indexed by service type. An object of class io_service must be * initialised before I/O objects such as sockets, resolvers and timers can be * used. These I/O objects are distinguished by having constructors that accept * an @c io_service& parameter. * * I/O services exist to manage the logical interface to the operating system on * behalf of the I/O objects. In particular, there are resources that are shared * across a class of I/O objects. For example, timers may be implemented in * terms of a single timer queue. The I/O services manage these shared * resources. * * Access to the services of an io_service is via three function templates, * use_service(), add_service() and has_service(). * * In a call to @c use_service(), the type argument chooses a service, * making available all members of the named type. If @c Service is not present * in an io_service, an object of type @c Service is created and added to the * io_service. A C++ program can check if an io_service implements a * particular service with the function template @c has_service(). * * Service objects may be explicitly added to an io_service using the function * template @c add_service(). If the @c Service is already present, the * service_already_exists exception is thrown. If the owner of the service is * not the same object as the io_service parameter, the invalid_service_owner * exception is thrown. * * Once a service reference is obtained from an io_service object by calling * use_service(), that reference remains usable as long as the owning io_service * object exists. * * All I/O service implementations have io_service::service as a public base * class. Custom I/O services may be implemented by deriving from this class and * then added to an io_service using the facilities described above. */ class io_service : private noncopyable { private: typedef detail::io_service_impl impl_type; #if defined(ASIO_HAS_IOCP) friend class detail::win_iocp_overlapped_ptr; #endif public: class work; friend class work; class id; class service; class strand; /// Constructor. ASIO_DECL io_service(); /// Constructor. /** * Construct with a hint about the required level of concurrency. * * @param concurrency_hint A suggestion to the implementation on how many * threads it should allow to run simultaneously. */ ASIO_DECL explicit io_service(std::size_t concurrency_hint); /// Destructor. /** * On destruction, the io_service performs the following sequence of * operations: * * @li For each service object @c svc in the io_service set, in reverse order * of the beginning of service object lifetime, performs * @c svc->shutdown_service(). * * @li Uninvoked handler objects that were scheduled for deferred invocation * on the io_service, or any associated strand, are destroyed. * * @li For each service object @c svc in the io_service set, in reverse order * of the beginning of service object lifetime, performs * delete static_cast(svc). * * @note The destruction sequence described above permits programs to * simplify their resource management by using @c shared_ptr<>. Where an * object's lifetime is tied to the lifetime of a connection (or some other * sequence of asynchronous operations), a @c shared_ptr to the object would * be bound into the handlers for all asynchronous operations associated with * it. This works as follows: * * @li When a single connection ends, all associated asynchronous operations * complete. The corresponding handler objects are destroyed, and all * @c shared_ptr references to the objects are destroyed. * * @li To shut down the whole program, the io_service function stop() is * called to terminate any run() calls as soon as possible. The io_service * destructor defined above destroys all handlers, causing all @c shared_ptr * references to all connection objects to be destroyed. */ ASIO_DECL ~io_service(); /// Run the io_service object's event processing loop. /** * The run() function blocks until all work has finished and there are no * more handlers to be dispatched, or until the io_service has been stopped. * * Multiple threads may call the run() function to set up a pool of threads * from which the io_service may execute handlers. All threads that are * waiting in the pool are equivalent and the io_service may choose any one * of them to invoke a handler. * * The run() function may be safely called again once it has completed only * after a call to reset(). * * @return The number of handlers that were executed. * * @throws asio::system_error Thrown on failure. * * @note The run() function must not be called from a thread that is currently * calling one of run(), run_one(), poll() or poll_one() on the same * io_service object. * * The poll() function may also be used to dispatch ready handlers, but * without blocking. */ ASIO_DECL std::size_t run(); /// Run the io_service object's event processing loop. /** * The run() function blocks until all work has finished and there are no * more handlers to be dispatched, or until the io_service has been stopped. * * Multiple threads may call the run() function to set up a pool of threads * from which the io_service may execute handlers. All threads that are * waiting in the pool are equivalent and the io_service may choose any one * of them to invoke a handler. * * The run() function may be safely called again once it has completed only * after a call to reset(). * * @param ec Set to indicate what error occurred, if any. * * @return The number of handlers that were executed. * * @note The run() function must not be called from a thread that is currently * calling one of run(), run_one(), poll() or poll_one() on the same * io_service object. * * The poll() function may also be used to dispatch ready handlers, but * without blocking. */ ASIO_DECL std::size_t run(asio::error_code& ec); /// Run the io_service object's event processing loop to execute at most one /// handler. /** * The run_one() function blocks until one handler has been dispatched, or * until the io_service has been stopped. * * @return The number of handlers that were executed. * * @throws asio::system_error Thrown on failure. */ ASIO_DECL std::size_t run_one(); /// Run the io_service object's event processing loop to execute at most one /// handler. /** * The run_one() function blocks until one handler has been dispatched, or * until the io_service has been stopped. * * @param ec Set to indicate what error occurred, if any. * * @return The number of handlers that were executed. */ ASIO_DECL std::size_t run_one(asio::error_code& ec); /// Run the io_service object's event processing loop to execute ready /// handlers. /** * The poll() function runs handlers that are ready to run, without blocking, * until the io_service has been stopped or there are no more ready handlers. * * @return The number of handlers that were executed. * * @throws asio::system_error Thrown on failure. */ ASIO_DECL std::size_t poll(); /// Run the io_service object's event processing loop to execute ready /// handlers. /** * The poll() function runs handlers that are ready to run, without blocking, * until the io_service has been stopped or there are no more ready handlers. * * @param ec Set to indicate what error occurred, if any. * * @return The number of handlers that were executed. */ ASIO_DECL std::size_t poll(asio::error_code& ec); /// Run the io_service object's event processing loop to execute one ready /// handler. /** * The poll_one() function runs at most one handler that is ready to run, * without blocking. * * @return The number of handlers that were executed. * * @throws asio::system_error Thrown on failure. */ ASIO_DECL std::size_t poll_one(); /// Run the io_service object's event processing loop to execute one ready /// handler. /** * The poll_one() function runs at most one handler that is ready to run, * without blocking. * * @param ec Set to indicate what error occurred, if any. * * @return The number of handlers that were executed. */ ASIO_DECL std::size_t poll_one(asio::error_code& ec); /// Stop the io_service object's event processing loop. /** * This function does not block, but instead simply signals the io_service to * stop. All invocations of its run() or run_one() member functions should * return as soon as possible. Subsequent calls to run(), run_one(), poll() * or poll_one() will return immediately until reset() is called. */ ASIO_DECL void stop(); /// Reset the io_service in preparation for a subsequent run() invocation. /** * This function must be called prior to any second or later set of * invocations of the run(), run_one(), poll() or poll_one() functions when a * previous invocation of these functions returned due to the io_service * being stopped or running out of work. This function allows the io_service * to reset any internal state, such as a "stopped" flag. * * This function must not be called while there are any unfinished calls to * the run(), run_one(), poll() or poll_one() functions. */ ASIO_DECL void reset(); /// Request the io_service to invoke the given handler. /** * This function is used to ask the io_service to execute the given handler. * * The io_service guarantees that the handler will only be called in a thread * in which the run(), run_one(), poll() or poll_one() member functions is * currently being invoked. The handler may be executed inside this function * if the guarantee can be met. * * @param handler The handler to be called. The io_service will make * a copy of the handler object as required. The function signature of the * handler must be: @code void handler(); @endcode * * @note This function throws an exception only if: * * @li the handler's @c asio_handler_allocate function; or * * @li the handler's copy constructor * * throws an exception. */ template void dispatch(CompletionHandler handler); /// Request the io_service to invoke the given handler and return immediately. /** * This function is used to ask the io_service to execute the given handler, * but without allowing the io_service to call the handler from inside this * function. * * The io_service guarantees that the handler will only be called in a thread * in which the run(), run_one(), poll() or poll_one() member functions is * currently being invoked. * * @param handler The handler to be called. The io_service will make * a copy of the handler object as required. The function signature of the * handler must be: @code void handler(); @endcode * * @note This function throws an exception only if: * * @li the handler's @c asio_handler_allocate function; or * * @li the handler's copy constructor * * throws an exception. */ template void post(CompletionHandler handler); /// Create a new handler that automatically dispatches the wrapped handler /// on the io_service. /** * This function is used to create a new handler function object that, when * invoked, will automatically pass the wrapped handler to the io_service * object's dispatch function. * * @param handler The handler to be wrapped. The io_service will make a copy * of the handler object as required. The function signature of the handler * must be: @code void handler(A1 a1, ... An an); @endcode * * @return A function object that, when invoked, passes the wrapped handler to * the io_service object's dispatch function. Given a function object with the * signature: * @code R f(A1 a1, ... An an); @endcode * If this function object is passed to the wrap function like so: * @code io_service.wrap(f); @endcode * then the return value is a function object with the signature * @code void g(A1 a1, ... An an); @endcode * that, when invoked, executes code equivalent to: * @code io_service.dispatch(boost::bind(f, a1, ... an)); @endcode */ template #if defined(GENERATING_DOCUMENTATION) unspecified #else detail::wrapped_handler #endif wrap(Handler handler); /// Obtain the service object corresponding to the given type. /** * This function is used to locate a service object that corresponds to * the given service type. If there is no existing implementation of the * service, then the io_service will create a new instance of the service. * * @param ios The io_service object that owns the service. * * @return The service interface implementing the specified service type. * Ownership of the service interface is not transferred to the caller. */ template friend Service& use_service(io_service& ios); /// Add a service object to the io_service. /** * This function is used to add a service to the io_service. * * @param ios The io_service object that owns the service. * * @param svc The service object. On success, ownership of the service object * is transferred to the io_service. When the io_service object is destroyed, * it will destroy the service object by performing: * @code delete static_cast(svc) @endcode * * @throws asio::service_already_exists Thrown if a service of the * given type is already present in the io_service. * * @throws asio::invalid_service_owner Thrown if the service's owning * io_service is not the io_service object specified by the ios parameter. */ template friend void add_service(io_service& ios, Service* svc); /// Determine if an io_service contains a specified service type. /** * This function is used to determine whether the io_service contains a * service object corresponding to the given service type. * * @param ios The io_service object that owns the service. * * @return A boolean indicating whether the io_service contains the service. */ template friend bool has_service(io_service& ios); private: #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) detail::winsock_init<> init_; #elif defined(__sun) || defined(__QNX__) || defined(__hpux) || defined(_AIX) \ || defined(__osf__) detail::signal_init<> init_; #endif // The service registry. asio::detail::service_registry* service_registry_; // The implementation. impl_type& impl_; }; /// Class to inform the io_service when it has work to do. /** * The work class is used to inform the io_service when work starts and * finishes. This ensures that the io_service object's run() function will not * exit while work is underway, and that it does exit when there is no * unfinished work remaining. * * The work class is copy-constructible so that it may be used as a data member * in a handler class. It is not assignable. */ class io_service::work { public: /// Constructor notifies the io_service that work is starting. /** * The constructor is used to inform the io_service that some work has begun. * This ensures that the io_service object's run() function will not exit * while the work is underway. */ explicit work(asio::io_service& io_service); /// Copy constructor notifies the io_service that work is starting. /** * The constructor is used to inform the io_service that some work has begun. * This ensures that the io_service object's run() function will not exit * while the work is underway. */ work(const work& other); /// Destructor notifies the io_service that the work is complete. /** * The destructor is used to inform the io_service that some work has * finished. Once the count of unfinished work reaches zero, the io_service * object's run() function is permitted to exit. */ ~work(); /// (Deprecated: use get_io_service().) Get the io_service associated with the /// work. asio::io_service& io_service(); /// Get the io_service associated with the work. asio::io_service& get_io_service(); private: // Prevent assignment. void operator=(const work& other); // The io_service. asio::io_service& io_service_; }; /// Class used to uniquely identify a service. class io_service::id : private noncopyable { public: /// Constructor. id() {} }; /// Base class for all io_service services. class io_service::service : private noncopyable { public: /// (Deprecated: use get_io_service().) Get the io_service object that owns /// the service. asio::io_service& io_service(); /// Get the io_service object that owns the service. asio::io_service& get_io_service(); protected: /// Constructor. /** * @param owner The io_service object that owns the service. */ ASIO_DECL service(asio::io_service& owner); /// Destructor. ASIO_DECL virtual ~service(); private: /// Destroy all user-defined handler objects owned by the service. virtual void shutdown_service() = 0; friend class asio::detail::service_registry; struct key { key() : type_info_(0), id_(0) {} const std::type_info* type_info_; const asio::io_service::id* id_; } key_; asio::io_service& owner_; service* next_; }; /// Exception thrown when trying to add a duplicate service to an io_service. class service_already_exists : public std::logic_error { public: ASIO_DECL service_already_exists(); }; /// Exception thrown when trying to add a service object to an io_service where /// the service has a different owner. class invalid_service_owner : public std::logic_error { public: ASIO_DECL invalid_service_owner(); }; namespace detail { // Special derived service id type to keep classes header-file only. template class service_id : public asio::io_service::id { }; // Special service base class to keep classes header-file only. template class service_base : public asio::io_service::service { public: static asio::detail::service_id id; // Constructor. service_base(asio::io_service& io_service) : asio::io_service::service(io_service) { } }; template asio::detail::service_id service_base::id; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #include "asio/impl/io_service.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/impl/io_service.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // ASIO_IO_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/ip/0000755000000000000000000000000012247075736021150 5ustar rootroot00000000000000percona-xtradb-cluster-galera/asio/asio/is_read_buffered.hpp0000644000000000000000000000302212247075736024516 0ustar rootroot00000000000000// // is_read_buffered.hpp // ~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IS_READ_BUFFERED_HPP #define ASIO_IS_READ_BUFFERED_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/buffered_read_stream_fwd.hpp" #include "asio/buffered_stream_fwd.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template char is_read_buffered_helper(buffered_stream* s); template char is_read_buffered_helper(buffered_read_stream* s); struct is_read_buffered_big_type { char data[10]; }; is_read_buffered_big_type is_read_buffered_helper(...); } // namespace detail /// The is_read_buffered class is a traits class that may be used to determine /// whether a stream type supports buffering of read data. template class is_read_buffered { public: #if defined(GENERATING_DOCUMENTATION) /// The value member is true only if the Stream type supports buffering of /// read data. static const bool value; #else BOOST_STATIC_CONSTANT(bool, value = sizeof(detail::is_read_buffered_helper((Stream*)0)) == 1); #endif }; } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IS_READ_BUFFERED_HPP percona-xtradb-cluster-galera/asio/asio/is_write_buffered.hpp0000644000000000000000000000304712247075736024744 0ustar rootroot00000000000000// // is_write_buffered.hpp // ~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IS_WRITE_BUFFERED_HPP #define ASIO_IS_WRITE_BUFFERED_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/buffered_stream_fwd.hpp" #include "asio/buffered_write_stream_fwd.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template char is_write_buffered_helper(buffered_stream* s); template char is_write_buffered_helper(buffered_write_stream* s); struct is_write_buffered_big_type { char data[10]; }; is_write_buffered_big_type is_write_buffered_helper(...); } // namespace detail /// The is_write_buffered class is a traits class that may be used to determine /// whether a stream type supports buffering of written data. template class is_write_buffered { public: #if defined(GENERATING_DOCUMENTATION) /// The value member is true only if the Stream type supports buffering of /// written data. static const bool value; #else BOOST_STATIC_CONSTANT(bool, value = sizeof(detail::is_write_buffered_helper((Stream*)0)) == 1); #endif }; } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IS_WRITE_BUFFERED_HPP percona-xtradb-cluster-galera/asio/asio/local/0000755000000000000000000000000012247075736021632 5ustar rootroot00000000000000percona-xtradb-cluster-galera/asio/asio/placeholders.hpp0000644000000000000000000000470512247075736023724 0ustar rootroot00000000000000// // placeholders.hpp // ~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_PLACEHOLDERS_HPP #define ASIO_PLACEHOLDERS_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include "asio/detail/push_options.hpp" namespace asio { namespace placeholders { #if defined(GENERATING_DOCUMENTATION) /// An argument placeholder, for use with boost::bind(), that corresponds to /// the error argument of a handler for any of the asynchronous functions. unspecified error; /// An argument placeholder, for use with boost::bind(), that corresponds to /// the bytes_transferred argument of a handler for asynchronous functions such /// as asio::basic_stream_socket::async_write_some or /// asio::async_write. unspecified bytes_transferred; /// An argument placeholder, for use with boost::bind(), that corresponds to /// the iterator argument of a handler for asynchronous functions such as /// asio::basic_resolver::resolve. unspecified iterator; #elif defined(__BORLANDC__) || defined(__GNUC__) inline boost::arg<1> error() { return boost::arg<1>(); } inline boost::arg<2> bytes_transferred() { return boost::arg<2>(); } inline boost::arg<2> iterator() { return boost::arg<2>(); } #else namespace detail { template struct placeholder { static boost::arg& get() { static boost::arg result; return result; } }; } #if BOOST_WORKAROUND(BOOST_MSVC, < 1400) static boost::arg<1>& error = asio::placeholders::detail::placeholder<1>::get(); static boost::arg<2>& bytes_transferred = asio::placeholders::detail::placeholder<2>::get(); static boost::arg<2>& iterator = asio::placeholders::detail::placeholder<2>::get(); #else namespace { boost::arg<1>& error = asio::placeholders::detail::placeholder<1>::get(); boost::arg<2>& bytes_transferred = asio::placeholders::detail::placeholder<2>::get(); boost::arg<2>& iterator = asio::placeholders::detail::placeholder<2>::get(); } // namespace #endif #endif } // namespace placeholders } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_PLACEHOLDERS_HPP percona-xtradb-cluster-galera/asio/asio/posix/0000755000000000000000000000000012247075736021702 5ustar rootroot00000000000000percona-xtradb-cluster-galera/asio/asio/raw_socket_service.hpp0000644000000000000000000002231712247075736025137 0ustar rootroot00000000000000// // raw_socket_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_RAW_SOCKET_SERVICE_HPP #define ASIO_RAW_SOCKET_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/error.hpp" #include "asio/io_service.hpp" #if defined(ASIO_HAS_IOCP) # include "asio/detail/win_iocp_socket_service.hpp" #else # include "asio/detail/reactive_socket_service.hpp" #endif #include "asio/detail/push_options.hpp" namespace asio { /// Default service implementation for a raw socket. template class raw_socket_service #if defined(GENERATING_DOCUMENTATION) : public asio::io_service::service #else : public asio::detail::service_base > #endif { public: #if defined(GENERATING_DOCUMENTATION) /// The unique service identifier. static asio::io_service::id id; #endif /// The protocol type. typedef Protocol protocol_type; /// The endpoint type. typedef typename Protocol::endpoint endpoint_type; private: // The type of the platform-specific implementation. #if defined(ASIO_HAS_IOCP) typedef detail::win_iocp_socket_service service_impl_type; #else typedef detail::reactive_socket_service service_impl_type; #endif public: /// The type of a raw socket. #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined implementation_type; #else typedef typename service_impl_type::implementation_type implementation_type; #endif /// The native socket type. #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined native_type; #else typedef typename service_impl_type::native_type native_type; #endif /// Construct a new raw socket service for the specified io_service. explicit raw_socket_service(asio::io_service& io_service) : asio::detail::service_base< raw_socket_service >(io_service), service_impl_(io_service) { } /// Destroy all user-defined handler objects owned by the service. void shutdown_service() { service_impl_.shutdown_service(); } /// Construct a new raw socket implementation. void construct(implementation_type& impl) { service_impl_.construct(impl); } /// Destroy a raw socket implementation. void destroy(implementation_type& impl) { service_impl_.destroy(impl); } // Open a new raw socket implementation. asio::error_code open(implementation_type& impl, const protocol_type& protocol, asio::error_code& ec) { if (protocol.type() == SOCK_RAW) service_impl_.open(impl, protocol, ec); else ec = asio::error::invalid_argument; return ec; } /// Assign an existing native socket to a raw socket. asio::error_code assign(implementation_type& impl, const protocol_type& protocol, const native_type& native_socket, asio::error_code& ec) { return service_impl_.assign(impl, protocol, native_socket, ec); } /// Determine whether the socket is open. bool is_open(const implementation_type& impl) const { return service_impl_.is_open(impl); } /// Close a raw socket implementation. asio::error_code close(implementation_type& impl, asio::error_code& ec) { return service_impl_.close(impl, ec); } /// Get the native socket implementation. native_type native(implementation_type& impl) { return service_impl_.native(impl); } /// Cancel all asynchronous operations associated with the socket. asio::error_code cancel(implementation_type& impl, asio::error_code& ec) { return service_impl_.cancel(impl, ec); } /// Determine whether the socket is at the out-of-band data mark. bool at_mark(const implementation_type& impl, asio::error_code& ec) const { return service_impl_.at_mark(impl, ec); } /// Determine the number of bytes available for reading. std::size_t available(const implementation_type& impl, asio::error_code& ec) const { return service_impl_.available(impl, ec); } // Bind the raw socket to the specified local endpoint. asio::error_code bind(implementation_type& impl, const endpoint_type& endpoint, asio::error_code& ec) { return service_impl_.bind(impl, endpoint, ec); } /// Connect the raw socket to the specified endpoint. asio::error_code connect(implementation_type& impl, const endpoint_type& peer_endpoint, asio::error_code& ec) { return service_impl_.connect(impl, peer_endpoint, ec); } /// Start an asynchronous connect. template void async_connect(implementation_type& impl, const endpoint_type& peer_endpoint, ConnectHandler handler) { service_impl_.async_connect(impl, peer_endpoint, handler); } /// Set a socket option. template asio::error_code set_option(implementation_type& impl, const SettableSocketOption& option, asio::error_code& ec) { return service_impl_.set_option(impl, option, ec); } /// Get a socket option. template asio::error_code get_option(const implementation_type& impl, GettableSocketOption& option, asio::error_code& ec) const { return service_impl_.get_option(impl, option, ec); } /// Perform an IO control command on the socket. template asio::error_code io_control(implementation_type& impl, IoControlCommand& command, asio::error_code& ec) { return service_impl_.io_control(impl, command, ec); } /// Get the local endpoint. endpoint_type local_endpoint(const implementation_type& impl, asio::error_code& ec) const { return service_impl_.local_endpoint(impl, ec); } /// Get the remote endpoint. endpoint_type remote_endpoint(const implementation_type& impl, asio::error_code& ec) const { return service_impl_.remote_endpoint(impl, ec); } /// Disable sends or receives on the socket. asio::error_code shutdown(implementation_type& impl, socket_base::shutdown_type what, asio::error_code& ec) { return service_impl_.shutdown(impl, what, ec); } /// Send the given data to the peer. template std::size_t send(implementation_type& impl, const ConstBufferSequence& buffers, socket_base::message_flags flags, asio::error_code& ec) { return service_impl_.send(impl, buffers, flags, ec); } /// Start an asynchronous send. template void async_send(implementation_type& impl, const ConstBufferSequence& buffers, socket_base::message_flags flags, WriteHandler handler) { service_impl_.async_send(impl, buffers, flags, handler); } /// Send raw data to the specified endpoint. template std::size_t send_to(implementation_type& impl, const ConstBufferSequence& buffers, const endpoint_type& destination, socket_base::message_flags flags, asio::error_code& ec) { return service_impl_.send_to(impl, buffers, destination, flags, ec); } /// Start an asynchronous send. template void async_send_to(implementation_type& impl, const ConstBufferSequence& buffers, const endpoint_type& destination, socket_base::message_flags flags, WriteHandler handler) { service_impl_.async_send_to(impl, buffers, destination, flags, handler); } /// Receive some data from the peer. template std::size_t receive(implementation_type& impl, const MutableBufferSequence& buffers, socket_base::message_flags flags, asio::error_code& ec) { return service_impl_.receive(impl, buffers, flags, ec); } /// Start an asynchronous receive. template void async_receive(implementation_type& impl, const MutableBufferSequence& buffers, socket_base::message_flags flags, ReadHandler handler) { service_impl_.async_receive(impl, buffers, flags, handler); } /// Receive raw data with the endpoint of the sender. template std::size_t receive_from(implementation_type& impl, const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, socket_base::message_flags flags, asio::error_code& ec) { return service_impl_.receive_from(impl, buffers, sender_endpoint, flags, ec); } /// Start an asynchronous receive that will get the endpoint of the sender. template void async_receive_from(implementation_type& impl, const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, socket_base::message_flags flags, ReadHandler handler) { service_impl_.async_receive_from(impl, buffers, sender_endpoint, flags, handler); } private: // The platform-specific implementation. service_impl_type service_impl_; }; } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_RAW_SOCKET_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/read.hpp0000644000000000000000000005264212247075736022175 0ustar rootroot00000000000000// // read.hpp // ~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_READ_HPP #define ASIO_READ_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/basic_streambuf_fwd.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { /** * @defgroup read asio::read * * @brief Attempt to read a certain amount of data from a stream before * returning. */ /*@{*/ /// Attempt to read a certain amount of data from a stream before returning. /** * This function is used to read a certain number of bytes of data from a * stream. The call will block until one of the following conditions is true: * * @li The supplied buffers are full. That is, the bytes transferred is equal to * the sum of the buffer sizes. * * @li An error occurred. * * This operation is implemented in terms of zero or more calls to the stream's * read_some function. * * @param s The stream from which the data is to be read. The type must support * the SyncReadStream concept. * * @param buffers One or more buffers into which the data will be read. The sum * of the buffer sizes indicates the maximum number of bytes to read from the * stream. * * @returns The number of bytes transferred. * * @throws asio::system_error Thrown on failure. * * @par Example * To read into a single data buffer use the @ref buffer function as follows: * @code asio::read(s, asio::buffer(data, size)); @endcode * See the @ref buffer documentation for information on reading into multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. * * @note This overload is equivalent to calling: * @code asio::read( * s, buffers, * asio::transfer_all()); @endcode */ template std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers); /// Attempt to read a certain amount of data from a stream before returning. /** * This function is used to read a certain number of bytes of data from a * stream. The call will block until one of the following conditions is true: * * @li The supplied buffers are full. That is, the bytes transferred is equal to * the sum of the buffer sizes. * * @li The completion_condition function object returns 0. * * This operation is implemented in terms of zero or more calls to the stream's * read_some function. * * @param s The stream from which the data is to be read. The type must support * the SyncReadStream concept. * * @param buffers One or more buffers into which the data will be read. The sum * of the buffer sizes indicates the maximum number of bytes to read from the * stream. * * @param completion_condition The function object to be called to determine * whether the read operation is complete. The signature of the function object * must be: * @code std::size_t completion_condition( * // Result of latest read_some operation. * const asio::error_code& error, * * // Number of bytes transferred so far. * std::size_t bytes_transferred * ); @endcode * A return value of 0 indicates that the read operation is complete. A non-zero * return value indicates the maximum number of bytes to be read on the next * call to the stream's read_some function. * * @returns The number of bytes transferred. * * @throws asio::system_error Thrown on failure. * * @par Example * To read into a single data buffer use the @ref buffer function as follows: * @code asio::read(s, asio::buffer(data, size), * asio::transfer_at_least(32)); @endcode * See the @ref buffer documentation for information on reading into multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers, CompletionCondition completion_condition); /// Attempt to read a certain amount of data from a stream before returning. /** * This function is used to read a certain number of bytes of data from a * stream. The call will block until one of the following conditions is true: * * @li The supplied buffers are full. That is, the bytes transferred is equal to * the sum of the buffer sizes. * * @li The completion_condition function object returns 0. * * This operation is implemented in terms of zero or more calls to the stream's * read_some function. * * @param s The stream from which the data is to be read. The type must support * the SyncReadStream concept. * * @param buffers One or more buffers into which the data will be read. The sum * of the buffer sizes indicates the maximum number of bytes to read from the * stream. * * @param completion_condition The function object to be called to determine * whether the read operation is complete. The signature of the function object * must be: * @code std::size_t completion_condition( * // Result of latest read_some operation. * const asio::error_code& error, * * // Number of bytes transferred so far. * std::size_t bytes_transferred * ); @endcode * A return value of 0 indicates that the read operation is complete. A non-zero * return value indicates the maximum number of bytes to be read on the next * call to the stream's read_some function. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes read. If an error occurs, returns the total * number of bytes successfully transferred prior to the error. */ template std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers, CompletionCondition completion_condition, asio::error_code& ec); #if !defined(BOOST_NO_IOSTREAM) /// Attempt to read a certain amount of data from a stream before returning. /** * This function is used to read a certain number of bytes of data from a * stream. The call will block until one of the following conditions is true: * * @li An error occurred. * * This operation is implemented in terms of zero or more calls to the stream's * read_some function. * * @param s The stream from which the data is to be read. The type must support * the SyncReadStream concept. * * @param b The basic_streambuf object into which the data will be read. * * @returns The number of bytes transferred. * * @throws asio::system_error Thrown on failure. * * @note This overload is equivalent to calling: * @code asio::read( * s, b, * asio::transfer_all()); @endcode */ template std::size_t read(SyncReadStream& s, basic_streambuf& b); /// Attempt to read a certain amount of data from a stream before returning. /** * This function is used to read a certain number of bytes of data from a * stream. The call will block until one of the following conditions is true: * * @li The completion_condition function object returns 0. * * This operation is implemented in terms of zero or more calls to the stream's * read_some function. * * @param s The stream from which the data is to be read. The type must support * the SyncReadStream concept. * * @param b The basic_streambuf object into which the data will be read. * * @param completion_condition The function object to be called to determine * whether the read operation is complete. The signature of the function object * must be: * @code std::size_t completion_condition( * // Result of latest read_some operation. * const asio::error_code& error, * * // Number of bytes transferred so far. * std::size_t bytes_transferred * ); @endcode * A return value of 0 indicates that the read operation is complete. A non-zero * return value indicates the maximum number of bytes to be read on the next * call to the stream's read_some function. * * @returns The number of bytes transferred. * * @throws asio::system_error Thrown on failure. */ template std::size_t read(SyncReadStream& s, basic_streambuf& b, CompletionCondition completion_condition); /// Attempt to read a certain amount of data from a stream before returning. /** * This function is used to read a certain number of bytes of data from a * stream. The call will block until one of the following conditions is true: * * @li The completion_condition function object returns 0. * * This operation is implemented in terms of zero or more calls to the stream's * read_some function. * * @param s The stream from which the data is to be read. The type must support * the SyncReadStream concept. * * @param b The basic_streambuf object into which the data will be read. * * @param completion_condition The function object to be called to determine * whether the read operation is complete. The signature of the function object * must be: * @code std::size_t completion_condition( * // Result of latest read_some operation. * const asio::error_code& error, * * // Number of bytes transferred so far. * std::size_t bytes_transferred * ); @endcode * A return value of 0 indicates that the read operation is complete. A non-zero * return value indicates the maximum number of bytes to be read on the next * call to the stream's read_some function. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes read. If an error occurs, returns the total * number of bytes successfully transferred prior to the error. */ template std::size_t read(SyncReadStream& s, basic_streambuf& b, CompletionCondition completion_condition, asio::error_code& ec); #endif // !defined(BOOST_NO_IOSTREAM) /*@}*/ /** * @defgroup async_read asio::async_read * * @brief Start an asynchronous operation to read a certain amount of data from * a stream. */ /*@{*/ /// Start an asynchronous operation to read a certain amount of data from a /// stream. /** * This function is used to asynchronously read a certain number of bytes of * data from a stream. The function call always returns immediately. The * asynchronous operation will continue until one of the following conditions is * true: * * @li The supplied buffers are full. That is, the bytes transferred is equal to * the sum of the buffer sizes. * * @li An error occurred. * * This operation is implemented in terms of zero or more calls to the stream's * async_read_some function, and is known as a composed operation. The * program must ensure that the stream performs no other read operations (such * as async_read, the stream's async_read_some function, or any other composed * operations that perform reads) until this operation completes. * * @param s The stream from which the data is to be read. The type must support * the AsyncReadStream concept. * * @param buffers One or more buffers into which the data will be read. The sum * of the buffer sizes indicates the maximum number of bytes to read from the * stream. Although the buffers object may be copied as necessary, ownership of * the underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param handler The handler to be called when the read operation completes. * Copies will be made of the handler as required. The function signature of the * handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * * std::size_t bytes_transferred // Number of bytes copied into the * // buffers. If an error occurred, * // this will be the number of * // bytes successfully transferred * // prior to the error. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation of * the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @par Example * To read into a single data buffer use the @ref buffer function as follows: * @code * asio::async_read(s, asio::buffer(data, size), handler); * @endcode * See the @ref buffer documentation for information on reading into multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. * * @note This overload is equivalent to calling: * @code asio::async_read( * s, buffers, * asio::transfer_all(), * handler); @endcode */ template void async_read(AsyncReadStream& s, const MutableBufferSequence& buffers, ReadHandler handler); /// Start an asynchronous operation to read a certain amount of data from a /// stream. /** * This function is used to asynchronously read a certain number of bytes of * data from a stream. The function call always returns immediately. The * asynchronous operation will continue until one of the following conditions is * true: * * @li The supplied buffers are full. That is, the bytes transferred is equal to * the sum of the buffer sizes. * * @li The completion_condition function object returns 0. * * @param s The stream from which the data is to be read. The type must support * the AsyncReadStream concept. * * @param buffers One or more buffers into which the data will be read. The sum * of the buffer sizes indicates the maximum number of bytes to read from the * stream. Although the buffers object may be copied as necessary, ownership of * the underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param completion_condition The function object to be called to determine * whether the read operation is complete. The signature of the function object * must be: * @code std::size_t completion_condition( * // Result of latest async_read_some operation. * const asio::error_code& error, * * // Number of bytes transferred so far. * std::size_t bytes_transferred * ); @endcode * A return value of 0 indicates that the read operation is complete. A non-zero * return value indicates the maximum number of bytes to be read on the next * call to the stream's async_read_some function. * * @param handler The handler to be called when the read operation completes. * Copies will be made of the handler as required. The function signature of the * handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * * std::size_t bytes_transferred // Number of bytes copied into the * // buffers. If an error occurred, * // this will be the number of * // bytes successfully transferred * // prior to the error. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation of * the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @par Example * To read into a single data buffer use the @ref buffer function as follows: * @code asio::async_read(s, * asio::buffer(data, size), * asio::transfer_at_least(32), * handler); @endcode * See the @ref buffer documentation for information on reading into multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template void async_read(AsyncReadStream& s, const MutableBufferSequence& buffers, CompletionCondition completion_condition, ReadHandler handler); #if !defined(BOOST_NO_IOSTREAM) /// Start an asynchronous operation to read a certain amount of data from a /// stream. /** * This function is used to asynchronously read a certain number of bytes of * data from a stream. The function call always returns immediately. The * asynchronous operation will continue until one of the following conditions is * true: * * @li An error occurred. * * This operation is implemented in terms of zero or more calls to the stream's * async_read_some function, and is known as a composed operation. The * program must ensure that the stream performs no other read operations (such * as async_read, the stream's async_read_some function, or any other composed * operations that perform reads) until this operation completes. * * @param s The stream from which the data is to be read. The type must support * the AsyncReadStream concept. * * @param b A basic_streambuf object into which the data will be read. Ownership * of the streambuf is retained by the caller, which must guarantee that it * remains valid until the handler is called. * * @param handler The handler to be called when the read operation completes. * Copies will be made of the handler as required. The function signature of the * handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * * std::size_t bytes_transferred // Number of bytes copied into the * // buffers. If an error occurred, * // this will be the number of * // bytes successfully transferred * // prior to the error. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation of * the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note This overload is equivalent to calling: * @code asio::async_read( * s, b, * asio::transfer_all(), * handler); @endcode */ template void async_read(AsyncReadStream& s, basic_streambuf& b, ReadHandler handler); /// Start an asynchronous operation to read a certain amount of data from a /// stream. /** * This function is used to asynchronously read a certain number of bytes of * data from a stream. The function call always returns immediately. The * asynchronous operation will continue until one of the following conditions is * true: * * @li The completion_condition function object returns 0. * * This operation is implemented in terms of zero or more calls to the stream's * async_read_some function, and is known as a composed operation. The * program must ensure that the stream performs no other read operations (such * as async_read, the stream's async_read_some function, or any other composed * operations that perform reads) until this operation completes. * * @param s The stream from which the data is to be read. The type must support * the AsyncReadStream concept. * * @param b A basic_streambuf object into which the data will be read. Ownership * of the streambuf is retained by the caller, which must guarantee that it * remains valid until the handler is called. * * @param completion_condition The function object to be called to determine * whether the read operation is complete. The signature of the function object * must be: * @code std::size_t completion_condition( * // Result of latest async_read_some operation. * const asio::error_code& error, * * // Number of bytes transferred so far. * std::size_t bytes_transferred * ); @endcode * A return value of 0 indicates that the read operation is complete. A non-zero * return value indicates the maximum number of bytes to be read on the next * call to the stream's async_read_some function. * * @param handler The handler to be called when the read operation completes. * Copies will be made of the handler as required. The function signature of the * handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * * std::size_t bytes_transferred // Number of bytes copied into the * // buffers. If an error occurred, * // this will be the number of * // bytes successfully transferred * // prior to the error. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation of * the handler will be performed in a manner equivalent to using * asio::io_service::post(). */ template void async_read(AsyncReadStream& s, basic_streambuf& b, CompletionCondition completion_condition, ReadHandler handler); #endif // !defined(BOOST_NO_IOSTREAM) /*@}*/ } // namespace asio #include "asio/detail/pop_options.hpp" #include "asio/impl/read.hpp" #endif // ASIO_READ_HPP percona-xtradb-cluster-galera/asio/asio/read_at.hpp0000644000000000000000000005360712247075736022663 0ustar rootroot00000000000000// // read_at.hpp // ~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_READ_AT_HPP #define ASIO_READ_AT_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include "asio/basic_streambuf_fwd.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { /** * @defgroup read_at asio::read_at * * @brief Attempt to read a certain amount of data at the specified offset * before returning. */ /*@{*/ /// Attempt to read a certain amount of data at the specified offset before /// returning. /** * This function is used to read a certain number of bytes of data from a * random access device at the specified offset. The call will block until one * of the following conditions is true: * * @li The supplied buffers are full. That is, the bytes transferred is equal to * the sum of the buffer sizes. * * @li An error occurred. * * This operation is implemented in terms of zero or more calls to the device's * read_some_at function. * * @param d The device from which the data is to be read. The type must support * the SyncRandomAccessReadDevice concept. * * @param offset The offset at which the data will be read. * * @param buffers One or more buffers into which the data will be read. The sum * of the buffer sizes indicates the maximum number of bytes to read from the * device. * * @returns The number of bytes transferred. * * @throws asio::system_error Thrown on failure. * * @par Example * To read into a single data buffer use the @ref buffer function as follows: * @code asio::read_at(d, 42, asio::buffer(data, size)); @endcode * See the @ref buffer documentation for information on reading into multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. * * @note This overload is equivalent to calling: * @code asio::read_at( * d, 42, buffers, * asio::transfer_all()); @endcode */ template std::size_t read_at(SyncRandomAccessReadDevice& d, boost::uint64_t offset, const MutableBufferSequence& buffers); /// Attempt to read a certain amount of data at the specified offset before /// returning. /** * This function is used to read a certain number of bytes of data from a * random access device at the specified offset. The call will block until one * of the following conditions is true: * * @li The supplied buffers are full. That is, the bytes transferred is equal to * the sum of the buffer sizes. * * @li The completion_condition function object returns 0. * * This operation is implemented in terms of zero or more calls to the device's * read_some_at function. * * @param d The device from which the data is to be read. The type must support * the SyncRandomAccessReadDevice concept. * * @param offset The offset at which the data will be read. * * @param buffers One or more buffers into which the data will be read. The sum * of the buffer sizes indicates the maximum number of bytes to read from the * device. * * @param completion_condition The function object to be called to determine * whether the read operation is complete. The signature of the function object * must be: * @code std::size_t completion_condition( * // Result of latest read_some_at operation. * const asio::error_code& error, * * // Number of bytes transferred so far. * std::size_t bytes_transferred * ); @endcode * A return value of 0 indicates that the read operation is complete. A non-zero * return value indicates the maximum number of bytes to be read on the next * call to the device's read_some_at function. * * @returns The number of bytes transferred. * * @throws asio::system_error Thrown on failure. * * @par Example * To read into a single data buffer use the @ref buffer function as follows: * @code asio::read_at(d, 42, asio::buffer(data, size), * asio::transfer_at_least(32)); @endcode * See the @ref buffer documentation for information on reading into multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template std::size_t read_at(SyncRandomAccessReadDevice& d, boost::uint64_t offset, const MutableBufferSequence& buffers, CompletionCondition completion_condition); /// Attempt to read a certain amount of data at the specified offset before /// returning. /** * This function is used to read a certain number of bytes of data from a * random access device at the specified offset. The call will block until one * of the following conditions is true: * * @li The supplied buffers are full. That is, the bytes transferred is equal to * the sum of the buffer sizes. * * @li The completion_condition function object returns 0. * * This operation is implemented in terms of zero or more calls to the device's * read_some_at function. * * @param d The device from which the data is to be read. The type must support * the SyncRandomAccessReadDevice concept. * * @param offset The offset at which the data will be read. * * @param buffers One or more buffers into which the data will be read. The sum * of the buffer sizes indicates the maximum number of bytes to read from the * device. * * @param completion_condition The function object to be called to determine * whether the read operation is complete. The signature of the function object * must be: * @code std::size_t completion_condition( * // Result of latest read_some_at operation. * const asio::error_code& error, * * // Number of bytes transferred so far. * std::size_t bytes_transferred * ); @endcode * A return value of 0 indicates that the read operation is complete. A non-zero * return value indicates the maximum number of bytes to be read on the next * call to the device's read_some_at function. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes read. If an error occurs, returns the total * number of bytes successfully transferred prior to the error. */ template std::size_t read_at(SyncRandomAccessReadDevice& d, boost::uint64_t offset, const MutableBufferSequence& buffers, CompletionCondition completion_condition, asio::error_code& ec); #if !defined(BOOST_NO_IOSTREAM) /// Attempt to read a certain amount of data at the specified offset before /// returning. /** * This function is used to read a certain number of bytes of data from a * random access device at the specified offset. The call will block until one * of the following conditions is true: * * @li An error occurred. * * This operation is implemented in terms of zero or more calls to the device's * read_some_at function. * * @param d The device from which the data is to be read. The type must support * the SyncRandomAccessReadDevice concept. * * @param offset The offset at which the data will be read. * * @param b The basic_streambuf object into which the data will be read. * * @returns The number of bytes transferred. * * @throws asio::system_error Thrown on failure. * * @note This overload is equivalent to calling: * @code asio::read_at( * d, 42, b, * asio::transfer_all()); @endcode */ template std::size_t read_at(SyncRandomAccessReadDevice& d, boost::uint64_t offset, basic_streambuf& b); /// Attempt to read a certain amount of data at the specified offset before /// returning. /** * This function is used to read a certain number of bytes of data from a * random access device at the specified offset. The call will block until one * of the following conditions is true: * * @li The completion_condition function object returns 0. * * This operation is implemented in terms of zero or more calls to the device's * read_some_at function. * * @param d The device from which the data is to be read. The type must support * the SyncRandomAccessReadDevice concept. * * @param offset The offset at which the data will be read. * * @param b The basic_streambuf object into which the data will be read. * * @param completion_condition The function object to be called to determine * whether the read operation is complete. The signature of the function object * must be: * @code std::size_t completion_condition( * // Result of latest read_some_at operation. * const asio::error_code& error, * * // Number of bytes transferred so far. * std::size_t bytes_transferred * ); @endcode * A return value of 0 indicates that the read operation is complete. A non-zero * return value indicates the maximum number of bytes to be read on the next * call to the device's read_some_at function. * * @returns The number of bytes transferred. * * @throws asio::system_error Thrown on failure. */ template std::size_t read_at(SyncRandomAccessReadDevice& d, boost::uint64_t offset, basic_streambuf& b, CompletionCondition completion_condition); /// Attempt to read a certain amount of data at the specified offset before /// returning. /** * This function is used to read a certain number of bytes of data from a * random access device at the specified offset. The call will block until one * of the following conditions is true: * * @li The completion_condition function object returns 0. * * This operation is implemented in terms of zero or more calls to the device's * read_some_at function. * * @param d The device from which the data is to be read. The type must support * the SyncRandomAccessReadDevice concept. * * @param offset The offset at which the data will be read. * * @param b The basic_streambuf object into which the data will be read. * * @param completion_condition The function object to be called to determine * whether the read operation is complete. The signature of the function object * must be: * @code std::size_t completion_condition( * // Result of latest read_some_at operation. * const asio::error_code& error, * * // Number of bytes transferred so far. * std::size_t bytes_transferred * ); @endcode * A return value of 0 indicates that the read operation is complete. A non-zero * return value indicates the maximum number of bytes to be read on the next * call to the device's read_some_at function. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes read. If an error occurs, returns the total * number of bytes successfully transferred prior to the error. */ template std::size_t read_at(SyncRandomAccessReadDevice& d, boost::uint64_t offset, basic_streambuf& b, CompletionCondition completion_condition, asio::error_code& ec); #endif // !defined(BOOST_NO_IOSTREAM) /*@}*/ /** * @defgroup async_read_at asio::async_read_at * * @brief Start an asynchronous operation to read a certain amount of data at * the specified offset. */ /*@{*/ /// Start an asynchronous operation to read a certain amount of data at the /// specified offset. /** * This function is used to asynchronously read a certain number of bytes of * data from a random access device at the specified offset. The function call * always returns immediately. The asynchronous operation will continue until * one of the following conditions is true: * * @li The supplied buffers are full. That is, the bytes transferred is equal to * the sum of the buffer sizes. * * @li An error occurred. * * This operation is implemented in terms of zero or more calls to the device's * async_read_some_at function. * * @param d The device from which the data is to be read. The type must support * the AsyncRandomAccessReadDevice concept. * * @param offset The offset at which the data will be read. * * @param buffers One or more buffers into which the data will be read. The sum * of the buffer sizes indicates the maximum number of bytes to read from the * device. Although the buffers object may be copied as necessary, ownership of * the underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param handler The handler to be called when the read operation completes. * Copies will be made of the handler as required. The function signature of the * handler must be: * @code void handler( * // Result of operation. * const asio::error_code& error, * * // Number of bytes copied into the buffers. If an error * // occurred, this will be the number of bytes successfully * // transferred prior to the error. * std::size_t bytes_transferred * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation of * the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @par Example * To read into a single data buffer use the @ref buffer function as follows: * @code * asio::async_read_at(d, 42, asio::buffer(data, size), handler); * @endcode * See the @ref buffer documentation for information on reading into multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. * * @note This overload is equivalent to calling: * @code asio::async_read_at( * d, 42, buffers, * asio::transfer_all(), * handler); @endcode */ template void async_read_at(AsyncRandomAccessReadDevice& d, boost::uint64_t offset, const MutableBufferSequence& buffers, ReadHandler handler); /// Start an asynchronous operation to read a certain amount of data at the /// specified offset. /** * This function is used to asynchronously read a certain number of bytes of * data from a random access device at the specified offset. The function call * always returns immediately. The asynchronous operation will continue until * one of the following conditions is true: * * @li The supplied buffers are full. That is, the bytes transferred is equal to * the sum of the buffer sizes. * * @li The completion_condition function object returns 0. * * @param d The device from which the data is to be read. The type must support * the AsyncRandomAccessReadDevice concept. * * @param offset The offset at which the data will be read. * * @param buffers One or more buffers into which the data will be read. The sum * of the buffer sizes indicates the maximum number of bytes to read from the * device. Although the buffers object may be copied as necessary, ownership of * the underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param completion_condition The function object to be called to determine * whether the read operation is complete. The signature of the function object * must be: * @code std::size_t completion_condition( * // Result of latest async_read_some_at operation. * const asio::error_code& error, * * // Number of bytes transferred so far. * std::size_t bytes_transferred * ); @endcode * A return value of 0 indicates that the read operation is complete. A non-zero * return value indicates the maximum number of bytes to be read on the next * call to the device's async_read_some_at function. * * @param handler The handler to be called when the read operation completes. * Copies will be made of the handler as required. The function signature of the * handler must be: * @code void handler( * // Result of operation. * const asio::error_code& error, * * // Number of bytes copied into the buffers. If an error * // occurred, this will be the number of bytes successfully * // transferred prior to the error. * std::size_t bytes_transferred * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation of * the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @par Example * To read into a single data buffer use the @ref buffer function as follows: * @code asio::async_read_at(d, 42, * asio::buffer(data, size), * asio::transfer_at_least(32), * handler); @endcode * See the @ref buffer documentation for information on reading into multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template void async_read_at(AsyncRandomAccessReadDevice& d, boost::uint64_t offset, const MutableBufferSequence& buffers, CompletionCondition completion_condition, ReadHandler handler); #if !defined(BOOST_NO_IOSTREAM) /// Start an asynchronous operation to read a certain amount of data at the /// specified offset. /** * This function is used to asynchronously read a certain number of bytes of * data from a random access device at the specified offset. The function call * always returns immediately. The asynchronous operation will continue until * one of the following conditions is true: * * @li An error occurred. * * This operation is implemented in terms of zero or more calls to the device's * async_read_some_at function. * * @param d The device from which the data is to be read. The type must support * the AsyncRandomAccessReadDevice concept. * * @param offset The offset at which the data will be read. * * @param b A basic_streambuf object into which the data will be read. Ownership * of the streambuf is retained by the caller, which must guarantee that it * remains valid until the handler is called. * * @param handler The handler to be called when the read operation completes. * Copies will be made of the handler as required. The function signature of the * handler must be: * @code void handler( * // Result of operation. * const asio::error_code& error, * * // Number of bytes copied into the buffers. If an error * // occurred, this will be the number of bytes successfully * // transferred prior to the error. * std::size_t bytes_transferred * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation of * the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note This overload is equivalent to calling: * @code asio::async_read_at( * d, 42, b, * asio::transfer_all(), * handler); @endcode */ template void async_read_at(AsyncRandomAccessReadDevice& d, boost::uint64_t offset, basic_streambuf& b, ReadHandler handler); /// Start an asynchronous operation to read a certain amount of data at the /// specified offset. /** * This function is used to asynchronously read a certain number of bytes of * data from a random access device at the specified offset. The function call * always returns immediately. The asynchronous operation will continue until * one of the following conditions is true: * * @li The completion_condition function object returns 0. * * This operation is implemented in terms of zero or more calls to the device's * async_read_some_at function. * * @param d The device from which the data is to be read. The type must support * the AsyncRandomAccessReadDevice concept. * * @param offset The offset at which the data will be read. * * @param b A basic_streambuf object into which the data will be read. Ownership * of the streambuf is retained by the caller, which must guarantee that it * remains valid until the handler is called. * * @param completion_condition The function object to be called to determine * whether the read operation is complete. The signature of the function object * must be: * @code std::size_t completion_condition( * // Result of latest async_read_some_at operation. * const asio::error_code& error, * * // Number of bytes transferred so far. * std::size_t bytes_transferred * ); @endcode * A return value of 0 indicates that the read operation is complete. A non-zero * return value indicates the maximum number of bytes to be read on the next * call to the device's async_read_some_at function. * * @param handler The handler to be called when the read operation completes. * Copies will be made of the handler as required. The function signature of the * handler must be: * @code void handler( * // Result of operation. * const asio::error_code& error, * * // Number of bytes copied into the buffers. If an error * // occurred, this will be the number of bytes successfully * // transferred prior to the error. * std::size_t bytes_transferred * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation of * the handler will be performed in a manner equivalent to using * asio::io_service::post(). */ template void async_read_at(AsyncRandomAccessReadDevice& d, boost::uint64_t offset, basic_streambuf& b, CompletionCondition completion_condition, ReadHandler handler); #endif // !defined(BOOST_NO_IOSTREAM) /*@}*/ } // namespace asio #include "asio/detail/pop_options.hpp" #include "asio/impl/read_at.hpp" #endif // ASIO_READ_AT_HPP percona-xtradb-cluster-galera/asio/asio/read_until.hpp0000644000000000000000000010730212247075736023402 0ustar rootroot00000000000000// // read_until.hpp // ~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_READ_UNTIL_HPP #define ASIO_READ_UNTIL_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(BOOST_NO_IOSTREAM) #include #include #include #include #include #include #include "asio/basic_streambuf.hpp" #include "asio/detail/regex_fwd.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { #if BOOST_WORKAROUND(__CODEGEARC__, BOOST_TESTED_AT(0x610)) template struct has_result_type { template struct inner { struct big { char a[100]; }; static big helper(U, ...); static char helper(U, typename U::result_type* = 0); }; static const T& ref(); enum { value = (sizeof((inner::helper)((ref)())) == 1) }; }; #else // BOOST_WORKAROUND(__CODEGEARC__, BOOST_TESTED_AT(0x610)) template struct has_result_type { struct big { char a[100]; }; template static big helper(U, ...); template static char helper(U, typename U::result_type* = 0); static const T& ref(); enum { value = (sizeof((helper)((ref)())) == 1) }; }; #endif // BOOST_WORKAROUND(__CODEGEARC__, BOOST_TESTED_AT(0x610)) } // namespace detail /// Type trait used to determine whether a type can be used as a match condition /// function with read_until and async_read_until. template struct is_match_condition { #if defined(GENERATING_DOCUMENTATION) /// The value member is true if the type may be used as a match condition. static const bool value; #else enum { value = boost::is_function::type>::value || detail::has_result_type::value }; #endif }; /** * @defgroup read_until asio::read_until * * @brief Read data into a streambuf until it contains a delimiter, matches a * regular expression, or a function object indicates a match. */ /*@{*/ /// Read data into a streambuf until it contains a specified delimiter. /** * This function is used to read data into the specified streambuf until the * streambuf's get area contains the specified delimiter. The call will block * until one of the following conditions is true: * * @li The get area of the streambuf contains the specified delimiter. * * @li An error occurred. * * This operation is implemented in terms of zero or more calls to the stream's * read_some function. If the streambuf's get area already contains the * delimiter, the function returns immediately. * * @param s The stream from which the data is to be read. The type must support * the SyncReadStream concept. * * @param b A streambuf object into which the data will be read. * * @param delim The delimiter character. * * @returns The number of bytes in the streambuf's get area up to and including * the delimiter. * * @throws asio::system_error Thrown on failure. * * @note After a successful read_until operation, the streambuf may contain * additional data beyond the delimiter. An application will typically leave * that data in the streambuf for a subsequent read_until operation to examine. * * @par Example * To read data into a streambuf until a newline is encountered: * @code asio::streambuf b; * asio::read_until(s, b, '\n'); * std::istream is(&b); * std::string line; * std::getline(is, line); @endcode * After the @c read_until operation completes successfully, the buffer @c b * contains the delimiter: * @code { 'a', 'b', ..., 'c', '\n', 'd', 'e', ... } @endcode * The call to @c std::getline then extracts the data up to and including the * delimiter, so that the string @c line contains: * @code { 'a', 'b', ..., 'c', '\n' } @endcode * The remaining data is left in the buffer @c b as follows: * @code { 'd', 'e', ... } @endcode * This data may be the start of a new line, to be extracted by a subsequent * @c read_until operation. */ template std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, char delim); /// Read data into a streambuf until it contains a specified delimiter. /** * This function is used to read data into the specified streambuf until the * streambuf's get area contains the specified delimiter. The call will block * until one of the following conditions is true: * * @li The get area of the streambuf contains the specified delimiter. * * @li An error occurred. * * This operation is implemented in terms of zero or more calls to the stream's * read_some function. If the streambuf's get area already contains the * delimiter, the function returns immediately. * * @param s The stream from which the data is to be read. The type must support * the SyncReadStream concept. * * @param b A streambuf object into which the data will be read. * * @param delim The delimiter character. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes in the streambuf's get area up to and including * the delimiter. Returns 0 if an error occurred. * * @note After a successful read_until operation, the streambuf may contain * additional data beyond the delimiter. An application will typically leave * that data in the streambuf for a subsequent read_until operation to examine. */ template std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, char delim, asio::error_code& ec); /// Read data into a streambuf until it contains a specified delimiter. /** * This function is used to read data into the specified streambuf until the * streambuf's get area contains the specified delimiter. The call will block * until one of the following conditions is true: * * @li The get area of the streambuf contains the specified delimiter. * * @li An error occurred. * * This operation is implemented in terms of zero or more calls to the stream's * read_some function. If the streambuf's get area already contains the * delimiter, the function returns immediately. * * @param s The stream from which the data is to be read. The type must support * the SyncReadStream concept. * * @param b A streambuf object into which the data will be read. * * @param delim The delimiter string. * * @returns The number of bytes in the streambuf's get area up to and including * the delimiter. * * @throws asio::system_error Thrown on failure. * * @note After a successful read_until operation, the streambuf may contain * additional data beyond the delimiter. An application will typically leave * that data in the streambuf for a subsequent read_until operation to examine. * * @par Example * To read data into a streambuf until a newline is encountered: * @code asio::streambuf b; * asio::read_until(s, b, "\r\n"); * std::istream is(&b); * std::string line; * std::getline(is, line); @endcode * After the @c read_until operation completes successfully, the buffer @c b * contains the delimiter: * @code { 'a', 'b', ..., 'c', '\r', '\n', 'd', 'e', ... } @endcode * The call to @c std::getline then extracts the data up to and including the * delimiter, so that the string @c line contains: * @code { 'a', 'b', ..., 'c', '\r', '\n' } @endcode * The remaining data is left in the buffer @c b as follows: * @code { 'd', 'e', ... } @endcode * This data may be the start of a new line, to be extracted by a subsequent * @c read_until operation. */ template std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, const std::string& delim); /// Read data into a streambuf until it contains a specified delimiter. /** * This function is used to read data into the specified streambuf until the * streambuf's get area contains the specified delimiter. The call will block * until one of the following conditions is true: * * @li The get area of the streambuf contains the specified delimiter. * * @li An error occurred. * * This operation is implemented in terms of zero or more calls to the stream's * read_some function. If the streambuf's get area already contains the * delimiter, the function returns immediately. * * @param s The stream from which the data is to be read. The type must support * the SyncReadStream concept. * * @param b A streambuf object into which the data will be read. * * @param delim The delimiter string. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes in the streambuf's get area up to and including * the delimiter. Returns 0 if an error occurred. * * @note After a successful read_until operation, the streambuf may contain * additional data beyond the delimiter. An application will typically leave * that data in the streambuf for a subsequent read_until operation to examine. */ template std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, const std::string& delim, asio::error_code& ec); /// Read data into a streambuf until some part of the data it contains matches /// a regular expression. /** * This function is used to read data into the specified streambuf until the * streambuf's get area contains some data that matches a regular expression. * The call will block until one of the following conditions is true: * * @li A substring of the streambuf's get area matches the regular expression. * * @li An error occurred. * * This operation is implemented in terms of zero or more calls to the stream's * read_some function. If the streambuf's get area already contains data that * matches the regular expression, the function returns immediately. * * @param s The stream from which the data is to be read. The type must support * the SyncReadStream concept. * * @param b A streambuf object into which the data will be read. * * @param expr The regular expression. * * @returns The number of bytes in the streambuf's get area up to and including * the substring that matches the regular expression. * * @throws asio::system_error Thrown on failure. * * @note After a successful read_until operation, the streambuf may contain * additional data beyond that which matched the regular expression. An * application will typically leave that data in the streambuf for a subsequent * read_until operation to examine. * * @par Example * To read data into a streambuf until a CR-LF sequence is encountered: * @code asio::streambuf b; * asio::read_until(s, b, boost::regex("\r\n")); * std::istream is(&b); * std::string line; * std::getline(is, line); @endcode * After the @c read_until operation completes successfully, the buffer @c b * contains the data which matched the regular expression: * @code { 'a', 'b', ..., 'c', '\r', '\n', 'd', 'e', ... } @endcode * The call to @c std::getline then extracts the data up to and including the * match, so that the string @c line contains: * @code { 'a', 'b', ..., 'c', '\r', '\n' } @endcode * The remaining data is left in the buffer @c b as follows: * @code { 'd', 'e', ... } @endcode * This data may be the start of a new line, to be extracted by a subsequent * @c read_until operation. */ template std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, const boost::regex& expr); /// Read data into a streambuf until some part of the data it contains matches /// a regular expression. /** * This function is used to read data into the specified streambuf until the * streambuf's get area contains some data that matches a regular expression. * The call will block until one of the following conditions is true: * * @li A substring of the streambuf's get area matches the regular expression. * * @li An error occurred. * * This operation is implemented in terms of zero or more calls to the stream's * read_some function. If the streambuf's get area already contains data that * matches the regular expression, the function returns immediately. * * @param s The stream from which the data is to be read. The type must support * the SyncReadStream concept. * * @param b A streambuf object into which the data will be read. * * @param expr The regular expression. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes in the streambuf's get area up to and including * the substring that matches the regular expression. Returns 0 if an error * occurred. * * @note After a successful read_until operation, the streambuf may contain * additional data beyond that which matched the regular expression. An * application will typically leave that data in the streambuf for a subsequent * read_until operation to examine. */ template std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, const boost::regex& expr, asio::error_code& ec); /// Read data into a streambuf until a function object indicates a match. /** * This function is used to read data into the specified streambuf until a * user-defined match condition function object, when applied to the data * contained in the streambuf, indicates a successful match. The call will * block until one of the following conditions is true: * * @li The match condition function object returns a std::pair where the second * element evaluates to true. * * @li An error occurred. * * This operation is implemented in terms of zero or more calls to the stream's * read_some function. If the match condition function object already indicates * a match, the function returns immediately. * * @param s The stream from which the data is to be read. The type must support * the SyncReadStream concept. * * @param b A streambuf object into which the data will be read. * * @param match_condition The function object to be called to determine whether * a match exists. The signature of the function object must be: * @code pair match_condition(iterator begin, iterator end); * @endcode * where @c iterator represents the type: * @code buffers_iterator::const_buffers_type> * @endcode * The iterator parameters @c begin and @c end define the range of bytes to be * scanned to determine whether there is a match. The @c first member of the * return value is an iterator marking one-past-the-end of the bytes that have * been consumed by the match function. This iterator is used to calculate the * @c begin parameter for any subsequent invocation of the match condition. The * @c second member of the return value is true if a match has been found, false * otherwise. * * @returns The number of bytes in the streambuf's get area that have been fully * consumed by the match function. * * @throws asio::system_error Thrown on failure. * * @note After a successful read_until operation, the streambuf may contain * additional data beyond that which matched the function object. An application * will typically leave that data in the streambuf for a subsequent * * @note The default implementation of the @c is_match_condition type trait * evaluates to true for function pointers and function objects with a * @c result_type typedef. It must be specialised for other user-defined * function objects. * * @par Examples * To read data into a streambuf until whitespace is encountered: * @code typedef asio::buffers_iterator< * asio::streambuf::const_buffers_type> iterator; * * std::pair * match_whitespace(iterator begin, iterator end) * { * iterator i = begin; * while (i != end) * if (std::isspace(*i++)) * return std::make_pair(i, true); * return std::make_pair(i, false); * } * ... * asio::streambuf b; * asio::read_until(s, b, match_whitespace); * @endcode * * To read data into a streambuf until a matching character is found: * @code class match_char * { * public: * explicit match_char(char c) : c_(c) {} * * template * std::pair operator()( * Iterator begin, Iterator end) const * { * Iterator i = begin; * while (i != end) * if (c_ == *i++) * return std::make_pair(i, true); * return std::make_pair(i, false); * } * * private: * char c_; * }; * * namespace asio { * template <> struct is_match_condition * : public boost::true_type {}; * } // namespace asio * ... * asio::streambuf b; * asio::read_until(s, b, match_char('a')); * @endcode */ template std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, MatchCondition match_condition, typename boost::enable_if >::type* = 0); /// Read data into a streambuf until a function object indicates a match. /** * This function is used to read data into the specified streambuf until a * user-defined match condition function object, when applied to the data * contained in the streambuf, indicates a successful match. The call will * block until one of the following conditions is true: * * @li The match condition function object returns a std::pair where the second * element evaluates to true. * * @li An error occurred. * * This operation is implemented in terms of zero or more calls to the stream's * read_some function. If the match condition function object already indicates * a match, the function returns immediately. * * @param s The stream from which the data is to be read. The type must support * the SyncReadStream concept. * * @param b A streambuf object into which the data will be read. * * @param match_condition The function object to be called to determine whether * a match exists. The signature of the function object must be: * @code pair match_condition(iterator begin, iterator end); * @endcode * where @c iterator represents the type: * @code buffers_iterator::const_buffers_type> * @endcode * The iterator parameters @c begin and @c end define the range of bytes to be * scanned to determine whether there is a match. The @c first member of the * return value is an iterator marking one-past-the-end of the bytes that have * been consumed by the match function. This iterator is used to calculate the * @c begin parameter for any subsequent invocation of the match condition. The * @c second member of the return value is true if a match has been found, false * otherwise. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes in the streambuf's get area that have been fully * consumed by the match function. Returns 0 if an error occurred. * * @note After a successful read_until operation, the streambuf may contain * additional data beyond that which matched the function object. An application * will typically leave that data in the streambuf for a subsequent * * @note The default implementation of the @c is_match_condition type trait * evaluates to true for function pointers and function objects with a * @c result_type typedef. It must be specialised for other user-defined * function objects. */ template std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, MatchCondition match_condition, asio::error_code& ec, typename boost::enable_if >::type* = 0); /*@}*/ /** * @defgroup async_read_until asio::async_read_until * * @brief Start an asynchronous operation to read data into a streambuf until it * contains a delimiter, matches a regular expression, or a function object * indicates a match. */ /*@{*/ /// Start an asynchronous operation to read data into a streambuf until it /// contains a specified delimiter. /** * This function is used to asynchronously read data into the specified * streambuf until the streambuf's get area contains the specified delimiter. * The function call always returns immediately. The asynchronous operation * will continue until one of the following conditions is true: * * @li The get area of the streambuf contains the specified delimiter. * * @li An error occurred. * * This operation is implemented in terms of zero or more calls to the stream's * async_read_some function, and is known as a composed operation. If * the streambuf's get area already contains the delimiter, this asynchronous * operation completes immediately. The program must ensure that the stream * performs no other read operations (such as async_read, async_read_until, the * stream's async_read_some function, or any other composed operations that * perform reads) until this operation completes. * * @param s The stream from which the data is to be read. The type must support * the AsyncReadStream concept. * * @param b A streambuf object into which the data will be read. Ownership of * the streambuf is retained by the caller, which must guarantee that it remains * valid until the handler is called. * * @param delim The delimiter character. * * @param handler The handler to be called when the read operation completes. * Copies will be made of the handler as required. The function signature of the * handler must be: * @code void handler( * // Result of operation. * const asio::error_code& error, * * // The number of bytes in the streambuf's get * // area up to and including the delimiter. * // 0 if an error occurred. * std::size_t bytes_transferred * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation of * the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note After a successful async_read_until operation, the streambuf may * contain additional data beyond the delimiter. An application will typically * leave that data in the streambuf for a subsequent async_read_until operation * to examine. * * @par Example * To asynchronously read data into a streambuf until a newline is encountered: * @code asio::streambuf b; * ... * void handler(const asio::error_code& e, std::size_t size) * { * if (!e) * { * std::istream is(&b); * std::string line; * std::getline(is, line); * ... * } * } * ... * asio::async_read_until(s, b, '\n', handler); @endcode * After the @c async_read_until operation completes successfully, the buffer * @c b contains the delimiter: * @code { 'a', 'b', ..., 'c', '\n', 'd', 'e', ... } @endcode * The call to @c std::getline then extracts the data up to and including the * delimiter, so that the string @c line contains: * @code { 'a', 'b', ..., 'c', '\n' } @endcode * The remaining data is left in the buffer @c b as follows: * @code { 'd', 'e', ... } @endcode * This data may be the start of a new line, to be extracted by a subsequent * @c async_read_until operation. */ template void async_read_until(AsyncReadStream& s, asio::basic_streambuf& b, char delim, ReadHandler handler); /// Start an asynchronous operation to read data into a streambuf until it /// contains a specified delimiter. /** * This function is used to asynchronously read data into the specified * streambuf until the streambuf's get area contains the specified delimiter. * The function call always returns immediately. The asynchronous operation * will continue until one of the following conditions is true: * * @li The get area of the streambuf contains the specified delimiter. * * @li An error occurred. * * This operation is implemented in terms of zero or more calls to the stream's * async_read_some function, and is known as a composed operation. If * the streambuf's get area already contains the delimiter, this asynchronous * operation completes immediately. The program must ensure that the stream * performs no other read operations (such as async_read, async_read_until, the * stream's async_read_some function, or any other composed operations that * perform reads) until this operation completes. * * @param s The stream from which the data is to be read. The type must support * the AsyncReadStream concept. * * @param b A streambuf object into which the data will be read. Ownership of * the streambuf is retained by the caller, which must guarantee that it remains * valid until the handler is called. * * @param delim The delimiter string. * * @param handler The handler to be called when the read operation completes. * Copies will be made of the handler as required. The function signature of the * handler must be: * @code void handler( * // Result of operation. * const asio::error_code& error, * * // The number of bytes in the streambuf's get * // area up to and including the delimiter. * // 0 if an error occurred. * std::size_t bytes_transferred * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation of * the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note After a successful async_read_until operation, the streambuf may * contain additional data beyond the delimiter. An application will typically * leave that data in the streambuf for a subsequent async_read_until operation * to examine. * * @par Example * To asynchronously read data into a streambuf until a newline is encountered: * @code asio::streambuf b; * ... * void handler(const asio::error_code& e, std::size_t size) * { * if (!e) * { * std::istream is(&b); * std::string line; * std::getline(is, line); * ... * } * } * ... * asio::async_read_until(s, b, "\r\n", handler); @endcode * After the @c async_read_until operation completes successfully, the buffer * @c b contains the delimiter: * @code { 'a', 'b', ..., 'c', '\r', '\n', 'd', 'e', ... } @endcode * The call to @c std::getline then extracts the data up to and including the * delimiter, so that the string @c line contains: * @code { 'a', 'b', ..., 'c', '\r', '\n' } @endcode * The remaining data is left in the buffer @c b as follows: * @code { 'd', 'e', ... } @endcode * This data may be the start of a new line, to be extracted by a subsequent * @c async_read_until operation. */ template void async_read_until(AsyncReadStream& s, asio::basic_streambuf& b, const std::string& delim, ReadHandler handler); /// Start an asynchronous operation to read data into a streambuf until some /// part of its data matches a regular expression. /** * This function is used to asynchronously read data into the specified * streambuf until the streambuf's get area contains some data that matches a * regular expression. The function call always returns immediately. The * asynchronous operation will continue until one of the following conditions * is true: * * @li A substring of the streambuf's get area matches the regular expression. * * @li An error occurred. * * This operation is implemented in terms of zero or more calls to the stream's * async_read_some function, and is known as a composed operation. If * the streambuf's get area already contains data that matches the regular * expression, this asynchronous operation completes immediately. The program * must ensure that the stream performs no other read operations (such as * async_read, async_read_until, the stream's async_read_some function, or any * other composed operations that perform reads) until this operation * completes. * * @param s The stream from which the data is to be read. The type must support * the AsyncReadStream concept. * * @param b A streambuf object into which the data will be read. Ownership of * the streambuf is retained by the caller, which must guarantee that it remains * valid until the handler is called. * * @param expr The regular expression. * * @param handler The handler to be called when the read operation completes. * Copies will be made of the handler as required. The function signature of the * handler must be: * @code void handler( * // Result of operation. * const asio::error_code& error, * * // The number of bytes in the streambuf's get * // area up to and including the substring * // that matches the regular. expression. * // 0 if an error occurred. * std::size_t bytes_transferred * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation of * the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note After a successful async_read_until operation, the streambuf may * contain additional data beyond that which matched the regular expression. An * application will typically leave that data in the streambuf for a subsequent * async_read_until operation to examine. * * @par Example * To asynchronously read data into a streambuf until a CR-LF sequence is * encountered: * @code asio::streambuf b; * ... * void handler(const asio::error_code& e, std::size_t size) * { * if (!e) * { * std::istream is(&b); * std::string line; * std::getline(is, line); * ... * } * } * ... * asio::async_read_until(s, b, boost::regex("\r\n"), handler); @endcode * After the @c async_read_until operation completes successfully, the buffer * @c b contains the data which matched the regular expression: * @code { 'a', 'b', ..., 'c', '\r', '\n', 'd', 'e', ... } @endcode * The call to @c std::getline then extracts the data up to and including the * match, so that the string @c line contains: * @code { 'a', 'b', ..., 'c', '\r', '\n' } @endcode * The remaining data is left in the buffer @c b as follows: * @code { 'd', 'e', ... } @endcode * This data may be the start of a new line, to be extracted by a subsequent * @c async_read_until operation. */ template void async_read_until(AsyncReadStream& s, asio::basic_streambuf& b, const boost::regex& expr, ReadHandler handler); /// Start an asynchronous operation to read data into a streambuf until a /// function object indicates a match. /** * This function is used to asynchronously read data into the specified * streambuf until a user-defined match condition function object, when applied * to the data contained in the streambuf, indicates a successful match. The * function call always returns immediately. The asynchronous operation will * continue until one of the following conditions is true: * * @li The match condition function object returns a std::pair where the second * element evaluates to true. * * @li An error occurred. * * This operation is implemented in terms of zero or more calls to the stream's * async_read_some function, and is known as a composed operation. If * the match condition function object already indicates a match, this * asynchronous operation completes immediately. The program must ensure that * the stream performs no other read operations (such as async_read, * async_read_until, the stream's async_read_some function, or any other * composed operations that perform reads) until this operation completes. * * @param s The stream from which the data is to be read. The type must support * the AsyncReadStream concept. * * @param b A streambuf object into which the data will be read. * * @param match_condition The function object to be called to determine whether * a match exists. The signature of the function object must be: * @code pair match_condition(iterator begin, iterator end); * @endcode * where @c iterator represents the type: * @code buffers_iterator::const_buffers_type> * @endcode * The iterator parameters @c begin and @c end define the range of bytes to be * scanned to determine whether there is a match. The @c first member of the * return value is an iterator marking one-past-the-end of the bytes that have * been consumed by the match function. This iterator is used to calculate the * @c begin parameter for any subsequent invocation of the match condition. The * @c second member of the return value is true if a match has been found, false * otherwise. * * @param handler The handler to be called when the read operation completes. * Copies will be made of the handler as required. The function signature of the * handler must be: * @code void handler( * // Result of operation. * const asio::error_code& error, * * // The number of bytes in the streambuf's get * // area that have been fully consumed by the * // match function. O if an error occurred. * std::size_t bytes_transferred * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation of * the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note After a successful async_read_until operation, the streambuf may * contain additional data beyond that which matched the function object. An * application will typically leave that data in the streambuf for a subsequent * async_read_until operation to examine. * * @note The default implementation of the @c is_match_condition type trait * evaluates to true for function pointers and function objects with a * @c result_type typedef. It must be specialised for other user-defined * function objects. * * @par Examples * To asynchronously read data into a streambuf until whitespace is encountered: * @code typedef asio::buffers_iterator< * asio::streambuf::const_buffers_type> iterator; * * std::pair * match_whitespace(iterator begin, iterator end) * { * iterator i = begin; * while (i != end) * if (std::isspace(*i++)) * return std::make_pair(i, true); * return std::make_pair(i, false); * } * ... * void handler(const asio::error_code& e, std::size_t size); * ... * asio::streambuf b; * asio::async_read_until(s, b, match_whitespace, handler); * @endcode * * To asynchronously read data into a streambuf until a matching character is * found: * @code class match_char * { * public: * explicit match_char(char c) : c_(c) {} * * template * std::pair operator()( * Iterator begin, Iterator end) const * { * Iterator i = begin; * while (i != end) * if (c_ == *i++) * return std::make_pair(i, true); * return std::make_pair(i, false); * } * * private: * char c_; * }; * * namespace asio { * template <> struct is_match_condition * : public boost::true_type {}; * } // namespace asio * ... * void handler(const asio::error_code& e, std::size_t size); * ... * asio::streambuf b; * asio::async_read_until(s, b, match_char('a'), handler); * @endcode */ template void async_read_until(AsyncReadStream& s, asio::basic_streambuf& b, MatchCondition match_condition, ReadHandler handler, typename boost::enable_if >::type* = 0); /*@}*/ } // namespace asio #include "asio/detail/pop_options.hpp" #include "asio/impl/read_until.hpp" #endif // !defined(BOOST_NO_IOSTREAM) #endif // ASIO_READ_UNTIL_HPP percona-xtradb-cluster-galera/asio/asio/serial_port.hpp0000644000000000000000000000164112247075736023576 0ustar rootroot00000000000000// // serial_port.hpp // ~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_SERIAL_PORT_HPP #define ASIO_SERIAL_PORT_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_SERIAL_PORT) \ || defined(GENERATING_DOCUMENTATION) #include "asio/basic_serial_port.hpp" namespace asio { /// Typedef for the typical usage of a serial port. typedef basic_serial_port<> serial_port; } // namespace asio #endif // defined(ASIO_HAS_SERIAL_PORT) // || defined(GENERATING_DOCUMENTATION) #endif // ASIO_SERIAL_PORT_HPP percona-xtradb-cluster-galera/asio/asio/serial_port_base.hpp0000644000000000000000000001114512247075736024570 0ustar rootroot00000000000000// // serial_port_base.hpp // ~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_SERIAL_PORT_BASE_HPP #define ASIO_SERIAL_PORT_BASE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_SERIAL_PORT) \ || defined(GENERATING_DOCUMENTATION) #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) # include #endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) #include #include "asio/detail/socket_types.hpp" #include "asio/error_code.hpp" #if defined(GENERATING_DOCUMENTATION) # define ASIO_OPTION_STORAGE implementation_defined #elif defined(BOOST_WINDOWS) || defined(__CYGWIN__) # define ASIO_OPTION_STORAGE DCB #else # define ASIO_OPTION_STORAGE termios #endif #include "asio/detail/push_options.hpp" namespace asio { /// The serial_port_base class is used as a base for the basic_serial_port class /// template so that we have a common place to define the serial port options. class serial_port_base { public: /// Serial port option to permit changing the baud rate. /** * Implements changing the baud rate for a given serial port. */ class baud_rate { public: explicit baud_rate(unsigned int rate = 0); unsigned int value() const; ASIO_DECL asio::error_code store( ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const; ASIO_DECL asio::error_code load( const ASIO_OPTION_STORAGE& storage, asio::error_code& ec); private: unsigned int value_; }; /// Serial port option to permit changing the flow control. /** * Implements changing the flow control for a given serial port. */ class flow_control { public: enum type { none, software, hardware }; ASIO_DECL explicit flow_control(type t = none); type value() const; ASIO_DECL asio::error_code store( ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const; ASIO_DECL asio::error_code load( const ASIO_OPTION_STORAGE& storage, asio::error_code& ec); private: type value_; }; /// Serial port option to permit changing the parity. /** * Implements changing the parity for a given serial port. */ class parity { public: enum type { none, odd, even }; ASIO_DECL explicit parity(type t = none); type value() const; ASIO_DECL asio::error_code store( ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const; ASIO_DECL asio::error_code load( const ASIO_OPTION_STORAGE& storage, asio::error_code& ec); private: type value_; }; /// Serial port option to permit changing the number of stop bits. /** * Implements changing the number of stop bits for a given serial port. */ class stop_bits { public: enum type { one, onepointfive, two }; ASIO_DECL explicit stop_bits(type t = one); type value() const; ASIO_DECL asio::error_code store( ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const; ASIO_DECL asio::error_code load( const ASIO_OPTION_STORAGE& storage, asio::error_code& ec); private: type value_; }; /// Serial port option to permit changing the character size. /** * Implements changing the character size for a given serial port. */ class character_size { public: ASIO_DECL explicit character_size(unsigned int t = 8); unsigned int value() const; ASIO_DECL asio::error_code store( ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const; ASIO_DECL asio::error_code load( const ASIO_OPTION_STORAGE& storage, asio::error_code& ec); private: unsigned int value_; }; protected: /// Protected destructor to prevent deletion through this type. ~serial_port_base() { } #if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) private: // Workaround to enable the empty base optimisation with Borland C++. char dummy_; #endif }; } // namespace asio #include "asio/detail/pop_options.hpp" #undef ASIO_OPTION_STORAGE #include "asio/impl/serial_port_base.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/impl/serial_port_base.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // defined(ASIO_HAS_SERIAL_PORT) // || defined(GENERATING_DOCUMENTATION) #endif // ASIO_SERIAL_PORT_BASE_HPP percona-xtradb-cluster-galera/asio/asio/serial_port_service.hpp0000644000000000000000000001316512247075736025322 0ustar rootroot00000000000000// // serial_port_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_SERIAL_PORT_SERVICE_HPP #define ASIO_SERIAL_PORT_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_SERIAL_PORT) \ || defined(GENERATING_DOCUMENTATION) #include #include #include "asio/detail/reactive_serial_port_service.hpp" #include "asio/detail/win_iocp_serial_port_service.hpp" #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/serial_port_base.hpp" #include "asio/detail/push_options.hpp" namespace asio { /// Default service implementation for a serial port. class serial_port_service #if defined(GENERATING_DOCUMENTATION) : public asio::io_service::service #else : public asio::detail::service_base #endif { public: #if defined(GENERATING_DOCUMENTATION) /// The unique service identifier. static asio::io_service::id id; #endif private: // The type of the platform-specific implementation. #if defined(ASIO_HAS_IOCP) typedef detail::win_iocp_serial_port_service service_impl_type; #else typedef detail::reactive_serial_port_service service_impl_type; #endif public: /// The type of a serial port implementation. #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined implementation_type; #else typedef service_impl_type::implementation_type implementation_type; #endif /// The native handle type. #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined native_type; #else typedef service_impl_type::native_type native_type; #endif /// Construct a new serial port service for the specified io_service. explicit serial_port_service(asio::io_service& io_service) : asio::detail::service_base(io_service), service_impl_(io_service) { } /// Destroy all user-defined handler objects owned by the service. void shutdown_service() { service_impl_.shutdown_service(); } /// Construct a new serial port implementation. void construct(implementation_type& impl) { service_impl_.construct(impl); } /// Destroy a serial port implementation. void destroy(implementation_type& impl) { service_impl_.destroy(impl); } /// Open a serial port. asio::error_code open(implementation_type& impl, const std::string& device, asio::error_code& ec) { return service_impl_.open(impl, device, ec); } /// Assign an existing native handle to a serial port. asio::error_code assign(implementation_type& impl, const native_type& native_handle, asio::error_code& ec) { return service_impl_.assign(impl, native_handle, ec); } /// Determine whether the handle is open. bool is_open(const implementation_type& impl) const { return service_impl_.is_open(impl); } /// Close a serial port implementation. asio::error_code close(implementation_type& impl, asio::error_code& ec) { return service_impl_.close(impl, ec); } /// Get the native handle implementation. native_type native(implementation_type& impl) { return service_impl_.native(impl); } /// Cancel all asynchronous operations associated with the handle. asio::error_code cancel(implementation_type& impl, asio::error_code& ec) { return service_impl_.cancel(impl, ec); } /// Set a serial port option. template asio::error_code set_option(implementation_type& impl, const SettableSerialPortOption& option, asio::error_code& ec) { return service_impl_.set_option(impl, option, ec); } /// Get a serial port option. template asio::error_code get_option(const implementation_type& impl, GettableSerialPortOption& option, asio::error_code& ec) const { return service_impl_.get_option(impl, option, ec); } /// Send a break sequence to the serial port. asio::error_code send_break(implementation_type& impl, asio::error_code& ec) { return service_impl_.send_break(impl, ec); } /// Write the given data to the stream. template std::size_t write_some(implementation_type& impl, const ConstBufferSequence& buffers, asio::error_code& ec) { return service_impl_.write_some(impl, buffers, ec); } /// Start an asynchronous write. template void async_write_some(implementation_type& impl, const ConstBufferSequence& buffers, WriteHandler handler) { service_impl_.async_write_some(impl, buffers, handler); } /// Read some data from the stream. template std::size_t read_some(implementation_type& impl, const MutableBufferSequence& buffers, asio::error_code& ec) { return service_impl_.read_some(impl, buffers, ec); } /// Start an asynchronous read. template void async_read_some(implementation_type& impl, const MutableBufferSequence& buffers, ReadHandler handler) { service_impl_.async_read_some(impl, buffers, handler); } private: // The platform-specific implementation. service_impl_type service_impl_; }; } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_SERIAL_PORT) // || defined(GENERATING_DOCUMENTATION) #endif // ASIO_SERIAL_PORT_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/socket_acceptor_service.hpp0000644000000000000000000001412112247075736026140 0ustar rootroot00000000000000// // socket_acceptor_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_SOCKET_ACCEPTOR_SERVICE_HPP #define ASIO_SOCKET_ACCEPTOR_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/basic_socket.hpp" #include "asio/error.hpp" #include "asio/io_service.hpp" #if defined(ASIO_HAS_IOCP) # include "asio/detail/win_iocp_socket_service.hpp" #else # include "asio/detail/reactive_socket_service.hpp" #endif #include "asio/detail/push_options.hpp" namespace asio { /// Default service implementation for a socket acceptor. template class socket_acceptor_service #if defined(GENERATING_DOCUMENTATION) : public asio::io_service::service #else : public asio::detail::service_base > #endif { public: #if defined(GENERATING_DOCUMENTATION) /// The unique service identifier. static asio::io_service::id id; #endif /// The protocol type. typedef Protocol protocol_type; /// The endpoint type. typedef typename protocol_type::endpoint endpoint_type; private: // The type of the platform-specific implementation. #if defined(ASIO_HAS_IOCP) typedef detail::win_iocp_socket_service service_impl_type; #else typedef detail::reactive_socket_service service_impl_type; #endif public: /// The native type of the socket acceptor. #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined implementation_type; #else typedef typename service_impl_type::implementation_type implementation_type; #endif /// The native acceptor type. #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined native_type; #else typedef typename service_impl_type::native_type native_type; #endif /// Construct a new socket acceptor service for the specified io_service. explicit socket_acceptor_service(asio::io_service& io_service) : asio::detail::service_base< socket_acceptor_service >(io_service), service_impl_(io_service) { } /// Destroy all user-defined handler objects owned by the service. void shutdown_service() { service_impl_.shutdown_service(); } /// Construct a new socket acceptor implementation. void construct(implementation_type& impl) { service_impl_.construct(impl); } /// Destroy a socket acceptor implementation. void destroy(implementation_type& impl) { service_impl_.destroy(impl); } /// Open a new socket acceptor implementation. asio::error_code open(implementation_type& impl, const protocol_type& protocol, asio::error_code& ec) { return service_impl_.open(impl, protocol, ec); } /// Assign an existing native acceptor to a socket acceptor. asio::error_code assign(implementation_type& impl, const protocol_type& protocol, const native_type& native_acceptor, asio::error_code& ec) { return service_impl_.assign(impl, protocol, native_acceptor, ec); } /// Determine whether the acceptor is open. bool is_open(const implementation_type& impl) const { return service_impl_.is_open(impl); } /// Cancel all asynchronous operations associated with the acceptor. asio::error_code cancel(implementation_type& impl, asio::error_code& ec) { return service_impl_.cancel(impl, ec); } /// Bind the socket acceptor to the specified local endpoint. asio::error_code bind(implementation_type& impl, const endpoint_type& endpoint, asio::error_code& ec) { return service_impl_.bind(impl, endpoint, ec); } /// Place the socket acceptor into the state where it will listen for new /// connections. asio::error_code listen(implementation_type& impl, int backlog, asio::error_code& ec) { return service_impl_.listen(impl, backlog, ec); } /// Close a socket acceptor implementation. asio::error_code close(implementation_type& impl, asio::error_code& ec) { return service_impl_.close(impl, ec); } /// Get the native acceptor implementation. native_type native(implementation_type& impl) { return service_impl_.native(impl); } /// Set a socket option. template asio::error_code set_option(implementation_type& impl, const SettableSocketOption& option, asio::error_code& ec) { return service_impl_.set_option(impl, option, ec); } /// Get a socket option. template asio::error_code get_option(const implementation_type& impl, GettableSocketOption& option, asio::error_code& ec) const { return service_impl_.get_option(impl, option, ec); } /// Perform an IO control command on the socket. template asio::error_code io_control(implementation_type& impl, IoControlCommand& command, asio::error_code& ec) { return service_impl_.io_control(impl, command, ec); } /// Get the local endpoint. endpoint_type local_endpoint(const implementation_type& impl, asio::error_code& ec) const { return service_impl_.local_endpoint(impl, ec); } /// Accept a new connection. template asio::error_code accept(implementation_type& impl, basic_socket& peer, endpoint_type* peer_endpoint, asio::error_code& ec) { return service_impl_.accept(impl, peer, peer_endpoint, ec); } /// Start an asynchronous accept. template void async_accept(implementation_type& impl, basic_socket& peer, endpoint_type* peer_endpoint, AcceptHandler handler) { service_impl_.async_accept(impl, peer, peer_endpoint, handler); } private: // The platform-specific implementation. service_impl_type service_impl_; }; } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_SOCKET_ACCEPTOR_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/socket_base.hpp0000644000000000000000000003277012247075736023544 0ustar rootroot00000000000000// // socket_base.hpp // ~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_SOCKET_BASE_HPP #define ASIO_SOCKET_BASE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/detail/io_control.hpp" #include "asio/detail/socket_option.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/push_options.hpp" namespace asio { /// The socket_base class is used as a base for the basic_stream_socket and /// basic_datagram_socket class templates so that we have a common place to /// define the shutdown_type and enum. class socket_base { public: /// Different ways a socket may be shutdown. enum shutdown_type { #if defined(GENERATING_DOCUMENTATION) /// Shutdown the receive side of the socket. shutdown_receive = implementation_defined, /// Shutdown the send side of the socket. shutdown_send = implementation_defined, /// Shutdown both send and receive on the socket. shutdown_both = implementation_defined #else shutdown_receive = asio::detail::shutdown_receive, shutdown_send = asio::detail::shutdown_send, shutdown_both = asio::detail::shutdown_both #endif }; /// Bitmask type for flags that can be passed to send and receive operations. typedef int message_flags; #if defined(GENERATING_DOCUMENTATION) /// Peek at incoming data without removing it from the input queue. static const int message_peek = implementation_defined; /// Process out-of-band data. static const int message_out_of_band = implementation_defined; /// Specify that the data should not be subject to routing. static const int message_do_not_route = implementation_defined; #else BOOST_STATIC_CONSTANT(int, message_peek = asio::detail::message_peek); BOOST_STATIC_CONSTANT(int, message_out_of_band = asio::detail::message_out_of_band); BOOST_STATIC_CONSTANT(int, message_do_not_route = asio::detail::message_do_not_route); #endif /// Socket option to permit sending of broadcast messages. /** * Implements the SOL_SOCKET/SO_BROADCAST socket option. * * @par Examples * Setting the option: * @code * asio::ip::udp::socket socket(io_service); * ... * asio::socket_base::broadcast option(true); * socket.set_option(option); * @endcode * * @par * Getting the current option value: * @code * asio::ip::udp::socket socket(io_service); * ... * asio::socket_base::broadcast option; * socket.get_option(option); * bool is_set = option.value(); * @endcode * * @par Concepts: * Socket_Option, Boolean_Socket_Option. */ #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined broadcast; #else typedef asio::detail::socket_option::boolean< SOL_SOCKET, SO_BROADCAST> broadcast; #endif /// Socket option to enable socket-level debugging. /** * Implements the SOL_SOCKET/SO_DEBUG socket option. * * @par Examples * Setting the option: * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::socket_base::debug option(true); * socket.set_option(option); * @endcode * * @par * Getting the current option value: * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::socket_base::debug option; * socket.get_option(option); * bool is_set = option.value(); * @endcode * * @par Concepts: * Socket_Option, Boolean_Socket_Option. */ #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined debug; #else typedef asio::detail::socket_option::boolean< SOL_SOCKET, SO_DEBUG> debug; #endif /// Socket option to prevent routing, use local interfaces only. /** * Implements the SOL_SOCKET/SO_DONTROUTE socket option. * * @par Examples * Setting the option: * @code * asio::ip::udp::socket socket(io_service); * ... * asio::socket_base::do_not_route option(true); * socket.set_option(option); * @endcode * * @par * Getting the current option value: * @code * asio::ip::udp::socket socket(io_service); * ... * asio::socket_base::do_not_route option; * socket.get_option(option); * bool is_set = option.value(); * @endcode * * @par Concepts: * Socket_Option, Boolean_Socket_Option. */ #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined do_not_route; #else typedef asio::detail::socket_option::boolean< SOL_SOCKET, SO_DONTROUTE> do_not_route; #endif /// Socket option to send keep-alives. /** * Implements the SOL_SOCKET/SO_KEEPALIVE socket option. * * @par Examples * Setting the option: * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::socket_base::keep_alive option(true); * socket.set_option(option); * @endcode * * @par * Getting the current option value: * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::socket_base::keep_alive option; * socket.get_option(option); * bool is_set = option.value(); * @endcode * * @par Concepts: * Socket_Option, Boolean_Socket_Option. */ #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined keep_alive; #else typedef asio::detail::socket_option::boolean< SOL_SOCKET, SO_KEEPALIVE> keep_alive; #endif /// Socket option for the send buffer size of a socket. /** * Implements the SOL_SOCKET/SO_SNDBUF socket option. * * @par Examples * Setting the option: * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::socket_base::send_buffer_size option(8192); * socket.set_option(option); * @endcode * * @par * Getting the current option value: * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::socket_base::send_buffer_size option; * socket.get_option(option); * int size = option.value(); * @endcode * * @par Concepts: * Socket_Option, Integer_Socket_Option. */ #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined send_buffer_size; #else typedef asio::detail::socket_option::integer< SOL_SOCKET, SO_SNDBUF> send_buffer_size; #endif /// Socket option for the send low watermark. /** * Implements the SOL_SOCKET/SO_SNDLOWAT socket option. * * @par Examples * Setting the option: * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::socket_base::send_low_watermark option(1024); * socket.set_option(option); * @endcode * * @par * Getting the current option value: * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::socket_base::send_low_watermark option; * socket.get_option(option); * int size = option.value(); * @endcode * * @par Concepts: * Socket_Option, Integer_Socket_Option. */ #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined send_low_watermark; #else typedef asio::detail::socket_option::integer< SOL_SOCKET, SO_SNDLOWAT> send_low_watermark; #endif /// Socket option for the receive buffer size of a socket. /** * Implements the SOL_SOCKET/SO_RCVBUF socket option. * * @par Examples * Setting the option: * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::socket_base::receive_buffer_size option(8192); * socket.set_option(option); * @endcode * * @par * Getting the current option value: * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::socket_base::receive_buffer_size option; * socket.get_option(option); * int size = option.value(); * @endcode * * @par Concepts: * Socket_Option, Integer_Socket_Option. */ #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined receive_buffer_size; #else typedef asio::detail::socket_option::integer< SOL_SOCKET, SO_RCVBUF> receive_buffer_size; #endif /// Socket option for the receive low watermark. /** * Implements the SOL_SOCKET/SO_RCVLOWAT socket option. * * @par Examples * Setting the option: * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::socket_base::receive_low_watermark option(1024); * socket.set_option(option); * @endcode * * @par * Getting the current option value: * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::socket_base::receive_low_watermark option; * socket.get_option(option); * int size = option.value(); * @endcode * * @par Concepts: * Socket_Option, Integer_Socket_Option. */ #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined receive_low_watermark; #else typedef asio::detail::socket_option::integer< SOL_SOCKET, SO_RCVLOWAT> receive_low_watermark; #endif /// Socket option to allow the socket to be bound to an address that is /// already in use. /** * Implements the SOL_SOCKET/SO_REUSEADDR socket option. * * @par Examples * Setting the option: * @code * asio::ip::tcp::acceptor acceptor(io_service); * ... * asio::socket_base::reuse_address option(true); * acceptor.set_option(option); * @endcode * * @par * Getting the current option value: * @code * asio::ip::tcp::acceptor acceptor(io_service); * ... * asio::socket_base::reuse_address option; * acceptor.get_option(option); * bool is_set = option.value(); * @endcode * * @par Concepts: * Socket_Option, Boolean_Socket_Option. */ #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined reuse_address; #else typedef asio::detail::socket_option::boolean< SOL_SOCKET, SO_REUSEADDR> reuse_address; #endif /// Socket option to specify whether the socket lingers on close if unsent /// data is present. /** * Implements the SOL_SOCKET/SO_LINGER socket option. * * @par Examples * Setting the option: * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::socket_base::linger option(true, 30); * socket.set_option(option); * @endcode * * @par * Getting the current option value: * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::socket_base::linger option; * socket.get_option(option); * bool is_set = option.enabled(); * unsigned short timeout = option.timeout(); * @endcode * * @par Concepts: * Socket_Option, Linger_Socket_Option. */ #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined linger; #else typedef asio::detail::socket_option::linger< SOL_SOCKET, SO_LINGER> linger; #endif /// Socket option to report aborted connections on accept. /** * Implements a custom socket option that determines whether or not an accept * operation is permitted to fail with asio::error::connection_aborted. * By default the option is false. * * @par Examples * Setting the option: * @code * asio::ip::tcp::acceptor acceptor(io_service); * ... * asio::socket_base::enable_connection_aborted option(true); * acceptor.set_option(option); * @endcode * * @par * Getting the current option value: * @code * asio::ip::tcp::acceptor acceptor(io_service); * ... * asio::socket_base::enable_connection_aborted option; * acceptor.get_option(option); * bool is_set = option.value(); * @endcode * * @par Concepts: * Socket_Option, Boolean_Socket_Option. */ #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined enable_connection_aborted; #else typedef asio::detail::socket_option::boolean< asio::detail::custom_socket_option_level, asio::detail::enable_connection_aborted_option> enable_connection_aborted; #endif /// IO control command to set the blocking mode of the socket. /** * Implements the FIONBIO IO control command. * * @par Example * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::socket_base::non_blocking_io command(true); * socket.io_control(command); * @endcode * * @par Concepts: * IO_Control_Command, Boolean_IO_Control_Command. */ #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined non_blocking_io; #else typedef asio::detail::io_control::non_blocking_io non_blocking_io; #endif /// IO control command to get the amount of data that can be read without /// blocking. /** * Implements the FIONREAD IO control command. * * @par Example * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::socket_base::bytes_readable command(true); * socket.io_control(command); * std::size_t bytes_readable = command.get(); * @endcode * * @par Concepts: * IO_Control_Command, Size_IO_Control_Command. */ #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined bytes_readable; #else typedef asio::detail::io_control::bytes_readable bytes_readable; #endif /// The maximum length of the queue of pending incoming connections. #if defined(GENERATING_DOCUMENTATION) static const int max_connections = implementation_defined; #else BOOST_STATIC_CONSTANT(int, max_connections = SOMAXCONN); #endif protected: /// Protected destructor to prevent deletion through this type. ~socket_base() { } #if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) private: // Workaround to enable the empty base optimisation with Borland C++. char dummy_; #endif }; } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_SOCKET_BASE_HPP percona-xtradb-cluster-galera/asio/asio/ssl/0000755000000000000000000000000012247075736021341 5ustar rootroot00000000000000percona-xtradb-cluster-galera/asio/asio/ssl.hpp0000644000000000000000000000127012247075736022052 0ustar rootroot00000000000000// // ssl.hpp // ~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_SSL_HPP #define ASIO_SSL_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/ssl/basic_context.hpp" #include "asio/ssl/context.hpp" #include "asio/ssl/context_base.hpp" #include "asio/ssl/context_service.hpp" #include "asio/ssl/stream.hpp" #include "asio/ssl/stream_base.hpp" #include "asio/ssl/stream_service.hpp" #endif // ASIO_SSL_HPP percona-xtradb-cluster-galera/asio/asio/strand.hpp0000644000000000000000000001662412247075736022555 0ustar rootroot00000000000000// // strand.hpp // ~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_STRAND_HPP #define ASIO_STRAND_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/detail/strand_service.hpp" #include "asio/detail/wrapped_handler.hpp" #include "asio/io_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { /// Provides serialised handler execution. /** * The io_service::strand class provides the ability to post and dispatch * handlers with the guarantee that none of those handlers will execute * concurrently. * * @par Order of handler invocation * Given: * * @li a strand object @c s * * @li an object @c a meeting completion handler requirements * * @li an object @c a1 which is an arbitrary copy of @c a made by the * implementation * * @li an object @c b meeting completion handler requirements * * @li an object @c b1 which is an arbitrary copy of @c b made by the * implementation * * if any of the following conditions are true: * * @li @c s.post(a) happens-before @c s.post(b) * * @li @c s.post(a) happens-before @c s.dispatch(b), where the latter is * performed outside the strand * * @li @c s.dispatch(a) happens-before @c s.post(b), where the former is * performed outside the strand * * @li @c s.dispatch(a) happens-before @c s.dispatch(b), where both are * performed outside the strand * * then @c asio_handler_invoke(a1, &a1) happens-before * @c asio_handler_invoke(b1, &b1). * * Note that in the following case: * @code async_op_1(..., s.wrap(a)); * async_op_2(..., s.wrap(b)); @endcode * the completion of the first async operation will perform @c s.dispatch(a), * and the second will perform @c s.dispatch(b), but the order in which those * are performed is unspecified. That is, you cannot state whether one * happens-before the other. Therefore none of the above conditions are met and * no ordering guarantee is made. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Safe. * * @par Concepts: * Dispatcher. */ class io_service::strand { public: /// Constructor. /** * Constructs the strand. * * @param io_service The io_service object that the strand will use to * dispatch handlers that are ready to be run. */ explicit strand(asio::io_service& io_service) : service_(asio::use_service< asio::detail::strand_service>(io_service)) { service_.construct(impl_); } /// Destructor. /** * Destroys a strand. * * Handlers posted through the strand that have not yet been invoked will * still be dispatched in a way that meets the guarantee of non-concurrency. */ ~strand() { service_.destroy(impl_); } /// (Deprecated: use get_io_service().) Get the io_service associated with /// the strand. /** * This function may be used to obtain the io_service object that the strand * uses to dispatch handlers for asynchronous operations. * * @return A reference to the io_service object that the strand will use to * dispatch handlers. Ownership is not transferred to the caller. */ asio::io_service& io_service() { return service_.get_io_service(); } /// Get the io_service associated with the strand. /** * This function may be used to obtain the io_service object that the strand * uses to dispatch handlers for asynchronous operations. * * @return A reference to the io_service object that the strand will use to * dispatch handlers. Ownership is not transferred to the caller. */ asio::io_service& get_io_service() { return service_.get_io_service(); } /// Request the strand to invoke the given handler. /** * This function is used to ask the strand to execute the given handler. * * The strand object guarantees that handlers posted or dispatched through * the strand will not be executed concurrently. The handler may be executed * inside this function if the guarantee can be met. If this function is * called from within a handler that was posted or dispatched through the same * strand, then the new handler will be executed immediately. * * The strand's guarantee is in addition to the guarantee provided by the * underlying io_service. The io_service guarantees that the handler will only * be called in a thread in which the io_service's run member function is * currently being invoked. * * @param handler The handler to be called. The strand will make a copy of the * handler object as required. The function signature of the handler must be: * @code void handler(); @endcode */ template void dispatch(Handler handler) { service_.dispatch(impl_, handler); } /// Request the strand to invoke the given handler and return /// immediately. /** * This function is used to ask the strand to execute the given handler, but * without allowing the strand to call the handler from inside this function. * * The strand object guarantees that handlers posted or dispatched through * the strand will not be executed concurrently. The strand's guarantee is in * addition to the guarantee provided by the underlying io_service. The * io_service guarantees that the handler will only be called in a thread in * which the io_service's run member function is currently being invoked. * * @param handler The handler to be called. The strand will make a copy of the * handler object as required. The function signature of the handler must be: * @code void handler(); @endcode */ template void post(Handler handler) { service_.post(impl_, handler); } /// Create a new handler that automatically dispatches the wrapped handler /// on the strand. /** * This function is used to create a new handler function object that, when * invoked, will automatically pass the wrapped handler to the strand's * dispatch function. * * @param handler The handler to be wrapped. The strand will make a copy of * the handler object as required. The function signature of the handler must * be: @code void handler(A1 a1, ... An an); @endcode * * @return A function object that, when invoked, passes the wrapped handler to * the strand's dispatch function. Given a function object with the signature: * @code R f(A1 a1, ... An an); @endcode * If this function object is passed to the wrap function like so: * @code strand.wrap(f); @endcode * then the return value is a function object with the signature * @code void g(A1 a1, ... An an); @endcode * that, when invoked, executes code equivalent to: * @code strand.dispatch(boost::bind(f, a1, ... an)); @endcode */ template #if defined(GENERATING_DOCUMENTATION) unspecified #else detail::wrapped_handler #endif wrap(Handler handler) { return detail::wrapped_handler(*this, handler); } private: asio::detail::strand_service& service_; asio::detail::strand_service::implementation_type impl_; }; /// Typedef for backwards compatibility. typedef asio::io_service::strand strand; } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_STRAND_HPP percona-xtradb-cluster-galera/asio/asio/stream_socket_service.hpp0000644000000000000000000001737212247075736025646 0ustar rootroot00000000000000// // stream_socket_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_STREAM_SOCKET_SERVICE_HPP #define ASIO_STREAM_SOCKET_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/error.hpp" #include "asio/io_service.hpp" #if defined(ASIO_HAS_IOCP) # include "asio/detail/win_iocp_socket_service.hpp" #else # include "asio/detail/reactive_socket_service.hpp" #endif #include "asio/detail/push_options.hpp" namespace asio { /// Default service implementation for a stream socket. template class stream_socket_service #if defined(GENERATING_DOCUMENTATION) : public asio::io_service::service #else : public asio::detail::service_base > #endif { public: #if defined(GENERATING_DOCUMENTATION) /// The unique service identifier. static asio::io_service::id id; #endif /// The protocol type. typedef Protocol protocol_type; /// The endpoint type. typedef typename Protocol::endpoint endpoint_type; private: // The type of the platform-specific implementation. #if defined(ASIO_HAS_IOCP) typedef detail::win_iocp_socket_service service_impl_type; #else typedef detail::reactive_socket_service service_impl_type; #endif public: /// The type of a stream socket implementation. #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined implementation_type; #else typedef typename service_impl_type::implementation_type implementation_type; #endif /// The native socket type. #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined native_type; #else typedef typename service_impl_type::native_type native_type; #endif /// Construct a new stream socket service for the specified io_service. explicit stream_socket_service(asio::io_service& io_service) : asio::detail::service_base< stream_socket_service >(io_service), service_impl_(io_service) { } /// Destroy all user-defined handler objects owned by the service. void shutdown_service() { service_impl_.shutdown_service(); } /// Construct a new stream socket implementation. void construct(implementation_type& impl) { service_impl_.construct(impl); } /// Destroy a stream socket implementation. void destroy(implementation_type& impl) { service_impl_.destroy(impl); } /// Open a stream socket. asio::error_code open(implementation_type& impl, const protocol_type& protocol, asio::error_code& ec) { if (protocol.type() == SOCK_STREAM) service_impl_.open(impl, protocol, ec); else ec = asio::error::invalid_argument; return ec; } /// Assign an existing native socket to a stream socket. asio::error_code assign(implementation_type& impl, const protocol_type& protocol, const native_type& native_socket, asio::error_code& ec) { return service_impl_.assign(impl, protocol, native_socket, ec); } /// Determine whether the socket is open. bool is_open(const implementation_type& impl) const { return service_impl_.is_open(impl); } /// Close a stream socket implementation. asio::error_code close(implementation_type& impl, asio::error_code& ec) { return service_impl_.close(impl, ec); } /// Get the native socket implementation. native_type native(implementation_type& impl) { return service_impl_.native(impl); } /// Cancel all asynchronous operations associated with the socket. asio::error_code cancel(implementation_type& impl, asio::error_code& ec) { return service_impl_.cancel(impl, ec); } /// Determine whether the socket is at the out-of-band data mark. bool at_mark(const implementation_type& impl, asio::error_code& ec) const { return service_impl_.at_mark(impl, ec); } /// Determine the number of bytes available for reading. std::size_t available(const implementation_type& impl, asio::error_code& ec) const { return service_impl_.available(impl, ec); } /// Bind the stream socket to the specified local endpoint. asio::error_code bind(implementation_type& impl, const endpoint_type& endpoint, asio::error_code& ec) { return service_impl_.bind(impl, endpoint, ec); } /// Connect the stream socket to the specified endpoint. asio::error_code connect(implementation_type& impl, const endpoint_type& peer_endpoint, asio::error_code& ec) { return service_impl_.connect(impl, peer_endpoint, ec); } /// Start an asynchronous connect. template void async_connect(implementation_type& impl, const endpoint_type& peer_endpoint, ConnectHandler handler) { service_impl_.async_connect(impl, peer_endpoint, handler); } /// Set a socket option. template asio::error_code set_option(implementation_type& impl, const SettableSocketOption& option, asio::error_code& ec) { return service_impl_.set_option(impl, option, ec); } /// Get a socket option. template asio::error_code get_option(const implementation_type& impl, GettableSocketOption& option, asio::error_code& ec) const { return service_impl_.get_option(impl, option, ec); } /// Perform an IO control command on the socket. template asio::error_code io_control(implementation_type& impl, IoControlCommand& command, asio::error_code& ec) { return service_impl_.io_control(impl, command, ec); } /// Get the local endpoint. endpoint_type local_endpoint(const implementation_type& impl, asio::error_code& ec) const { return service_impl_.local_endpoint(impl, ec); } /// Get the remote endpoint. endpoint_type remote_endpoint(const implementation_type& impl, asio::error_code& ec) const { return service_impl_.remote_endpoint(impl, ec); } /// Disable sends or receives on the socket. asio::error_code shutdown(implementation_type& impl, socket_base::shutdown_type what, asio::error_code& ec) { return service_impl_.shutdown(impl, what, ec); } /// Send the given data to the peer. template std::size_t send(implementation_type& impl, const ConstBufferSequence& buffers, socket_base::message_flags flags, asio::error_code& ec) { return service_impl_.send(impl, buffers, flags, ec); } /// Start an asynchronous send. template void async_send(implementation_type& impl, const ConstBufferSequence& buffers, socket_base::message_flags flags, WriteHandler handler) { service_impl_.async_send(impl, buffers, flags, handler); } /// Receive some data from the peer. template std::size_t receive(implementation_type& impl, const MutableBufferSequence& buffers, socket_base::message_flags flags, asio::error_code& ec) { return service_impl_.receive(impl, buffers, flags, ec); } /// Start an asynchronous receive. template void async_receive(implementation_type& impl, const MutableBufferSequence& buffers, socket_base::message_flags flags, ReadHandler handler) { service_impl_.async_receive(impl, buffers, flags, handler); } private: // The platform-specific implementation. service_impl_type service_impl_; }; } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_STREAM_SOCKET_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/streambuf.hpp0000644000000000000000000000135312247075736023243 0ustar rootroot00000000000000// // streambuf.hpp // ~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_STREAMBUF_HPP #define ASIO_STREAMBUF_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(BOOST_NO_IOSTREAM) #include "asio/basic_streambuf.hpp" namespace asio { /// Typedef for the typical usage of basic_streambuf. typedef basic_streambuf<> streambuf; } // namespace asio #endif // !defined(BOOST_NO_IOSTREAM) #endif // ASIO_STREAMBUF_HPP percona-xtradb-cluster-galera/asio/asio/system_error.hpp0000644000000000000000000000475712247075736024023 0ustar rootroot00000000000000// // system_error.hpp // ~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_SYSTEM_ERROR_HPP #define ASIO_SYSTEM_ERROR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include #include #include "asio/error_code.hpp" #include "asio/detail/push_options.hpp" namespace asio { /// The system_error class is used to represent system conditions that /// prevent the library from operating correctly. class system_error : public std::exception { public: /// Construct with an error code. system_error(const error_code& code) : code_(code), context_() { } /// Construct with an error code and context. system_error(const error_code& code, const std::string& context) : code_(code), context_(context) { } /// Copy constructor. system_error(const system_error& other) : std::exception(other), code_(other.code_), context_(other.context_), what_() { } /// Destructor. virtual ~system_error() throw () { } /// Assignment operator. system_error& operator=(const system_error& e) { context_ = e.context_; code_ = e.code_; what_.reset(); return *this; } /// Get a string representation of the exception. virtual const char* what() const throw () { #if !defined(BOOST_NO_EXCEPTIONS) try #endif // !defined(BOOST_NO_EXCEPTIONS) { if (!what_) { std::string tmp(context_); if (tmp.length()) tmp += ": "; tmp += code_.message(); what_.reset(new std::string(tmp)); } return what_->c_str(); } #if !defined(BOOST_NO_EXCEPTIONS) catch (std::exception&) { return "system_error"; } #endif // !defined(BOOST_NO_EXCEPTIONS) } /// Get the error code associated with the exception. error_code code() const { return code_; } private: // The code associated with the error. error_code code_; // The context associated with the error. std::string context_; // The string representation of the error. mutable boost::scoped_ptr what_; }; } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_SYSTEM_ERROR_HPP percona-xtradb-cluster-galera/asio/asio/thread.hpp0000644000000000000000000000440512247075736022523 0ustar rootroot00000000000000// // thread.hpp // ~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_THREAD_HPP #define ASIO_THREAD_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/detail/noncopyable.hpp" #include "asio/detail/thread.hpp" #include "asio/detail/push_options.hpp" namespace asio { /// A simple abstraction for starting threads. /** * The asio::thread class implements the smallest possible subset of the * functionality of boost::thread. It is intended to be used only for starting * a thread and waiting for it to exit. If more extensive threading * capabilities are required, you are strongly advised to use something else. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. * * @par Example * A typical use of asio::thread would be to launch a thread to run an * io_service's event processing loop: * * @par * @code asio::io_service io_service; * // ... * asio::thread t(boost::bind(&asio::io_service::run, &io_service)); * // ... * t.join(); @endcode */ class thread : private noncopyable { public: /// Start a new thread that executes the supplied function. /** * This constructor creates a new thread that will execute the given function * or function object. * * @param f The function or function object to be run in the thread. The * function signature must be: @code void f(); @endcode */ template explicit thread(Function f) : impl_(f) { } /// Destructor. ~thread() { } /// Wait for the thread to exit. /** * This function will block until the thread has exited. * * If this function is not called before the thread object is destroyed, the * thread itself will continue to run until completion. You will, however, * no longer have the ability to wait for it to exit. */ void join() { impl_.join(); } private: detail::thread impl_; }; } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_THREAD_HPP percona-xtradb-cluster-galera/asio/asio/time_traits.hpp0000644000000000000000000000406512247075736023602 0ustar rootroot00000000000000// // time_traits.hpp // ~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_TIME_TRAITS_HPP #define ASIO_TIME_TRAITS_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/socket_types.hpp" // Must come before posix_time. #include "asio/detail/push_options.hpp" #include #include "asio/detail/pop_options.hpp" #include "asio/detail/push_options.hpp" namespace asio { /// Time traits suitable for use with the deadline timer. template struct time_traits; /// Time traits specialised for posix_time. template <> struct time_traits { /// The time type. typedef boost::posix_time::ptime time_type; /// The duration type. typedef boost::posix_time::time_duration duration_type; /// Get the current time. static time_type now() { #if defined(BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK) return boost::posix_time::microsec_clock::universal_time(); #else // defined(BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK) return boost::posix_time::second_clock::universal_time(); #endif // defined(BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK) } /// Add a duration to a time. static time_type add(const time_type& t, const duration_type& d) { return t + d; } /// Subtract one time from another. static duration_type subtract(const time_type& t1, const time_type& t2) { return t1 - t2; } /// Test whether one time is less than another. static bool less_than(const time_type& t1, const time_type& t2) { return t1 < t2; } /// Convert to POSIX duration type. static boost::posix_time::time_duration to_posix_duration( const duration_type& d) { return d; } }; } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_TIME_TRAITS_HPP percona-xtradb-cluster-galera/asio/asio/version.hpp0000644000000000000000000000120312247075736022732 0ustar rootroot00000000000000// // version.hpp // ~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_VERSION_HPP #define ASIO_VERSION_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) // ASIO_VERSION % 100 is the sub-minor version // ASIO_VERSION / 100 % 1000 is the minor version // ASIO_VERSION / 100000 is the major version #define ASIO_VERSION 100408 // 1.4.8 #endif // ASIO_VERSION_HPP percona-xtradb-cluster-galera/asio/asio/windows/0000755000000000000000000000000012247075736022232 5ustar rootroot00000000000000percona-xtradb-cluster-galera/asio/asio/write.hpp0000644000000000000000000005302112247075736022404 0ustar rootroot00000000000000// // write.hpp // ~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_WRITE_HPP #define ASIO_WRITE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/basic_streambuf_fwd.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { /** * @defgroup write asio::write * * @brief Write a certain amount of data to a stream before returning. */ /*@{*/ /// Write all of the supplied data to a stream before returning. /** * This function is used to write a certain number of bytes of data to a stream. * The call will block until one of the following conditions is true: * * @li All of the data in the supplied buffers has been written. That is, the * bytes transferred is equal to the sum of the buffer sizes. * * @li An error occurred. * * This operation is implemented in terms of zero or more calls to the stream's * write_some function. * * @param s The stream to which the data is to be written. The type must support * the SyncWriteStream concept. * * @param buffers One or more buffers containing the data to be written. The sum * of the buffer sizes indicates the maximum number of bytes to write to the * stream. * * @returns The number of bytes transferred. * * @throws asio::system_error Thrown on failure. * * @par Example * To write a single data buffer use the @ref buffer function as follows: * @code asio::write(s, asio::buffer(data, size)); @endcode * See the @ref buffer documentation for information on writing multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. * * @note This overload is equivalent to calling: * @code asio::write( * s, buffers, * asio::transfer_all()); @endcode */ template std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers); /// Write a certain amount of data to a stream before returning. /** * This function is used to write a certain number of bytes of data to a stream. * The call will block until one of the following conditions is true: * * @li All of the data in the supplied buffers has been written. That is, the * bytes transferred is equal to the sum of the buffer sizes. * * @li The completion_condition function object returns 0. * * This operation is implemented in terms of zero or more calls to the stream's * write_some function. * * @param s The stream to which the data is to be written. The type must support * the SyncWriteStream concept. * * @param buffers One or more buffers containing the data to be written. The sum * of the buffer sizes indicates the maximum number of bytes to write to the * stream. * * @param completion_condition The function object to be called to determine * whether the write operation is complete. The signature of the function object * must be: * @code std::size_t completion_condition( * // Result of latest write_some operation. * const asio::error_code& error, * * // Number of bytes transferred so far. * std::size_t bytes_transferred * ); @endcode * A return value of 0 indicates that the write operation is complete. A * non-zero return value indicates the maximum number of bytes to be written on * the next call to the stream's write_some function. * * @returns The number of bytes transferred. * * @throws asio::system_error Thrown on failure. * * @par Example * To write a single data buffer use the @ref buffer function as follows: * @code asio::write(s, asio::buffer(data, size), * asio::transfer_at_least(32)); @endcode * See the @ref buffer documentation for information on writing multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers, CompletionCondition completion_condition); /// Write a certain amount of data to a stream before returning. /** * This function is used to write a certain number of bytes of data to a stream. * The call will block until one of the following conditions is true: * * @li All of the data in the supplied buffers has been written. That is, the * bytes transferred is equal to the sum of the buffer sizes. * * @li The completion_condition function object returns 0. * * This operation is implemented in terms of zero or more calls to the stream's * write_some function. * * @param s The stream to which the data is to be written. The type must support * the SyncWriteStream concept. * * @param buffers One or more buffers containing the data to be written. The sum * of the buffer sizes indicates the maximum number of bytes to write to the * stream. * * @param completion_condition The function object to be called to determine * whether the write operation is complete. The signature of the function object * must be: * @code std::size_t completion_condition( * // Result of latest write_some operation. * const asio::error_code& error, * * // Number of bytes transferred so far. * std::size_t bytes_transferred * ); @endcode * A return value of 0 indicates that the write operation is complete. A * non-zero return value indicates the maximum number of bytes to be written on * the next call to the stream's write_some function. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes written. If an error occurs, returns the total * number of bytes successfully transferred prior to the error. */ template std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers, CompletionCondition completion_condition, asio::error_code& ec); #if !defined(BOOST_NO_IOSTREAM) /// Write all of the supplied data to a stream before returning. /** * This function is used to write a certain number of bytes of data to a stream. * The call will block until one of the following conditions is true: * * @li All of the data in the supplied basic_streambuf has been written. * * @li An error occurred. * * This operation is implemented in terms of zero or more calls to the stream's * write_some function. * * @param s The stream to which the data is to be written. The type must support * the SyncWriteStream concept. * * @param b The basic_streambuf object from which data will be written. * * @returns The number of bytes transferred. * * @throws asio::system_error Thrown on failure. * * @note This overload is equivalent to calling: * @code asio::write( * s, b, * asio::transfer_all()); @endcode */ template std::size_t write(SyncWriteStream& s, basic_streambuf& b); /// Write a certain amount of data to a stream before returning. /** * This function is used to write a certain number of bytes of data to a stream. * The call will block until one of the following conditions is true: * * @li All of the data in the supplied basic_streambuf has been written. * * @li The completion_condition function object returns 0. * * This operation is implemented in terms of zero or more calls to the stream's * write_some function. * * @param s The stream to which the data is to be written. The type must support * the SyncWriteStream concept. * * @param b The basic_streambuf object from which data will be written. * * @param completion_condition The function object to be called to determine * whether the write operation is complete. The signature of the function object * must be: * @code std::size_t completion_condition( * // Result of latest write_some operation. * const asio::error_code& error, * * // Number of bytes transferred so far. * std::size_t bytes_transferred * ); @endcode * A return value of 0 indicates that the write operation is complete. A * non-zero return value indicates the maximum number of bytes to be written on * the next call to the stream's write_some function. * * @returns The number of bytes transferred. * * @throws asio::system_error Thrown on failure. */ template std::size_t write(SyncWriteStream& s, basic_streambuf& b, CompletionCondition completion_condition); /// Write a certain amount of data to a stream before returning. /** * This function is used to write a certain number of bytes of data to a stream. * The call will block until one of the following conditions is true: * * @li All of the data in the supplied basic_streambuf has been written. * * @li The completion_condition function object returns 0. * * This operation is implemented in terms of zero or more calls to the stream's * write_some function. * * @param s The stream to which the data is to be written. The type must support * the SyncWriteStream concept. * * @param b The basic_streambuf object from which data will be written. * * @param completion_condition The function object to be called to determine * whether the write operation is complete. The signature of the function object * must be: * @code std::size_t completion_condition( * // Result of latest write_some operation. * const asio::error_code& error, * * // Number of bytes transferred so far. * std::size_t bytes_transferred * ); @endcode * A return value of 0 indicates that the write operation is complete. A * non-zero return value indicates the maximum number of bytes to be written on * the next call to the stream's write_some function. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes written. If an error occurs, returns the total * number of bytes successfully transferred prior to the error. */ template std::size_t write(SyncWriteStream& s, basic_streambuf& b, CompletionCondition completion_condition, asio::error_code& ec); #endif // !defined(BOOST_NO_IOSTREAM) /*@}*/ /** * @defgroup async_write asio::async_write * * @brief Start an asynchronous operation to write a certain amount of data to a * stream. */ /*@{*/ /// Start an asynchronous operation to write all of the supplied data to a /// stream. /** * This function is used to asynchronously write a certain number of bytes of * data to a stream. The function call always returns immediately. The * asynchronous operation will continue until one of the following conditions * is true: * * @li All of the data in the supplied buffers has been written. That is, the * bytes transferred is equal to the sum of the buffer sizes. * * @li An error occurred. * * This operation is implemented in terms of zero or more calls to the stream's * async_write_some function, and is known as a composed operation. The * program must ensure that the stream performs no other write operations (such * as async_write, the stream's async_write_some function, or any other composed * operations that perform writes) until this operation completes. * * @param s The stream to which the data is to be written. The type must support * the AsyncWriteStream concept. * * @param buffers One or more buffers containing the data to be written. * Although the buffers object may be copied as necessary, ownership of the * underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param handler The handler to be called when the write operation completes. * Copies will be made of the handler as required. The function signature of * the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * * std::size_t bytes_transferred // Number of bytes written from the * // buffers. If an error occurred, * // this will be less than the sum * // of the buffer sizes. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation of * the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @par Example * To write a single data buffer use the @ref buffer function as follows: * @code * asio::async_write(s, asio::buffer(data, size), handler); * @endcode * See the @ref buffer documentation for information on writing multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template void async_write(AsyncWriteStream& s, const ConstBufferSequence& buffers, WriteHandler handler); /// Start an asynchronous operation to write a certain amount of data to a /// stream. /** * This function is used to asynchronously write a certain number of bytes of * data to a stream. The function call always returns immediately. The * asynchronous operation will continue until one of the following conditions * is true: * * @li All of the data in the supplied buffers has been written. That is, the * bytes transferred is equal to the sum of the buffer sizes. * * @li The completion_condition function object returns 0. * * This operation is implemented in terms of zero or more calls to the stream's * async_write_some function, and is known as a composed operation. The * program must ensure that the stream performs no other write operations (such * as async_write, the stream's async_write_some function, or any other composed * operations that perform writes) until this operation completes. * * @param s The stream to which the data is to be written. The type must support * the AsyncWriteStream concept. * * @param buffers One or more buffers containing the data to be written. * Although the buffers object may be copied as necessary, ownership of the * underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param completion_condition The function object to be called to determine * whether the write operation is complete. The signature of the function object * must be: * @code std::size_t completion_condition( * // Result of latest async_write_some operation. * const asio::error_code& error, * * // Number of bytes transferred so far. * std::size_t bytes_transferred * ); @endcode * A return value of 0 indicates that the write operation is complete. A * non-zero return value indicates the maximum number of bytes to be written on * the next call to the stream's async_write_some function. * * @param handler The handler to be called when the write operation completes. * Copies will be made of the handler as required. The function signature of the * handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * * std::size_t bytes_transferred // Number of bytes written from the * // buffers. If an error occurred, * // this will be less than the sum * // of the buffer sizes. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation of * the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @par Example * To write a single data buffer use the @ref buffer function as follows: * @code asio::async_write(s, * asio::buffer(data, size), * asio::transfer_at_least(32), * handler); @endcode * See the @ref buffer documentation for information on writing multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template void async_write(AsyncWriteStream& s, const ConstBufferSequence& buffers, CompletionCondition completion_condition, WriteHandler handler); #if !defined(BOOST_NO_IOSTREAM) /// Start an asynchronous operation to write all of the supplied data to a /// stream. /** * This function is used to asynchronously write a certain number of bytes of * data to a stream. The function call always returns immediately. The * asynchronous operation will continue until one of the following conditions * is true: * * @li All of the data in the supplied basic_streambuf has been written. * * @li An error occurred. * * This operation is implemented in terms of zero or more calls to the stream's * async_write_some function, and is known as a composed operation. The * program must ensure that the stream performs no other write operations (such * as async_write, the stream's async_write_some function, or any other composed * operations that perform writes) until this operation completes. * * @param s The stream to which the data is to be written. The type must support * the AsyncWriteStream concept. * * @param b A basic_streambuf object from which data will be written. Ownership * of the streambuf is retained by the caller, which must guarantee that it * remains valid until the handler is called. * * @param handler The handler to be called when the write operation completes. * Copies will be made of the handler as required. The function signature of the * handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * * std::size_t bytes_transferred // Number of bytes written from the * // buffers. If an error occurred, * // this will be less than the sum * // of the buffer sizes. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation of * the handler will be performed in a manner equivalent to using * asio::io_service::post(). */ template void async_write(AsyncWriteStream& s, basic_streambuf& b, WriteHandler handler); /// Start an asynchronous operation to write a certain amount of data to a /// stream. /** * This function is used to asynchronously write a certain number of bytes of * data to a stream. The function call always returns immediately. The * asynchronous operation will continue until one of the following conditions * is true: * * @li All of the data in the supplied basic_streambuf has been written. * * @li The completion_condition function object returns 0. * * This operation is implemented in terms of zero or more calls to the stream's * async_write_some function, and is known as a composed operation. The * program must ensure that the stream performs no other write operations (such * as async_write, the stream's async_write_some function, or any other composed * operations that perform writes) until this operation completes. * * @param s The stream to which the data is to be written. The type must support * the AsyncWriteStream concept. * * @param b A basic_streambuf object from which data will be written. Ownership * of the streambuf is retained by the caller, which must guarantee that it * remains valid until the handler is called. * * @param completion_condition The function object to be called to determine * whether the write operation is complete. The signature of the function object * must be: * @code std::size_t completion_condition( * // Result of latest async_write_some operation. * const asio::error_code& error, * * // Number of bytes transferred so far. * std::size_t bytes_transferred * ); @endcode * A return value of 0 indicates that the write operation is complete. A * non-zero return value indicates the maximum number of bytes to be written on * the next call to the stream's async_write_some function. * * @param handler The handler to be called when the write operation completes. * Copies will be made of the handler as required. The function signature of the * handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * * std::size_t bytes_transferred // Number of bytes written from the * // buffers. If an error occurred, * // this will be less than the sum * // of the buffer sizes. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation of * the handler will be performed in a manner equivalent to using * asio::io_service::post(). */ template void async_write(AsyncWriteStream& s, basic_streambuf& b, CompletionCondition completion_condition, WriteHandler handler); #endif // !defined(BOOST_NO_IOSTREAM) /*@}*/ } // namespace asio #include "asio/detail/pop_options.hpp" #include "asio/impl/write.hpp" #endif // ASIO_WRITE_HPP percona-xtradb-cluster-galera/asio/asio/write_at.hpp0000644000000000000000000005355212247075736023101 0ustar rootroot00000000000000// // write_at.hpp // ~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_WRITE_AT_HPP #define ASIO_WRITE_AT_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include "asio/basic_streambuf_fwd.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { /** * @defgroup write_at asio::write_at * * @brief Write a certain amount of data at a specified offset before returning. */ /*@{*/ /// Write all of the supplied data at the specified offset before returning. /** * This function is used to write a certain number of bytes of data to a random * access device at a specified offset. The call will block until one of the * following conditions is true: * * @li All of the data in the supplied buffers has been written. That is, the * bytes transferred is equal to the sum of the buffer sizes. * * @li An error occurred. * * This operation is implemented in terms of zero or more calls to the device's * write_some_at function. * * @param d The device to which the data is to be written. The type must support * the SyncRandomAccessWriteDevice concept. * * @param offset The offset at which the data will be written. * * @param buffers One or more buffers containing the data to be written. The sum * of the buffer sizes indicates the maximum number of bytes to write to the * device. * * @returns The number of bytes transferred. * * @throws asio::system_error Thrown on failure. * * @par Example * To write a single data buffer use the @ref buffer function as follows: * @code asio::write_at(d, 42, asio::buffer(data, size)); @endcode * See the @ref buffer documentation for information on writing multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. * * @note This overload is equivalent to calling: * @code asio::write_at( * d, offset, buffers, * asio::transfer_all()); @endcode */ template std::size_t write_at(SyncRandomAccessWriteDevice& d, boost::uint64_t offset, const ConstBufferSequence& buffers); /// Write a certain amount of data at a specified offset before returning. /** * This function is used to write a certain number of bytes of data to a random * access device at a specified offset. The call will block until one of the * following conditions is true: * * @li All of the data in the supplied buffers has been written. That is, the * bytes transferred is equal to the sum of the buffer sizes. * * @li The completion_condition function object returns 0. * * This operation is implemented in terms of zero or more calls to the device's * write_some_at function. * * @param d The device to which the data is to be written. The type must support * the SyncRandomAccessWriteDevice concept. * * @param offset The offset at which the data will be written. * * @param buffers One or more buffers containing the data to be written. The sum * of the buffer sizes indicates the maximum number of bytes to write to the * device. * * @param completion_condition The function object to be called to determine * whether the write operation is complete. The signature of the function object * must be: * @code std::size_t completion_condition( * // Result of latest write_some_at operation. * const asio::error_code& error, * * // Number of bytes transferred so far. * std::size_t bytes_transferred * ); @endcode * A return value of 0 indicates that the write operation is complete. A * non-zero return value indicates the maximum number of bytes to be written on * the next call to the device's write_some_at function. * * @returns The number of bytes transferred. * * @throws asio::system_error Thrown on failure. * * @par Example * To write a single data buffer use the @ref buffer function as follows: * @code asio::write_at(d, 42, asio::buffer(data, size), * asio::transfer_at_least(32)); @endcode * See the @ref buffer documentation for information on writing multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template std::size_t write_at(SyncRandomAccessWriteDevice& d, boost::uint64_t offset, const ConstBufferSequence& buffers, CompletionCondition completion_condition); /// Write a certain amount of data at a specified offset before returning. /** * This function is used to write a certain number of bytes of data to a random * access device at a specified offset. The call will block until one of the * following conditions is true: * * @li All of the data in the supplied buffers has been written. That is, the * bytes transferred is equal to the sum of the buffer sizes. * * @li The completion_condition function object returns 0. * * This operation is implemented in terms of zero or more calls to the device's * write_some_at function. * * @param d The device to which the data is to be written. The type must support * the SyncRandomAccessWriteDevice concept. * * @param offset The offset at which the data will be written. * * @param buffers One or more buffers containing the data to be written. The sum * of the buffer sizes indicates the maximum number of bytes to write to the * device. * * @param completion_condition The function object to be called to determine * whether the write operation is complete. The signature of the function object * must be: * @code std::size_t completion_condition( * // Result of latest write_some_at operation. * const asio::error_code& error, * * // Number of bytes transferred so far. * std::size_t bytes_transferred * ); @endcode * A return value of 0 indicates that the write operation is complete. A * non-zero return value indicates the maximum number of bytes to be written on * the next call to the device's write_some_at function. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes written. If an error occurs, returns the total * number of bytes successfully transferred prior to the error. */ template std::size_t write_at(SyncRandomAccessWriteDevice& d, boost::uint64_t offset, const ConstBufferSequence& buffers, CompletionCondition completion_condition, asio::error_code& ec); #if !defined(BOOST_NO_IOSTREAM) /// Write all of the supplied data at the specified offset before returning. /** * This function is used to write a certain number of bytes of data to a random * access device at a specified offset. The call will block until one of the * following conditions is true: * * @li All of the data in the supplied basic_streambuf has been written. * * @li An error occurred. * * This operation is implemented in terms of zero or more calls to the device's * write_some_at function. * * @param d The device to which the data is to be written. The type must support * the SyncRandomAccessWriteDevice concept. * * @param offset The offset at which the data will be written. * * @param b The basic_streambuf object from which data will be written. * * @returns The number of bytes transferred. * * @throws asio::system_error Thrown on failure. * * @note This overload is equivalent to calling: * @code asio::write_at( * d, 42, b, * asio::transfer_all()); @endcode */ template std::size_t write_at(SyncRandomAccessWriteDevice& d, boost::uint64_t offset, basic_streambuf& b); /// Write a certain amount of data at a specified offset before returning. /** * This function is used to write a certain number of bytes of data to a random * access device at a specified offset. The call will block until one of the * following conditions is true: * * @li All of the data in the supplied basic_streambuf has been written. * * @li The completion_condition function object returns 0. * * This operation is implemented in terms of zero or more calls to the device's * write_some_at function. * * @param d The device to which the data is to be written. The type must support * the SyncRandomAccessWriteDevice concept. * * @param offset The offset at which the data will be written. * * @param b The basic_streambuf object from which data will be written. * * @param completion_condition The function object to be called to determine * whether the write operation is complete. The signature of the function object * must be: * @code std::size_t completion_condition( * // Result of latest write_some_at operation. * const asio::error_code& error, * * // Number of bytes transferred so far. * std::size_t bytes_transferred * ); @endcode * A return value of 0 indicates that the write operation is complete. A * non-zero return value indicates the maximum number of bytes to be written on * the next call to the device's write_some_at function. * * @returns The number of bytes transferred. * * @throws asio::system_error Thrown on failure. */ template std::size_t write_at(SyncRandomAccessWriteDevice& d, boost::uint64_t offset, basic_streambuf& b, CompletionCondition completion_condition); /// Write a certain amount of data at a specified offset before returning. /** * This function is used to write a certain number of bytes of data to a random * access device at a specified offset. The call will block until one of the * following conditions is true: * * @li All of the data in the supplied basic_streambuf has been written. * * @li The completion_condition function object returns 0. * * This operation is implemented in terms of zero or more calls to the device's * write_some_at function. * * @param d The device to which the data is to be written. The type must support * the SyncRandomAccessWriteDevice concept. * * @param offset The offset at which the data will be written. * * @param b The basic_streambuf object from which data will be written. * * @param completion_condition The function object to be called to determine * whether the write operation is complete. The signature of the function object * must be: * @code std::size_t completion_condition( * // Result of latest write_some_at operation. * const asio::error_code& error, * * // Number of bytes transferred so far. * std::size_t bytes_transferred * ); @endcode * A return value of 0 indicates that the write operation is complete. A * non-zero return value indicates the maximum number of bytes to be written on * the next call to the device's write_some_at function. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes written. If an error occurs, returns the total * number of bytes successfully transferred prior to the error. */ template std::size_t write_at(SyncRandomAccessWriteDevice& d, boost::uint64_t offset, basic_streambuf& b, CompletionCondition completion_condition, asio::error_code& ec); #endif // !defined(BOOST_NO_IOSTREAM) /*@}*/ /** * @defgroup async_write_at asio::async_write_at * * @brief Start an asynchronous operation to write a certain amount of data at * the specified offset. */ /*@{*/ /// Start an asynchronous operation to write all of the supplied data at the /// specified offset. /** * This function is used to asynchronously write a certain number of bytes of * data to a random access device at a specified offset. The function call * always returns immediately. The asynchronous operation will continue until * one of the following conditions is true: * * @li All of the data in the supplied buffers has been written. That is, the * bytes transferred is equal to the sum of the buffer sizes. * * @li An error occurred. * * This operation is implemented in terms of zero or more calls to the device's * async_write_some_at function. * * @param d The device to which the data is to be written. The type must support * the AsyncRandomAccessWriteDevice concept. * * @param offset The offset at which the data will be written. * * @param buffers One or more buffers containing the data to be written. * Although the buffers object may be copied as necessary, ownership of the * underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param handler The handler to be called when the write operation completes. * Copies will be made of the handler as required. The function signature of * the handler must be: * @code void handler( * // Result of operation. * const asio::error_code& error, * * // Number of bytes written from the buffers. If an error * // occurred, this will be less than the sum of the buffer sizes. * std::size_t bytes_transferred * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation of * the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @par Example * To write a single data buffer use the @ref buffer function as follows: * @code * asio::async_write_at(d, 42, asio::buffer(data, size), handler); * @endcode * See the @ref buffer documentation for information on writing multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template void async_write_at(AsyncRandomAccessWriteDevice& d, boost::uint64_t offset, const ConstBufferSequence& buffers, WriteHandler handler); /// Start an asynchronous operation to write a certain amount of data at the /// specified offset. /** * This function is used to asynchronously write a certain number of bytes of * data to a random access device at a specified offset. The function call * always returns immediately. The asynchronous operation will continue until * one of the following conditions is true: * * @li All of the data in the supplied buffers has been written. That is, the * bytes transferred is equal to the sum of the buffer sizes. * * @li The completion_condition function object returns 0. * * This operation is implemented in terms of zero or more calls to the device's * async_write_some_at function. * * @param d The device to which the data is to be written. The type must support * the AsyncRandomAccessWriteDevice concept. * * @param offset The offset at which the data will be written. * * @param buffers One or more buffers containing the data to be written. * Although the buffers object may be copied as necessary, ownership of the * underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param completion_condition The function object to be called to determine * whether the write operation is complete. The signature of the function object * must be: * @code std::size_t completion_condition( * // Result of latest async_write_some_at operation. * const asio::error_code& error, * * // Number of bytes transferred so far. * std::size_t bytes_transferred * ); @endcode * A return value of 0 indicates that the write operation is complete. A * non-zero return value indicates the maximum number of bytes to be written on * the next call to the device's async_write_some_at function. * * @param handler The handler to be called when the write operation completes. * Copies will be made of the handler as required. The function signature of the * handler must be: * @code void handler( * // Result of operation. * const asio::error_code& error, * * // Number of bytes written from the buffers. If an error * // occurred, this will be less than the sum of the buffer sizes. * std::size_t bytes_transferred * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation of * the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @par Example * To write a single data buffer use the @ref buffer function as follows: * @code asio::async_write_at(d, 42, * asio::buffer(data, size), * asio::transfer_at_least(32), * handler); @endcode * See the @ref buffer documentation for information on writing multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template void async_write_at(AsyncRandomAccessWriteDevice& d, boost::uint64_t offset, const ConstBufferSequence& buffers, CompletionCondition completion_condition, WriteHandler handler); #if !defined(BOOST_NO_IOSTREAM) /// Start an asynchronous operation to write all of the supplied data at the /// specified offset. /** * This function is used to asynchronously write a certain number of bytes of * data to a random access device at a specified offset. The function call * always returns immediately. The asynchronous operation will continue until * one of the following conditions is true: * * @li All of the data in the supplied basic_streambuf has been written. * * @li An error occurred. * * This operation is implemented in terms of zero or more calls to the device's * async_write_some_at function. * * @param d The device to which the data is to be written. The type must support * the AsyncRandomAccessWriteDevice concept. * * @param offset The offset at which the data will be written. * * @param b A basic_streambuf object from which data will be written. Ownership * of the streambuf is retained by the caller, which must guarantee that it * remains valid until the handler is called. * * @param handler The handler to be called when the write operation completes. * Copies will be made of the handler as required. The function signature of the * handler must be: * @code void handler( * // Result of operation. * const asio::error_code& error, * * // Number of bytes written from the buffers. If an error * // occurred, this will be less than the sum of the buffer sizes. * std::size_t bytes_transferred * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation of * the handler will be performed in a manner equivalent to using * asio::io_service::post(). */ template void async_write_at(AsyncRandomAccessWriteDevice& d, boost::uint64_t offset, basic_streambuf& b, WriteHandler handler); /// Start an asynchronous operation to write a certain amount of data at the /// specified offset. /** * This function is used to asynchronously write a certain number of bytes of * data to a random access device at a specified offset. The function call * always returns immediately. The asynchronous operation will continue until * one of the following conditions is true: * * @li All of the data in the supplied basic_streambuf has been written. * * @li The completion_condition function object returns 0. * * This operation is implemented in terms of zero or more calls to the device's * async_write_some_at function. * * @param d The device to which the data is to be written. The type must support * the AsyncRandomAccessWriteDevice concept. * * @param offset The offset at which the data will be written. * * @param b A basic_streambuf object from which data will be written. Ownership * of the streambuf is retained by the caller, which must guarantee that it * remains valid until the handler is called. * * @param completion_condition The function object to be called to determine * whether the write operation is complete. The signature of the function object * must be: * @code std::size_t completion_condition( * // Result of latest async_write_some_at operation. * const asio::error_code& error, * * // Number of bytes transferred so far. * std::size_t bytes_transferred * ); @endcode * A return value of 0 indicates that the write operation is complete. A * non-zero return value indicates the maximum number of bytes to be written on * the next call to the device's async_write_some_at function. * * @param handler The handler to be called when the write operation completes. * Copies will be made of the handler as required. The function signature of the * handler must be: * @code void handler( * // Result of operation. * const asio::error_code& error, * * // Number of bytes written from the buffers. If an error * // occurred, this will be less than the sum of the buffer sizes. * std::size_t bytes_transferred * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation of * the handler will be performed in a manner equivalent to using * asio::io_service::post(). */ template void async_write_at(AsyncRandomAccessWriteDevice& d, boost::uint64_t offset, basic_streambuf& b, CompletionCondition completion_condition, WriteHandler handler); #endif // !defined(BOOST_NO_IOSTREAM) /*@}*/ } // namespace asio #include "asio/detail/pop_options.hpp" #include "asio/impl/write_at.hpp" #endif // ASIO_WRITE_AT_HPP percona-xtradb-cluster-galera/asio/asio/detail/array_fwd.hpp0000644000000000000000000000112312247075736024466 0ustar rootroot00000000000000// // detail/array_fwd.hpp // ~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_ARRAY_FWD_HPP #define ASIO_DETAIL_ARRAY_FWD_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) namespace boost { template class array; } // namespace boost #endif // ASIO_DETAIL_ARRAY_FWD_HPP percona-xtradb-cluster-galera/asio/asio/detail/base_from_completion_cond.hpp0000644000000000000000000000315312247075736027706 0ustar rootroot00000000000000// // detail/base_from_completion_cond.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_BASE_FROM_COMPLETION_COND_HPP #define ASIO_DETAIL_BASE_FROM_COMPLETION_COND_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/completion_condition.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class base_from_completion_cond { protected: explicit base_from_completion_cond(CompletionCondition completion_condition) : completion_condition_(completion_condition) { } std::size_t check_for_completion( const asio::error_code& ec, std::size_t total_transferred) { return detail::adapt_completion_condition_result( completion_condition_(ec, total_transferred)); } private: CompletionCondition completion_condition_; }; template <> class base_from_completion_cond { protected: explicit base_from_completion_cond(transfer_all_t) { } static std::size_t check_for_completion( const asio::error_code& ec, std::size_t total_transferred) { return transfer_all_t()(ec, total_transferred); } }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_BASE_FROM_COMPLETION_COND_HPP percona-xtradb-cluster-galera/asio/asio/detail/bind_handler.hpp0000644000000000000000000002237712247075736025137 0ustar rootroot00000000000000// // detail/bind_handler.hpp // ~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_BIND_HANDLER_HPP #define ASIO_DETAIL_BIND_HANDLER_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class binder1 { public: binder1(const Handler& handler, const Arg1& arg1) : handler_(handler), arg1_(arg1) { } void operator()() { handler_(static_cast(arg1_)); } void operator()() const { handler_(arg1_); } //private: Handler handler_; Arg1 arg1_; }; template inline void* asio_handler_allocate(std::size_t size, binder1* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, binder1* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, binder1* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } template inline binder1 bind_handler(const Handler& handler, const Arg1& arg1) { return binder1(handler, arg1); } template class binder2 { public: binder2(const Handler& handler, const Arg1& arg1, const Arg2& arg2) : handler_(handler), arg1_(arg1), arg2_(arg2) { } void operator()() { handler_(static_cast(arg1_), static_cast(arg2_)); } void operator()() const { handler_(arg1_, arg2_); } //private: Handler handler_; Arg1 arg1_; Arg2 arg2_; }; template inline void* asio_handler_allocate(std::size_t size, binder2* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, binder2* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, binder2* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } template inline binder2 bind_handler(const Handler& handler, const Arg1& arg1, const Arg2& arg2) { return binder2(handler, arg1, arg2); } template class binder3 { public: binder3(const Handler& handler, const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) : handler_(handler), arg1_(arg1), arg2_(arg2), arg3_(arg3) { } void operator()() { handler_(static_cast(arg1_), static_cast(arg2_), static_cast(arg3_)); } void operator()() const { handler_(arg1_, arg2_, arg3_); } //private: Handler handler_; Arg1 arg1_; Arg2 arg2_; Arg3 arg3_; }; template inline void* asio_handler_allocate(std::size_t size, binder3* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, binder3* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, binder3* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } template inline binder3 bind_handler(const Handler& handler, const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) { return binder3(handler, arg1, arg2, arg3); } template class binder4 { public: binder4(const Handler& handler, const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4) : handler_(handler), arg1_(arg1), arg2_(arg2), arg3_(arg3), arg4_(arg4) { } void operator()() { handler_(static_cast(arg1_), static_cast(arg2_), static_cast(arg3_), static_cast(arg4_)); } void operator()() const { handler_(arg1_, arg2_, arg3_, arg4_); } //private: Handler handler_; Arg1 arg1_; Arg2 arg2_; Arg3 arg3_; Arg4 arg4_; }; template inline void* asio_handler_allocate(std::size_t size, binder4* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, binder4* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, binder4* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } template inline binder4 bind_handler( const Handler& handler, const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4) { return binder4(handler, arg1, arg2, arg3, arg4); } template class binder5 { public: binder5(const Handler& handler, const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4, const Arg5& arg5) : handler_(handler), arg1_(arg1), arg2_(arg2), arg3_(arg3), arg4_(arg4), arg5_(arg5) { } void operator()() { handler_(static_cast(arg1_), static_cast(arg2_), static_cast(arg3_), static_cast(arg4_), static_cast(arg5_)); } void operator()() const { handler_(arg1_, arg2_, arg3_, arg4_, arg5_); } //private: Handler handler_; Arg1 arg1_; Arg2 arg2_; Arg3 arg3_; Arg4 arg4_; Arg5 arg5_; }; template inline void* asio_handler_allocate(std::size_t size, binder5* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, binder5* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, binder5* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } template inline binder5 bind_handler( const Handler& handler, const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4, const Arg5& arg5) { return binder5(handler, arg1, arg2, arg3, arg4, arg5); } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_BIND_HANDLER_HPP percona-xtradb-cluster-galera/asio/asio/detail/buffer_resize_guard.hpp0000644000000000000000000000305612247075736026533 0ustar rootroot00000000000000// // detail/buffer_resize_guard.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP #define ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/detail/push_options.hpp" namespace asio { namespace detail { // Helper class to manage buffer resizing in an exception safe way. template class buffer_resize_guard { public: // Constructor. buffer_resize_guard(Buffer& buffer) : buffer_(buffer), old_size_(buffer.size()) { } // Destructor rolls back the buffer resize unless commit was called. ~buffer_resize_guard() { if (old_size_ != std::numeric_limits::max BOOST_PREVENT_MACRO_SUBSTITUTION()) { buffer_.resize(old_size_); } } // Commit the resize transaction. void commit() { old_size_ = std::numeric_limits::max BOOST_PREVENT_MACRO_SUBSTITUTION(); } private: // The buffer being managed. Buffer& buffer_; // The size of the buffer at the time the guard was constructed. size_t old_size_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP percona-xtradb-cluster-galera/asio/asio/detail/buffer_sequence_adapter.hpp0000644000000000000000000001366412247075736027366 0ustar rootroot00000000000000// // detail/buffer_sequence_adapter.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_BUFFER_SEQUENCE_ADAPTER_HPP #define ASIO_DETAIL_BUFFER_SEQUENCE_ADAPTER_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/buffer.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class buffer_sequence_adapter_base { protected: #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) typedef WSABUF native_buffer_type; static void init_native_buffer(WSABUF& buf, const asio::mutable_buffer& buffer) { buf.buf = asio::buffer_cast(buffer); buf.len = static_cast(asio::buffer_size(buffer)); } static void init_native_buffer(WSABUF& buf, const asio::const_buffer& buffer) { buf.buf = const_cast(asio::buffer_cast(buffer)); buf.len = static_cast(asio::buffer_size(buffer)); } #else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) typedef iovec native_buffer_type; static void init_iov_base(void*& base, void* addr) { base = addr; } template static void init_iov_base(T& base, void* addr) { base = static_cast(addr); } static void init_native_buffer(iovec& iov, const asio::mutable_buffer& buffer) { init_iov_base(iov.iov_base, asio::buffer_cast(buffer)); iov.iov_len = asio::buffer_size(buffer); } static void init_native_buffer(iovec& iov, const asio::const_buffer& buffer) { init_iov_base(iov.iov_base, const_cast( asio::buffer_cast(buffer))); iov.iov_len = asio::buffer_size(buffer); } #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) }; // Helper class to translate buffers into the native buffer representation. template class buffer_sequence_adapter : buffer_sequence_adapter_base { public: explicit buffer_sequence_adapter(const Buffers& buffers) : count_(0), total_buffer_size_(0) { typename Buffers::const_iterator iter = buffers.begin(); typename Buffers::const_iterator end = buffers.end(); for (; iter != end && count_ < max_buffers; ++iter, ++count_) { Buffer buffer(*iter); init_native_buffer(buffers_[count_], buffer); total_buffer_size_ += asio::buffer_size(buffer); } } native_buffer_type* buffers() { return buffers_; } std::size_t count() const { return count_; } bool all_empty() const { return total_buffer_size_ == 0; } static bool all_empty(const Buffers& buffers) { typename Buffers::const_iterator iter = buffers.begin(); typename Buffers::const_iterator end = buffers.end(); std::size_t i = 0; for (; iter != end && i < max_buffers; ++iter, ++i) if (asio::buffer_size(Buffer(*iter)) > 0) return false; return true; } static void validate(const Buffers& buffers) { typename Buffers::const_iterator iter = buffers.begin(); typename Buffers::const_iterator end = buffers.end(); for (; iter != end; ++iter) { Buffer buffer(*iter); asio::buffer_cast(buffer); } } static Buffer first(const Buffers& buffers) { typename Buffers::const_iterator iter = buffers.begin(); typename Buffers::const_iterator end = buffers.end(); for (; iter != end; ++iter) { Buffer buffer(*iter); if (asio::buffer_size(buffer) != 0) return buffer; } return Buffer(); } private: // The maximum number of buffers to support in a single operation. enum { max_buffers = 64 < max_iov_len ? 64 : max_iov_len }; native_buffer_type buffers_[max_buffers]; std::size_t count_; std::size_t total_buffer_size_; }; template class buffer_sequence_adapter : buffer_sequence_adapter_base { public: explicit buffer_sequence_adapter( const asio::mutable_buffers_1& buffers) { init_native_buffer(buffer_, Buffer(buffers)); total_buffer_size_ = asio::buffer_size(buffers); } native_buffer_type* buffers() { return &buffer_; } std::size_t count() const { return 1; } bool all_empty() const { return total_buffer_size_ == 0; } static bool all_empty(const asio::mutable_buffers_1& buffers) { return asio::buffer_size(buffers) == 0; } static void validate(const asio::mutable_buffers_1& buffers) { asio::buffer_cast(buffers); } static Buffer first(const asio::mutable_buffers_1& buffers) { return Buffer(buffers); } private: native_buffer_type buffer_; std::size_t total_buffer_size_; }; template class buffer_sequence_adapter : buffer_sequence_adapter_base { public: explicit buffer_sequence_adapter( const asio::const_buffers_1& buffers) { init_native_buffer(buffer_, Buffer(buffers)); total_buffer_size_ = asio::buffer_size(buffers); } native_buffer_type* buffers() { return &buffer_; } std::size_t count() const { return 1; } bool all_empty() const { return total_buffer_size_ == 0; } static bool all_empty(const asio::const_buffers_1& buffers) { return asio::buffer_size(buffers) == 0; } static void validate(const asio::const_buffers_1& buffers) { asio::buffer_cast(buffers); } static Buffer first(const asio::const_buffers_1& buffers) { return Buffer(buffers); } private: native_buffer_type buffer_; std::size_t total_buffer_size_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_BUFFER_SEQUENCE_ADAPTER_HPP percona-xtradb-cluster-galera/asio/asio/detail/buffered_stream_storage.hpp0000644000000000000000000000534412247075736027402 0ustar rootroot00000000000000// // detail/buffered_stream_storage.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP #define ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include #include #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class buffered_stream_storage { public: // The type of the bytes stored in the buffer. typedef unsigned char byte_type; // The type used for offsets into the buffer. typedef std::size_t size_type; // Constructor. explicit buffered_stream_storage(std::size_t capacity) : begin_offset_(0), end_offset_(0), buffer_(capacity) { } /// Clear the buffer. void clear() { begin_offset_ = 0; end_offset_ = 0; } // Return a pointer to the beginning of the unread data. byte_type* data() { return &buffer_[0] + begin_offset_; } // Return a pointer to the beginning of the unread data. const byte_type* data() const { return &buffer_[0] + begin_offset_; } // Is there no unread data in the buffer. bool empty() const { return begin_offset_ == end_offset_; } // Return the amount of unread data the is in the buffer. size_type size() const { return end_offset_ - begin_offset_; } // Resize the buffer to the specified length. void resize(size_type length) { assert(length <= capacity()); if (begin_offset_ + length <= capacity()) { end_offset_ = begin_offset_ + length; } else { using namespace std; // For memmove. memmove(&buffer_[0], &buffer_[0] + begin_offset_, size()); end_offset_ = length; begin_offset_ = 0; } } // Return the maximum size for data in the buffer. size_type capacity() const { return buffer_.size(); } // Consume multiple bytes from the beginning of the buffer. void consume(size_type count) { assert(begin_offset_ + count <= end_offset_); begin_offset_ += count; if (empty()) clear(); } private: // The offset to the beginning of the unread data. size_type begin_offset_; // The offset to the end of the unread data. size_type end_offset_; // The data in the buffer. std::vector buffer_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP percona-xtradb-cluster-galera/asio/asio/detail/call_stack.hpp0000644000000000000000000000377112247075736024623 0ustar rootroot00000000000000// // detail/call_stack.hpp // ~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_CALL_STACK_HPP #define ASIO_DETAIL_CALL_STACK_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/detail/noncopyable.hpp" #include "asio/detail/tss_ptr.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { // Helper class to determine whether or not the current thread is inside an // invocation of io_service::run() for a specified io_service object. template class call_stack { public: // Context class automatically pushes an owner on to the stack. class context : private noncopyable { public: // Push the owner on to the stack. explicit context(Owner* d) : owner_(d), next_(call_stack::top_) { call_stack::top_ = this; } // Pop the owner from the stack. ~context() { call_stack::top_ = next_; } private: friend class call_stack; // The owner associated with the context. Owner* owner_; // The next element in the stack. context* next_; }; friend class context; // Determine whether the specified owner is on the stack. static bool contains(Owner* d) { context* elem = top_; while (elem) { if (elem->owner_ == d) return true; elem = elem->next_; } return false; } private: // The top of the stack of calls for the current thread. static tss_ptr top_; }; template tss_ptr::context> call_stack::top_; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_CALL_STACK_HPP percona-xtradb-cluster-galera/asio/asio/detail/completion_handler.hpp0000644000000000000000000000420012247075736026355 0ustar rootroot00000000000000// // detail/completion_handler.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_COMPLETION_HANDLER_HPP #define ASIO_DETAIL_COMPLETION_HANDLER_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/operation.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class completion_handler : public operation { public: ASIO_DEFINE_HANDLER_PTR(completion_handler); completion_handler(Handler h) : operation(&completion_handler::do_complete), handler_(h) { } static void do_complete(io_service_impl* owner, operation* base, asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) { // Take ownership of the handler object. completion_handler* h(static_cast(base)); ptr p = { boost::addressof(h->handler_), h, h }; // Make a copy of the handler so that the memory can be deallocated before // the upcall is made. Even if we're not about to make an upcall, a // sub-object of the handler may be the true owner of the memory associated // with the handler. Consequently, a local copy of the handler is required // to ensure that any owning sub-object remains valid until after we have // deallocated the memory here. Handler handler(h->handler_); p.h = boost::addressof(handler); p.reset(); // Make the upcall if required. if (owner) { asio::detail::fenced_block b; asio_handler_invoke_helpers::invoke(handler, handler); } } private: Handler handler_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_COMPLETION_HANDLER_HPP percona-xtradb-cluster-galera/asio/asio/detail/config.hpp0000644000000000000000000001705612247075736023771 0ustar rootroot00000000000000// // detail/config.hpp // ~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_CONFIG_HPP #define ASIO_DETAIL_CONFIG_HPP #include // Default to a header-only implementation. The user must specifically request // separate compilation by defining either ASIO_SEPARATE_COMPILATION or // ASIO_DYN_LINK (as a DLL/shared library implies separate compilation). #if !defined(ASIO_HEADER_ONLY) # if !defined(ASIO_SEPARATE_COMPILATION) # if !defined(ASIO_DYN_LINK) # define ASIO_HEADER_ONLY # endif // !defined(ASIO_DYN_LINK) # endif // !defined(ASIO_SEPARATE_COMPILATION) #endif // !defined(ASIO_HEADER_ONLY) #if defined(ASIO_HEADER_ONLY) # define ASIO_DECL inline #else // defined(ASIO_HEADER_ONLY) # if defined(BOOST_HAS_DECLSPEC) // We need to import/export our code only if the user has specifically asked // for it by defining ASIO_DYN_LINK. # if defined(ASIO_DYN_LINK) // Export if this is our own source, otherwise import. # if defined(ASIO_SOURCE) # define ASIO_DECL __declspec(dllexport) # else // defined(ASIO_SOURCE) # define ASIO_DECL __declspec(dllimport) # endif // defined(ASIO_SOURCE) # endif // defined(ASIO_DYN_LINK) # endif // defined(BOOST_HAS_DECLSPEC) #endif // defined(ASIO_HEADER_ONLY) // If ASIO_DECL isn't defined yet define it now. #if !defined(ASIO_DECL) # define ASIO_DECL #endif // !defined(ASIO_DECL) // Windows: target OS version. #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) # if !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS) # if defined(_MSC_VER) || defined(__BORLANDC__) # pragma message( \ "Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately. For example:\n"\ "- add -D_WIN32_WINNT=0x0501 to the compiler command line; or\n"\ "- add _WIN32_WINNT=0x0501 to your project's Preprocessor Definitions.\n"\ "Assuming _WIN32_WINNT=0x0501 (i.e. Windows XP target).") # else // defined(_MSC_VER) || defined(__BORLANDC__) # warning Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately. # warning For example, add -D_WIN32_WINNT=0x0501 to the compiler command line. # warning Assuming _WIN32_WINNT=0x0501 (i.e. Windows XP target). # endif // defined(_MSC_VER) || defined(__BORLANDC__) # define _WIN32_WINNT 0x0501 # endif // !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS) # if defined(_MSC_VER) # if defined(_WIN32) && !defined(WIN32) # if !defined(_WINSOCK2API_) # define WIN32 // Needed for correct types in winsock2.h # else // !defined(_WINSOCK2API_) # error Please define the macro WIN32 in your compiler options # endif // !defined(_WINSOCK2API_) # endif // defined(_WIN32) && !defined(WIN32) # endif // defined(_MSC_VER) # if defined(__BORLANDC__) # if defined(__WIN32__) && !defined(WIN32) # if !defined(_WINSOCK2API_) # define WIN32 // Needed for correct types in winsock2.h # else // !defined(_WINSOCK2API_) # error Please define the macro WIN32 in your compiler options # endif // !defined(_WINSOCK2API_) # endif // defined(__WIN32__) && !defined(WIN32) # endif // defined(__BORLANDC__) # if defined(__CYGWIN__) # if !defined(__USE_W32_SOCKETS) # error You must add -D__USE_W32_SOCKETS to your compiler options. # endif // !defined(__USE_W32_SOCKETS) # endif // defined(__CYGWIN__) #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) // Windows: minimise header inclusion. #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) # if !defined(ASIO_NO_WIN32_LEAN_AND_MEAN) # if !defined(WIN32_LEAN_AND_MEAN) # define WIN32_LEAN_AND_MEAN # endif // !defined(WIN32_LEAN_AND_MEAN) # endif // !defined(ASIO_NO_WIN32_LEAN_AND_MEAN) #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) // Windows: suppress definition of "min" and "max" macros. #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) # if !defined(ASIO_NO_NOMINMAX) # if !defined(NOMINMAX) # define NOMINMAX 1 # endif // !defined(NOMINMAX) # endif // !defined(ASIO_NO_NOMINMAX) #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) // Windows: IO Completion Ports. #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) # if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400) # if !defined(UNDER_CE) # if !defined(ASIO_DISABLE_IOCP) # define ASIO_HAS_IOCP 1 # endif // !defined(ASIO_DISABLE_IOCP) # endif // !defined(UNDER_CE) # endif // defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400) #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) // Linux: epoll, eventfd and timerfd. #if defined(__linux__) # include # if !defined(ASIO_DISABLE_EPOLL) # if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,45) # define ASIO_HAS_EPOLL 1 # endif // LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,45) # endif // !defined(ASIO_DISABLE_EVENTFD) # if !defined(ASIO_DISABLE_EVENTFD) # if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) # define ASIO_HAS_EVENTFD 1 # endif // LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) # endif // !defined(ASIO_DISABLE_EVENTFD) # if defined(ASIO_HAS_EPOLL) # if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8) # define ASIO_HAS_TIMERFD 1 # endif // (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8) # endif // defined(ASIO_HAS_EPOLL) #endif // defined(__linux__) // Mac OS X, FreeBSD, NetBSD, OpenBSD: kqueue. #if (defined(__MACH__) && defined(__APPLE__)) \ || defined(__FreeBSD__) \ || defined(__NetBSD__) \ || defined(__OpenBSD__) # if !defined(ASIO_DISABLE_KQUEUE) # define ASIO_HAS_KQUEUE 1 # endif // !defined(ASIO_DISABLE_KQUEUE) #endif // (defined(__MACH__) && defined(__APPLE__)) // || defined(__FreeBSD__) // || defined(__NetBSD__) // || defined(__OpenBSD__) // Solaris: /dev/poll. #if defined(__sun) # if !defined(ASIO_DISABLE_DEV_POLL) # define ASIO_HAS_DEV_POLL 1 # endif // !defined(ASIO_DISABLE_DEV_POLL) #endif // defined(__sun) // Serial ports. #if defined(ASIO_HAS_IOCP) \ || !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) # if !defined(__SYMBIAN32__) # if !defined(ASIO_DISABLE_SERIAL_PORT) # define ASIO_HAS_SERIAL_PORT 1 # endif // !defined(ASIO_DISABLE_SERIAL_PORT) # endif // !defined(__SYMBIAN32__) #endif // defined(ASIO_HAS_IOCP) // || !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) // Windows: stream handles. #if !defined(ASIO_DISABLE_WINDOWS_STREAM_HANDLE) # if defined(ASIO_HAS_IOCP) # define ASIO_HAS_WINDOWS_STREAM_HANDLE 1 # endif // defined(ASIO_HAS_IOCP) #endif // !defined(ASIO_DISABLE_WINDOWS_STREAM_HANDLE) // Windows: random access handles. #if !defined(ASIO_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE) # if defined(ASIO_HAS_IOCP) # define ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE 1 # endif // defined(ASIO_HAS_IOCP) #endif // !defined(ASIO_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE) // Windows: OVERLAPPED wrapper. #if !defined(ASIO_DISABLE_WINDOWS_OVERLAPPED_PTR) # if defined(ASIO_HAS_IOCP) # define ASIO_HAS_WINDOWS_OVERLAPPED_PTR 1 # endif // defined(ASIO_HAS_IOCP) #endif // !defined(ASIO_DISABLE_WINDOWS_OVERLAPPED_PTR) // POSIX: stream-oriented file descriptors. #if !defined(ASIO_DISABLE_POSIX_STREAM_DESCRIPTOR) # if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) # define ASIO_HAS_POSIX_STREAM_DESCRIPTOR 1 # endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) #endif // !defined(ASIO_DISABLE_POSIX_STREAM_DESCRIPTOR) // UNIX domain sockets. #if !defined(ASIO_DISABLE_LOCAL_SOCKETS) # if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) # define ASIO_HAS_LOCAL_SOCKETS 1 # endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) #endif // !defined(ASIO_DISABLE_LOCAL_SOCKETS) #endif // ASIO_DETAIL_CONFIG_HPP percona-xtradb-cluster-galera/asio/asio/detail/consuming_buffers.hpp0000644000000000000000000001542712247075736026242 0ustar rootroot00000000000000// // detail/consuming_buffers.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_CONSUMING_BUFFERS_HPP #define ASIO_DETAIL_CONSUMING_BUFFERS_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include #include "asio/buffer.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { // A proxy iterator for a sub-range in a list of buffers. template class consuming_buffers_iterator : public boost::iterator { public: // Default constructor creates an end iterator. consuming_buffers_iterator() : at_end_(true) { } // Construct with a buffer for the first entry and an iterator // range for the remaining entries. consuming_buffers_iterator(bool at_end, const Buffer& first, Buffer_Iterator begin_remainder, Buffer_Iterator end_remainder, std::size_t max_size) : at_end_(max_size > 0 ? at_end : true), first_(buffer(first, max_size)), begin_remainder_(begin_remainder), end_remainder_(end_remainder), offset_(0), max_size_(max_size) { } // Dereference an iterator. const Buffer& operator*() const { return dereference(); } // Dereference an iterator. const Buffer* operator->() const { return &dereference(); } // Increment operator (prefix). consuming_buffers_iterator& operator++() { increment(); return *this; } // Increment operator (postfix). consuming_buffers_iterator operator++(int) { consuming_buffers_iterator tmp(*this); ++*this; return tmp; } // Test two iterators for equality. friend bool operator==(const consuming_buffers_iterator& a, const consuming_buffers_iterator& b) { return a.equal(b); } // Test two iterators for inequality. friend bool operator!=(const consuming_buffers_iterator& a, const consuming_buffers_iterator& b) { return !a.equal(b); } private: void increment() { if (!at_end_) { if (begin_remainder_ == end_remainder_ || offset_ + buffer_size(first_) >= max_size_) { at_end_ = true; } else { offset_ += buffer_size(first_); first_ = buffer(*begin_remainder_++, max_size_ - offset_); } } } bool equal(const consuming_buffers_iterator& other) const { if (at_end_ && other.at_end_) return true; return !at_end_ && !other.at_end_ && buffer_cast(first_) == buffer_cast(other.first_) && buffer_size(first_) == buffer_size(other.first_) && begin_remainder_ == other.begin_remainder_ && end_remainder_ == other.end_remainder_; } const Buffer& dereference() const { return first_; } bool at_end_; Buffer first_; Buffer_Iterator begin_remainder_; Buffer_Iterator end_remainder_; std::size_t offset_; std::size_t max_size_; }; // A proxy for a sub-range in a list of buffers. template class consuming_buffers { public: // The type for each element in the list of buffers. typedef Buffer value_type; // A forward-only iterator type that may be used to read elements. typedef consuming_buffers_iterator const_iterator; // Construct to represent the entire list of buffers. consuming_buffers(const Buffers& buffers) : buffers_(buffers), at_end_(buffers_.begin() == buffers_.end()), begin_remainder_(buffers_.begin()), max_size_((std::numeric_limits::max)()) { if (!at_end_) { first_ = *buffers_.begin(); ++begin_remainder_; } } // Copy constructor. consuming_buffers(const consuming_buffers& other) : buffers_(other.buffers_), at_end_(other.at_end_), first_(other.first_), begin_remainder_(buffers_.begin()), max_size_(other.max_size_) { typename Buffers::const_iterator first = other.buffers_.begin(); typename Buffers::const_iterator second = other.begin_remainder_; std::advance(begin_remainder_, std::distance(first, second)); } // Assignment operator. consuming_buffers& operator=(const consuming_buffers& other) { buffers_ = other.buffers_; at_end_ = other.at_end_; first_ = other.first_; begin_remainder_ = buffers_.begin(); typename Buffers::const_iterator first = other.buffers_.begin(); typename Buffers::const_iterator second = other.begin_remainder_; std::advance(begin_remainder_, std::distance(first, second)); max_size_ = other.max_size_; return *this; } // Get a forward-only iterator to the first element. const_iterator begin() const { return const_iterator(at_end_, first_, begin_remainder_, buffers_.end(), max_size_); } // Get a forward-only iterator for one past the last element. const_iterator end() const { return const_iterator(); } // Set the maximum size for a single transfer. void prepare(std::size_t max_size) { max_size_ = max_size; } // Consume the specified number of bytes from the buffers. void consume(std::size_t size) { // Remove buffers from the start until the specified size is reached. while (size > 0 && !at_end_) { if (buffer_size(first_) <= size) { size -= buffer_size(first_); if (begin_remainder_ == buffers_.end()) at_end_ = true; else first_ = *begin_remainder_++; } else { first_ = first_ + size; size = 0; } } // Remove any more empty buffers at the start. while (!at_end_ && buffer_size(first_) == 0) { if (begin_remainder_ == buffers_.end()) at_end_ = true; else first_ = *begin_remainder_++; } } private: Buffers buffers_; bool at_end_; Buffer first_; typename Buffers::const_iterator begin_remainder_; std::size_t max_size_; }; // Specialisation for null_buffers to ensure that the null_buffers type is // always passed through to the underlying read or write operation. template class consuming_buffers : public asio::null_buffers { public: consuming_buffers(const asio::null_buffers&) { // No-op. } void prepare(std::size_t) { // No-op. } void consume(std::size_t) { // No-op. } }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_CONSUMING_BUFFERS_HPP percona-xtradb-cluster-galera/asio/asio/detail/deadline_timer_service.hpp0000644000000000000000000001232712247075736027205 0ustar rootroot00000000000000// // detail/deadline_timer_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP #define ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/detail/bind_handler.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/noncopyable.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/timer_op.hpp" #include "asio/detail/timer_queue.hpp" #include "asio/detail/timer_scheduler.hpp" #include "asio/detail/wait_handler.hpp" #include "asio/detail/push_options.hpp" #include #include "asio/detail/pop_options.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class deadline_timer_service { public: // The time type. typedef typename Time_Traits::time_type time_type; // The duration type. typedef typename Time_Traits::duration_type duration_type; // The implementation type of the timer. This type is dependent on the // underlying implementation of the timer service. struct implementation_type : private asio::detail::noncopyable { time_type expiry; bool might_have_pending_waits; typename timer_queue::per_timer_data timer_data; }; // Constructor. deadline_timer_service(asio::io_service& io_service) : scheduler_(asio::use_service(io_service)) { scheduler_.init_task(); scheduler_.add_timer_queue(timer_queue_); } // Destructor. ~deadline_timer_service() { scheduler_.remove_timer_queue(timer_queue_); } // Destroy all user-defined handler objects owned by the service. void shutdown_service() { } // Construct a new timer implementation. void construct(implementation_type& impl) { impl.expiry = time_type(); impl.might_have_pending_waits = false; } // Destroy a timer implementation. void destroy(implementation_type& impl) { asio::error_code ec; cancel(impl, ec); } // Cancel any asynchronous wait operations associated with the timer. std::size_t cancel(implementation_type& impl, asio::error_code& ec) { if (!impl.might_have_pending_waits) { ec = asio::error_code(); return 0; } std::size_t count = scheduler_.cancel_timer(timer_queue_, impl.timer_data); impl.might_have_pending_waits = false; ec = asio::error_code(); return count; } // Get the expiry time for the timer as an absolute time. time_type expires_at(const implementation_type& impl) const { return impl.expiry; } // Set the expiry time for the timer as an absolute time. std::size_t expires_at(implementation_type& impl, const time_type& expiry_time, asio::error_code& ec) { std::size_t count = cancel(impl, ec); impl.expiry = expiry_time; ec = asio::error_code(); return count; } // Get the expiry time for the timer relative to now. duration_type expires_from_now(const implementation_type& impl) const { return Time_Traits::subtract(expires_at(impl), Time_Traits::now()); } // Set the expiry time for the timer relative to now. std::size_t expires_from_now(implementation_type& impl, const duration_type& expiry_time, asio::error_code& ec) { return expires_at(impl, Time_Traits::add(Time_Traits::now(), expiry_time), ec); } // Perform a blocking wait on the timer. void wait(implementation_type& impl, asio::error_code& ec) { time_type now = Time_Traits::now(); while (Time_Traits::less_than(now, impl.expiry)) { boost::posix_time::time_duration timeout = Time_Traits::to_posix_duration(Time_Traits::subtract(impl.expiry, now)); ::timeval tv; tv.tv_sec = timeout.total_seconds(); tv.tv_usec = timeout.total_microseconds() % 1000000; asio::error_code ec; socket_ops::select(0, 0, 0, 0, &tv, ec); now = Time_Traits::now(); } ec = asio::error_code(); } // Start an asynchronous wait on the timer. template void async_wait(implementation_type& impl, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef wait_handler op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(handler); impl.might_have_pending_waits = true; scheduler_.schedule_timer(timer_queue_, impl.expiry, impl.timer_data, p.p); p.v = p.p = 0; } private: // The queue of timers. timer_queue timer_queue_; // The object that schedules and executes timers. Usually a reactor. timer_scheduler& scheduler_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/detail/descriptor_ops.hpp0000644000000000000000000000543712247075736025563 0ustar rootroot00000000000000// // detail/descriptor_ops.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_DESCRIPTOR_OPS_HPP #define ASIO_DETAIL_DESCRIPTOR_OPS_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) #include #include "asio/error_code.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { namespace descriptor_ops { // Descriptor state bits. enum { // The user wants a non-blocking descriptor. user_set_non_blocking = 1, // The descriptor has been set non-blocking. internal_non_blocking = 2, // Helper "state" used to determine whether the descriptor is non-blocking. non_blocking = user_set_non_blocking | internal_non_blocking }; typedef unsigned char state_type; template inline ReturnType error_wrapper(ReturnType return_value, asio::error_code& ec) { ec = asio::error_code(errno, asio::error::get_system_category()); return return_value; } ASIO_DECL int open(const char* path, int flags, asio::error_code& ec); ASIO_DECL int close(int d, state_type& state, asio::error_code& ec); ASIO_DECL bool set_internal_non_blocking(int d, state_type& state, asio::error_code& ec); typedef iovec buf; ASIO_DECL std::size_t sync_read(int d, state_type state, buf* bufs, std::size_t count, bool all_empty, asio::error_code& ec); ASIO_DECL bool non_blocking_read(int d, buf* bufs, std::size_t count, asio::error_code& ec, std::size_t& bytes_transferred); ASIO_DECL std::size_t sync_write(int d, state_type state, const buf* bufs, std::size_t count, bool all_empty, asio::error_code& ec); ASIO_DECL bool non_blocking_write(int d, const buf* bufs, std::size_t count, asio::error_code& ec, std::size_t& bytes_transferred); ASIO_DECL int ioctl(int d, state_type& state, long cmd, ioctl_arg_type* arg, asio::error_code& ec); ASIO_DECL int fcntl(int d, long cmd, asio::error_code& ec); ASIO_DECL int fcntl(int d, long cmd, long arg, asio::error_code& ec); ASIO_DECL int poll_read(int d, asio::error_code& ec); ASIO_DECL int poll_write(int d, asio::error_code& ec); } // namespace descriptor_ops } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/descriptor_ops.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) #endif // ASIO_DETAIL_DESCRIPTOR_OPS_HPP percona-xtradb-cluster-galera/asio/asio/detail/descriptor_read_op.hpp0000644000000000000000000000657412247075736026376 0ustar rootroot00000000000000// // detail/descriptor_read_op.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_DESCRIPTOR_READ_OP_HPP #define ASIO_DETAIL_DESCRIPTOR_READ_OP_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) #include #include "asio/detail/bind_handler.hpp" #include "asio/detail/buffer_sequence_adapter.hpp" #include "asio/detail/descriptor_ops.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/reactor_op.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class descriptor_read_op_base : public reactor_op { public: descriptor_read_op_base(int descriptor, const MutableBufferSequence& buffers, func_type complete_func) : reactor_op(&descriptor_read_op_base::do_perform, complete_func), descriptor_(descriptor), buffers_(buffers) { } static bool do_perform(reactor_op* base) { descriptor_read_op_base* o(static_cast(base)); buffer_sequence_adapter bufs(o->buffers_); return descriptor_ops::non_blocking_read(o->descriptor_, bufs.buffers(), bufs.count(), o->ec_, o->bytes_transferred_); } private: int descriptor_; MutableBufferSequence buffers_; }; template class descriptor_read_op : public descriptor_read_op_base { public: ASIO_DEFINE_HANDLER_PTR(descriptor_read_op); descriptor_read_op(int descriptor, const MutableBufferSequence& buffers, Handler handler) : descriptor_read_op_base( descriptor, buffers, &descriptor_read_op::do_complete), handler_(handler) { } static void do_complete(io_service_impl* owner, operation* base, asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) { // Take ownership of the handler object. descriptor_read_op* o(static_cast(base)); ptr p = { boost::addressof(o->handler_), o, o }; // Make a copy of the handler so that the memory can be deallocated before // the upcall is made. Even if we're not about to make an upcall, a // sub-object of the handler may be the true owner of the memory associated // with the handler. Consequently, a local copy of the handler is required // to ensure that any owning sub-object remains valid until after we have // deallocated the memory here. detail::binder2 handler(o->handler_, o->ec_, o->bytes_transferred_); p.h = boost::addressof(handler.handler_); p.reset(); // Make the upcall if required. if (owner) { asio::detail::fenced_block b; asio_handler_invoke_helpers::invoke(handler, handler.handler_); } } private: Handler handler_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) #endif // ASIO_DETAIL_DESCRIPTOR_READ_OP_HPP percona-xtradb-cluster-galera/asio/asio/detail/descriptor_write_op.hpp0000644000000000000000000000657512247075736026616 0ustar rootroot00000000000000// // detail/descriptor_write_op.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_DESCRIPTOR_WRITE_OP_HPP #define ASIO_DETAIL_DESCRIPTOR_WRITE_OP_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) #include #include "asio/detail/bind_handler.hpp" #include "asio/detail/buffer_sequence_adapter.hpp" #include "asio/detail/descriptor_ops.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/reactor_op.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class descriptor_write_op_base : public reactor_op { public: descriptor_write_op_base(int descriptor, const ConstBufferSequence& buffers, func_type complete_func) : reactor_op(&descriptor_write_op_base::do_perform, complete_func), descriptor_(descriptor), buffers_(buffers) { } static bool do_perform(reactor_op* base) { descriptor_write_op_base* o(static_cast(base)); buffer_sequence_adapter bufs(o->buffers_); return descriptor_ops::non_blocking_write(o->descriptor_, bufs.buffers(), bufs.count(), o->ec_, o->bytes_transferred_); } private: int descriptor_; ConstBufferSequence buffers_; }; template class descriptor_write_op : public descriptor_write_op_base { public: ASIO_DEFINE_HANDLER_PTR(descriptor_write_op); descriptor_write_op(int descriptor, const ConstBufferSequence& buffers, Handler handler) : descriptor_write_op_base( descriptor, buffers, &descriptor_write_op::do_complete), handler_(handler) { } static void do_complete(io_service_impl* owner, operation* base, asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) { // Take ownership of the handler object. descriptor_write_op* o(static_cast(base)); ptr p = { boost::addressof(o->handler_), o, o }; // Make a copy of the handler so that the memory can be deallocated before // the upcall is made. Even if we're not about to make an upcall, a // sub-object of the handler may be the true owner of the memory associated // with the handler. Consequently, a local copy of the handler is required // to ensure that any owning sub-object remains valid until after we have // deallocated the memory here. detail::binder2 handler(o->handler_, o->ec_, o->bytes_transferred_); p.h = boost::addressof(handler.handler_); p.reset(); // Make the upcall if required. if (owner) { asio::detail::fenced_block b; asio_handler_invoke_helpers::invoke(handler, handler.handler_); } } private: Handler handler_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) #endif // ASIO_DETAIL_DESCRIPTOR_WRITE_OP_HPP percona-xtradb-cluster-galera/asio/asio/detail/dev_poll_reactor.hpp0000644000000000000000000001373712247075736026051 0ustar rootroot00000000000000// // detail/dev_poll_reactor.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_DEV_POLL_REACTOR_HPP #define ASIO_DETAIL_DEV_POLL_REACTOR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_DEV_POLL) #include #include #include #include "asio/detail/dev_poll_reactor_fwd.hpp" #include "asio/detail/hash_map.hpp" #include "asio/detail/mutex.hpp" #include "asio/detail/op_queue.hpp" #include "asio/detail/reactor_op.hpp" #include "asio/detail/reactor_op_queue.hpp" #include "asio/detail/select_interrupter.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/timer_op.hpp" #include "asio/detail/timer_queue_base.hpp" #include "asio/detail/timer_queue_fwd.hpp" #include "asio/detail/timer_queue_set.hpp" #include "asio/io_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class dev_poll_reactor : public asio::detail::service_base { public: enum { read_op = 0, write_op = 1, connect_op = 1, except_op = 2, max_ops = 3 }; // Per-descriptor data. struct per_descriptor_data { }; // Constructor. ASIO_DECL dev_poll_reactor(asio::io_service& io_service); // Destructor. ASIO_DECL ~dev_poll_reactor(); // Destroy all user-defined handler objects owned by the service. ASIO_DECL void shutdown_service(); // Initialise the task. ASIO_DECL void init_task(); // Register a socket with the reactor. Returns 0 on success, system error // code on failure. ASIO_DECL int register_descriptor(socket_type, per_descriptor_data&); // Post a reactor operation for immediate completion. void post_immediate_completion(reactor_op* op) { io_service_.post_immediate_completion(op); } // Start a new operation. The reactor operation will be performed when the // given descriptor is flagged as ready, or an error has occurred. ASIO_DECL void start_op(int op_type, socket_type descriptor, per_descriptor_data&, reactor_op* op, bool allow_speculative); // Cancel all operations associated with the given descriptor. The // handlers associated with the descriptor will be invoked with the // operation_aborted error. ASIO_DECL void cancel_ops(socket_type descriptor, per_descriptor_data&); // Cancel any operations that are running against the descriptor and remove // its registration from the reactor. ASIO_DECL void close_descriptor( socket_type descriptor, per_descriptor_data&); // Add a new timer queue to the reactor. template void add_timer_queue(timer_queue& queue); // Remove a timer queue from the reactor. template void remove_timer_queue(timer_queue& queue); // Schedule a new operation in the given timer queue to expire at the // specified absolute time. template void schedule_timer(timer_queue& queue, const typename Time_Traits::time_type& time, typename timer_queue::per_timer_data& timer, timer_op* op); // Cancel the timer operations associated with the given token. Returns the // number of operations that have been posted or dispatched. template std::size_t cancel_timer(timer_queue& queue, typename timer_queue::per_timer_data& timer); // Run /dev/poll once until interrupted or events are ready to be dispatched. ASIO_DECL void run(bool block, op_queue& ops); // Interrupt the select loop. ASIO_DECL void interrupt(); private: // Create the /dev/poll file descriptor. Throws an exception if the descriptor // cannot be created. ASIO_DECL static int do_dev_poll_create(); // Helper function to add a new timer queue. ASIO_DECL void do_add_timer_queue(timer_queue_base& queue); // Helper function to remove a timer queue. ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue); // Get the timeout value for the /dev/poll DP_POLL operation. The timeout // value is returned as a number of milliseconds. A return value of -1 // indicates that the poll should block indefinitely. ASIO_DECL int get_timeout(); // Cancel all operations associated with the given descriptor. The do_cancel // function of the handler objects will be invoked. This function does not // acquire the dev_poll_reactor's mutex. ASIO_DECL void cancel_ops_unlocked(socket_type descriptor, const asio::error_code& ec); // Add a pending event entry for the given descriptor. ASIO_DECL ::pollfd& add_pending_event_change(int descriptor); // The io_service implementation used to post completions. io_service_impl& io_service_; // Mutex to protect access to internal data. asio::detail::mutex mutex_; // The /dev/poll file descriptor. int dev_poll_fd_; // Vector of /dev/poll events waiting to be written to the descriptor. std::vector< ::pollfd> pending_event_changes_; // Hash map to associate a descriptor with a pending event change index. hash_map pending_event_change_index_; // The interrupter is used to break a blocking DP_POLL operation. select_interrupter interrupter_; // The queues of read, write and except operations. reactor_op_queue op_queue_[max_ops]; // The timer queues. timer_queue_set timer_queues_; // Whether the service has been shut down. bool shutdown_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #include "asio/detail/impl/dev_poll_reactor.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/dev_poll_reactor.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // defined(ASIO_HAS_DEV_POLL) #endif // ASIO_DETAIL_DEV_POLL_REACTOR_HPP percona-xtradb-cluster-galera/asio/asio/detail/dev_poll_reactor_fwd.hpp0000644000000000000000000000140412247075736026675 0ustar rootroot00000000000000// // detail/dev_poll_reactor_fwd.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_DEV_POLL_REACTOR_FWD_HPP #define ASIO_DETAIL_DEV_POLL_REACTOR_FWD_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_DEV_POLL) namespace asio { namespace detail { class dev_poll_reactor; } // namespace detail } // namespace asio #endif // defined(ASIO_HAS_DEV_POLL) #endif // ASIO_DETAIL_DEV_POLL_REACTOR_FWD_HPP percona-xtradb-cluster-galera/asio/asio/detail/epoll_reactor.hpp0000644000000000000000000001411212247075736025344 0ustar rootroot00000000000000// // detail/epoll_reactor.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_EPOLL_REACTOR_HPP #define ASIO_DETAIL_EPOLL_REACTOR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_EPOLL) #include "asio/io_service.hpp" #include "asio/detail/epoll_reactor_fwd.hpp" #include "asio/detail/mutex.hpp" #include "asio/detail/object_pool.hpp" #include "asio/detail/op_queue.hpp" #include "asio/detail/reactor_op.hpp" #include "asio/detail/select_interrupter.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/timer_op.hpp" #include "asio/detail/timer_queue_base.hpp" #include "asio/detail/timer_queue_fwd.hpp" #include "asio/detail/timer_queue_set.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class epoll_reactor : public asio::detail::service_base { public: enum { read_op = 0, write_op = 1, connect_op = 1, except_op = 2, max_ops = 3 }; // Per-descriptor queues. class descriptor_state { friend class epoll_reactor; friend class object_pool_access; mutex mutex_; op_queue op_queue_[max_ops]; bool shutdown_; descriptor_state* next_; descriptor_state* prev_; }; // Per-descriptor data. typedef descriptor_state* per_descriptor_data; // Constructor. ASIO_DECL epoll_reactor(asio::io_service& io_service); // Destructor. ASIO_DECL ~epoll_reactor(); // Destroy all user-defined handler objects owned by the service. ASIO_DECL void shutdown_service(); // Initialise the task. ASIO_DECL void init_task(); // Register a socket with the reactor. Returns 0 on success, system error // code on failure. ASIO_DECL int register_descriptor(socket_type descriptor, per_descriptor_data& descriptor_data); // Post a reactor operation for immediate completion. void post_immediate_completion(reactor_op* op) { io_service_.post_immediate_completion(op); } // Start a new operation. The reactor operation will be performed when the // given descriptor is flagged as ready, or an error has occurred. ASIO_DECL void start_op(int op_type, socket_type descriptor, per_descriptor_data& descriptor_data, reactor_op* op, bool allow_speculative); // Cancel all operations associated with the given descriptor. The // handlers associated with the descriptor will be invoked with the // operation_aborted error. ASIO_DECL void cancel_ops(socket_type descriptor, per_descriptor_data& descriptor_data); // Cancel any operations that are running against the descriptor and remove // its registration from the reactor. ASIO_DECL void close_descriptor(socket_type descriptor, per_descriptor_data& descriptor_data); // Add a new timer queue to the reactor. template void add_timer_queue(timer_queue& timer_queue); // Remove a timer queue from the reactor. template void remove_timer_queue(timer_queue& timer_queue); // Schedule a new operation in the given timer queue to expire at the // specified absolute time. template void schedule_timer(timer_queue& queue, const typename Time_Traits::time_type& time, typename timer_queue::per_timer_data& timer, timer_op* op); // Cancel the timer operations associated with the given token. Returns the // number of operations that have been posted or dispatched. template std::size_t cancel_timer(timer_queue& queue, typename timer_queue::per_timer_data& timer); // Run epoll once until interrupted or events are ready to be dispatched. ASIO_DECL void run(bool block, op_queue& ops); // Interrupt the select loop. ASIO_DECL void interrupt(); private: // The hint to pass to epoll_create to size its data structures. enum { epoll_size = 20000 }; // Create the epoll file descriptor. Throws an exception if the descriptor // cannot be created. ASIO_DECL static int do_epoll_create(); // Helper function to add a new timer queue. ASIO_DECL void do_add_timer_queue(timer_queue_base& queue); // Helper function to remove a timer queue. ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue); // Called to recalculate and update the timeout. ASIO_DECL void update_timeout(); // Get the timeout value for the epoll_wait call. The timeout value is // returned as a number of milliseconds. A return value of -1 indicates // that epoll_wait should block indefinitely. ASIO_DECL int get_timeout(); #if defined(ASIO_HAS_TIMERFD) // Get the timeout value for the timer descriptor. The return value is the // flag argument to be used when calling timerfd_settime. ASIO_DECL int get_timeout(itimerspec& ts); #endif // defined(ASIO_HAS_TIMERFD) // The io_service implementation used to post completions. io_service_impl& io_service_; // Mutex to protect access to internal data. mutex mutex_; // The epoll file descriptor. int epoll_fd_; // The timer file descriptor. int timer_fd_; // The interrupter is used to break a blocking epoll_wait call. select_interrupter interrupter_; // The timer queues. timer_queue_set timer_queues_; // Whether the service has been shut down. bool shutdown_; // Mutex to protect access to the registered descriptors. mutex registered_descriptors_mutex_; // Keep track of all registered descriptors. object_pool registered_descriptors_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #include "asio/detail/impl/epoll_reactor.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/epoll_reactor.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // defined(ASIO_HAS_EPOLL) #endif // ASIO_DETAIL_EPOLL_REACTOR_HPP percona-xtradb-cluster-galera/asio/asio/detail/epoll_reactor_fwd.hpp0000644000000000000000000000135412247075736026210 0ustar rootroot00000000000000// // detail/epoll_reactor_fwd.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_EPOLL_REACTOR_FWD_HPP #define ASIO_DETAIL_EPOLL_REACTOR_FWD_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_EPOLL) namespace asio { namespace detail { class epoll_reactor; } // namespace detail } // namespace asio #endif // defined(ASIO_HAS_EPOLL) #endif // ASIO_DETAIL_EPOLL_REACTOR_FWD_HPP percona-xtradb-cluster-galera/asio/asio/detail/event.hpp0000644000000000000000000000214112247075736023632 0ustar rootroot00000000000000// // detail/event.hpp // ~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_EVENT_HPP #define ASIO_DETAIL_EVENT_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) # include "asio/detail/null_event.hpp" #elif defined(BOOST_WINDOWS) # include "asio/detail/win_event.hpp" #elif defined(BOOST_HAS_PTHREADS) # include "asio/detail/posix_event.hpp" #else # error Only Windows and POSIX are supported! #endif namespace asio { namespace detail { #if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) typedef null_event event; #elif defined(BOOST_WINDOWS) typedef win_event event; #elif defined(BOOST_HAS_PTHREADS) typedef posix_event event; #endif } // namespace detail } // namespace asio #endif // ASIO_DETAIL_EVENT_HPP percona-xtradb-cluster-galera/asio/asio/detail/eventfd_select_interrupter.hpp0000644000000000000000000000412412247075736030151 0ustar rootroot00000000000000// // detail/eventfd_select_interrupter.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2008 Roelof Naude (roelof.naude at gmail dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_EVENTFD_SELECT_INTERRUPTER_HPP #define ASIO_DETAIL_EVENTFD_SELECT_INTERRUPTER_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_EVENTFD) #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class eventfd_select_interrupter { public: // Constructor. ASIO_DECL eventfd_select_interrupter(); // Destructor. ASIO_DECL ~eventfd_select_interrupter(); // Interrupt the select call. ASIO_DECL void interrupt(); // Reset the select interrupt. Returns true if the call was interrupted. ASIO_DECL bool reset(); // Get the read descriptor to be passed to select. int read_descriptor() const { return read_descriptor_; } private: // The read end of a connection used to interrupt the select call. This file // descriptor is passed to select such that when it is time to stop, a single // 64bit value will be written on the other end of the connection and this // descriptor will become readable. int read_descriptor_; // The write end of a connection used to interrupt the select call. A single // 64bit non-zero value may be written to this to wake up the select which is // waiting for the other end to become readable. This descriptor will only // differ from the read descriptor when a pipe is used. int write_descriptor_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/eventfd_select_interrupter.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // defined(ASIO_HAS_EVENTFD) #endif // ASIO_DETAIL_EVENTFD_SELECT_INTERRUPTER_HPP percona-xtradb-cluster-galera/asio/asio/detail/fd_set_adapter.hpp0000644000000000000000000000157512247075736025467 0ustar rootroot00000000000000// // detail/fd_set_adapter.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_FD_SET_ADAPTER_HPP #define ASIO_DETAIL_FD_SET_ADAPTER_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/detail/posix_fd_set_adapter.hpp" #include "asio/detail/win_fd_set_adapter.hpp" namespace asio { namespace detail { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) typedef win_fd_set_adapter fd_set_adapter; #else typedef posix_fd_set_adapter fd_set_adapter; #endif } // namespace detail } // namespace asio #endif // ASIO_DETAIL_FD_SET_ADAPTER_HPP percona-xtradb-cluster-galera/asio/asio/detail/fenced_block.hpp0000644000000000000000000000551712247075736025121 0ustar rootroot00000000000000// // detail/fenced_block.hpp // ~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_FENCED_BLOCK_HPP #define ASIO_DETAIL_FENCED_BLOCK_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" // #763 - Galera library is multithreaded so make sure that ASIO will have // MT support too. #if !defined(BOOST_HAS_THREADS) #error "BOOST_HAS_THREADS not defined" #endif #if !defined(BOOST_HAS_THREADS) \ || defined(ASIO_DISABLE_THREADS) \ || defined(ASIO_DISABLE_FENCED_BLOCK) # include "asio/detail/null_fenced_block.hpp" #elif defined(__MACH__) && defined(__APPLE__) # include "asio/detail/macos_fenced_block.hpp" #elif defined(__sun) # include "asio/detail/solaris_fenced_block.hpp" #elif defined(__GNUC__) && defined(__arm__) # include "asio/detail/gcc_arm_fenced_block.hpp" #elif defined(__GNUC__) && (defined(__hppa) || defined(__hppa__)) # include "asio/detail/gcc_hppa_fenced_block.hpp" #elif defined(__GNUC__) \ && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) \ && !defined(__INTEL_COMPILER) && !defined(__ICL) \ && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__) # include "asio/detail/gcc_sync_fenced_block.hpp" #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) # include "asio/detail/gcc_x86_fenced_block.hpp" #elif defined(BOOST_WINDOWS) && !defined(UNDER_CE) # include "asio/detail/win_fenced_block.hpp" #else # include "asio/detail/null_fenced_block.hpp" #endif namespace asio { namespace detail { #if !defined(BOOST_HAS_THREADS) \ || defined(ASIO_DISABLE_THREADS) \ || defined(ASIO_DISABLE_FENCED_BLOCK) typedef null_fenced_block fenced_block; #elif defined(__MACH__) && defined(__APPLE__) typedef macos_fenced_block fenced_block; #elif defined(__sun) typedef solaris_fenced_block fenced_block; #elif defined(__GNUC__) && defined(__arm__) typedef gcc_arm_fenced_block fenced_block; #elif defined(__GNUC__) && (defined(__hppa) || defined(__hppa__)) typedef gcc_hppa_fenced_block fenced_block; #elif defined(__GNUC__) \ && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) \ && !defined(__INTEL_COMPILER) && !defined(__ICL) \ && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__) typedef gcc_sync_fenced_block fenced_block; #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) typedef gcc_x86_fenced_block fenced_block; #elif defined(BOOST_WINDOWS) && !defined(UNDER_CE) typedef win_fenced_block fenced_block; #else typedef null_fenced_block fenced_block; #endif } // namespace detail } // namespace asio #endif // ASIO_DETAIL_FENCED_BLOCK_HPP percona-xtradb-cluster-galera/asio/asio/detail/gcc_arm_fenced_block.hpp0000644000000000000000000000331212247075736026563 0ustar rootroot00000000000000// // detail/gcc_arm_fenced_block.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_GCC_ARM_FENCED_BLOCK_HPP #define ASIO_DETAIL_GCC_ARM_FENCED_BLOCK_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(__GNUC__) && defined(__arm__) #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class gcc_arm_fenced_block : private noncopyable { public: // Constructor. gcc_arm_fenced_block() { barrier(); } // Destructor. ~gcc_arm_fenced_block() { barrier(); } private: static void barrier() { #if defined(__ARM_ARCH_4__) \ || defined(__ARM_ARCH_4T__) \ || defined(__ARM_ARCH_5__) \ || defined(__ARM_ARCH_5E__) \ || defined(__ARM_ARCH_5T__) \ || defined(__ARM_ARCH_5TE__) \ || defined(__ARM_ARCH_5TEJ__) \ || defined(__ARM_ARCH_6__) \ || defined(__ARM_ARCH_6J__) \ || defined(__ARM_ARCH_6K__) \ || defined(__ARM_ARCH_6Z__) \ || defined(__ARM_ARCH_6ZK__) \ || defined(__ARM_ARCH_6T2__) int a = 0, b = 0; __asm__ __volatile__ ("swp %0, %1, [%2]" : "=&r"(a) : "r"(1), "r"(&b) : "memory", "cc"); #else // ARMv7 and later. __asm__ __volatile__ ("dmb" : : : "memory"); #endif } }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(__GNUC__) && defined(__arm__) #endif // ASIO_DETAIL_GCC_ARM_FENCED_BLOCK_HPP percona-xtradb-cluster-galera/asio/asio/detail/gcc_fenced_block.hpp0000644000000000000000000000275312247075736025734 0ustar rootroot00000000000000// // gcc_fenced_block.hpp // ~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_GCC_FENCED_BLOCK_HPP #define ASIO_DETAIL_GCC_FENCED_BLOCK_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/push_options.hpp" #include "asio/detail/push_options.hpp" #include #include "asio/detail/pop_options.hpp" #if defined(__GNUC__) \ && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) \ && !defined(__INTEL_COMPILER) && !defined(__ICL) \ && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__) namespace asio { namespace detail { class gcc_fenced_block : private noncopyable { public: // Constructor. gcc_fenced_block() : value_(0) { __sync_lock_test_and_set(&value_, 1); } // Destructor. ~gcc_fenced_block() { __sync_lock_release(&value_); } private: int value_; }; } // namespace detail } // namespace asio #endif // defined(__GNUC__) // && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) // && !defined(__INTEL_COMPILER) && !defined(__ICL) // && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__) #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_GCC_FENCED_BLOCK_HPP percona-xtradb-cluster-galera/asio/asio/detail/gcc_hppa_fenced_block.hpp0000644000000000000000000000235312247075736026740 0ustar rootroot00000000000000// // detail/gcc_hppa_fenced_block.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_GCC_HPPA_FENCED_BLOCK_HPP #define ASIO_DETAIL_GCC_HPPA_FENCED_BLOCK_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(__GNUC__) && (defined(__hppa) || defined(__hppa__)) #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class gcc_hppa_fenced_block : private noncopyable { public: // Constructor. gcc_hppa_fenced_block() { barrier(); } // Destructor. ~gcc_hppa_fenced_block() { barrier(); } private: static void barrier() { // This is just a placeholder and almost certainly not sufficient. __asm__ __volatile__ ("" : : : "memory"); } }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(__GNUC__) && (defined(__hppa) || defined(__hppa__)) #endif // ASIO_DETAIL_GCC_HPPA_FENCED_BLOCK_HPP percona-xtradb-cluster-galera/asio/asio/detail/gcc_sync_fenced_block.hpp0000644000000000000000000000273012247075736026763 0ustar rootroot00000000000000// // detail/gcc_sync_fenced_block.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_GCC_SYNC_FENCED_BLOCK_HPP #define ASIO_DETAIL_GCC_SYNC_FENCED_BLOCK_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(__GNUC__) \ && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) \ && !defined(__INTEL_COMPILER) && !defined(__ICL) \ && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__) #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class gcc_sync_fenced_block : private noncopyable { public: // Constructor. gcc_sync_fenced_block() : value_(0) { __sync_lock_test_and_set(&value_, 1); } // Destructor. ~gcc_sync_fenced_block() { __sync_lock_release(&value_); } private: int value_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(__GNUC__) // && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) // && !defined(__INTEL_COMPILER) && !defined(__ICL) // && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__) #endif // ASIO_DETAIL_GCC_SYNC_FENCED_BLOCK_HPP percona-xtradb-cluster-galera/asio/asio/detail/gcc_x86_fenced_block.hpp0000644000000000000000000000233612247075736026436 0ustar rootroot00000000000000// // detail/gcc_x86_fenced_block.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_GCC_X86_FENCED_BLOCK_HPP #define ASIO_DETAIL_GCC_X86_FENCED_BLOCK_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class gcc_x86_fenced_block : private noncopyable { public: // Constructor. gcc_x86_fenced_block() { barrier(); } // Destructor. ~gcc_x86_fenced_block() { barrier(); } private: static int barrier() { int r = 0; __asm__ __volatile__ ("xchgl %%eax, %0" : "=m" (r) : : "memory", "cc"); return r; } }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) #endif // ASIO_DETAIL_GCC_X86_FENCED_BLOCK_HPP percona-xtradb-cluster-galera/asio/asio/detail/handler_alloc_helpers.hpp0000644000000000000000000000414612247075736027031 0ustar rootroot00000000000000// // detail/handler_alloc_helpers.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP #define ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include "asio/detail/noncopyable.hpp" #include "asio/handler_alloc_hook.hpp" #include "asio/detail/push_options.hpp" // Calls to asio_handler_allocate and asio_handler_deallocate must be made from // a namespace that does not contain any overloads of these functions. The // asio_handler_alloc_helpers namespace is defined here for that purpose. namespace asio_handler_alloc_helpers { template inline void* allocate(std::size_t s, Handler& h) { #if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) \ || BOOST_WORKAROUND(__GNUC__, < 3) return ::operator new(s); #else using namespace asio; return asio_handler_allocate(s, boost::addressof(h)); #endif } template inline void deallocate(void* p, std::size_t s, Handler& h) { #if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) \ || BOOST_WORKAROUND(__GNUC__, < 3) ::operator delete(p); #else using namespace asio; asio_handler_deallocate(p, s, boost::addressof(h)); #endif } } // namespace asio_handler_alloc_helpers #define ASIO_DEFINE_HANDLER_PTR(op) \ struct ptr \ { \ Handler* h; \ void* v; \ op* p; \ ~ptr() \ { \ reset(); \ } \ void reset() \ { \ if (p) \ { \ p->~op(); \ p = 0; \ } \ if (v) \ { \ asio_handler_alloc_helpers::deallocate(v, sizeof(op), *h); \ v = 0; \ } \ } \ } \ /**/ #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP percona-xtradb-cluster-galera/asio/asio/detail/handler_invoke_helpers.hpp0000644000000000000000000000262612247075736027233 0ustar rootroot00000000000000// // detail/handler_invoke_helpers.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP #define ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include "asio/handler_invoke_hook.hpp" #include "asio/detail/push_options.hpp" // Calls to asio_handler_invoke must be made from a namespace that does not // contain overloads of this function. The asio_handler_invoke_helpers // namespace is defined here for that purpose. namespace asio_handler_invoke_helpers { template inline void invoke(const Function& function, Context& context) { #if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) \ || BOOST_WORKAROUND(__GNUC__, < 3) Function tmp(function); tmp(); #else using namespace asio; asio_handler_invoke(function, boost::addressof(context)); #endif } } // namespace asio_handler_invoke_helpers #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP percona-xtradb-cluster-galera/asio/asio/detail/hash_map.hpp0000644000000000000000000001755212247075736024305 0ustar rootroot00000000000000// // detail/hash_map.hpp // ~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_HASH_MAP_HPP #define ASIO_DETAIL_HASH_MAP_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include #include "asio/detail/noncopyable.hpp" #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) # include "asio/detail/socket_types.hpp" #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) #include "asio/detail/push_options.hpp" namespace asio { namespace detail { inline std::size_t calculate_hash_value(int i) { return static_cast(i); } inline std::size_t calculate_hash_value(void* p) { return reinterpret_cast(p) + (reinterpret_cast(p) >> 3); } #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) inline std::size_t calculate_hash_value(SOCKET s) { return static_cast(s); } #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) // Note: assumes K and V are POD types. template class hash_map : private noncopyable { public: // The type of a value in the map. typedef std::pair value_type; // The type of a non-const iterator over the hash map. typedef typename std::list::iterator iterator; // The type of a const iterator over the hash map. typedef typename std::list::const_iterator const_iterator; // Constructor. hash_map() : size_(0), buckets_(0), num_buckets_(0) { } // Destructor. ~hash_map() { delete[] buckets_; } // Get an iterator for the beginning of the map. iterator begin() { return values_.begin(); } // Get an iterator for the beginning of the map. const_iterator begin() const { return values_.begin(); } // Get an iterator for the end of the map. iterator end() { return values_.end(); } // Get an iterator for the end of the map. const_iterator end() const { return values_.end(); } // Check whether the map is empty. bool empty() const { return values_.empty(); } // Find an entry in the map. iterator find(const K& k) { if (num_buckets_) { size_t bucket = calculate_hash_value(k) % num_buckets_; iterator it = buckets_[bucket].first; if (it == values_.end()) return values_.end(); iterator end = buckets_[bucket].last; ++end; while (it != end) { if (it->first == k) return it; ++it; } } return values_.end(); } // Find an entry in the map. const_iterator find(const K& k) const { if (num_buckets_) { size_t bucket = calculate_hash_value(k) % num_buckets_; const_iterator it = buckets_[bucket].first; if (it == values_.end()) return it; const_iterator end = buckets_[bucket].last; ++end; while (it != end) { if (it->first == k) return it; ++it; } } return values_.end(); } // Insert a new entry into the map. std::pair insert(const value_type& v) { if (size_ + 1 >= num_buckets_) rehash(hash_size(size_ + 1)); size_t bucket = calculate_hash_value(v.first) % num_buckets_; iterator it = buckets_[bucket].first; if (it == values_.end()) { buckets_[bucket].first = buckets_[bucket].last = values_insert(values_.end(), v); ++size_; return std::pair(buckets_[bucket].last, true); } iterator end = buckets_[bucket].last; ++end; while (it != end) { if (it->first == v.first) return std::pair(it, false); ++it; } buckets_[bucket].last = values_insert(end, v); ++size_; return std::pair(buckets_[bucket].last, true); } // Erase an entry from the map. void erase(iterator it) { assert(it != values_.end()); size_t bucket = calculate_hash_value(it->first) % num_buckets_; bool is_first = (it == buckets_[bucket].first); bool is_last = (it == buckets_[bucket].last); if (is_first && is_last) buckets_[bucket].first = buckets_[bucket].last = values_.end(); else if (is_first) ++buckets_[bucket].first; else if (is_last) --buckets_[bucket].last; values_erase(it); --size_; } // Erase a key from the map. void erase(const K& k) { iterator it = find(k); if (it != values_.end()) erase(it); } // Remove all entries from the map. void clear() { // Clear the values. values_.clear(); size_ = 0; // Initialise all buckets to empty. iterator end = values_.end(); for (size_t i = 0; i < num_buckets_; ++i) buckets_[i].first = buckets_[i].last = end; } private: // Calculate the hash size for the specified number of elements. static std::size_t hash_size(std::size_t num_elems) { static std::size_t sizes[] = { #if defined(ASIO_HASH_MAP_BUCKETS) ASIO_HASH_MAP_BUCKETS #else // ASIO_HASH_MAP_BUCKETS 3, 13, 23, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469, 12582917, 25165843 #endif // ASIO_HASH_MAP_BUCKETS }; const std::size_t nth_size = sizeof(sizes) / sizeof(std::size_t) - 1; for (std::size_t i = 0; i < nth_size; ++i) if (num_elems < sizes[i]) return sizes[i]; return sizes[nth_size]; } // Re-initialise the hash from the values already contained in the list. void rehash(std::size_t num_buckets) { if (num_buckets == num_buckets_) return; num_buckets_ = num_buckets; iterator end = values_.end(); // Update number of buckets and initialise all buckets to empty. bucket_type* tmp = new bucket_type[num_buckets_]; delete[] buckets_; buckets_ = tmp; for (std::size_t i = 0; i < num_buckets_; ++i) buckets_[i].first = buckets_[i].last = end; // Put all values back into the hash. iterator iter = values_.begin(); while (iter != end) { std::size_t bucket = calculate_hash_value(iter->first) % num_buckets_; if (buckets_[bucket].last == end) { buckets_[bucket].first = buckets_[bucket].last = iter++; } else if (++buckets_[bucket].last == iter) { ++iter; } else { values_.splice(buckets_[bucket].last, values_, iter++); --buckets_[bucket].last; } } } // Insert an element into the values list by splicing from the spares list, // if a spare is available, and otherwise by inserting a new element. iterator values_insert(iterator it, const value_type& v) { if (spares_.empty()) { return values_.insert(it, v); } else { spares_.front() = v; values_.splice(it, spares_, spares_.begin()); return --it; } } // Erase an element from the values list by splicing it to the spares list. void values_erase(iterator it) { *it = value_type(); spares_.splice(spares_.begin(), values_, it); } // The number of elements in the hash. std::size_t size_; // The list of all values in the hash map. std::list values_; // The list of spare nodes waiting to be recycled. Assumes that POD types only // are stored in the hash map. std::list spares_; // The type for a bucket in the hash table. struct bucket_type { iterator first; iterator last; }; // The buckets in the hash. bucket_type* buckets_; // The number of buckets in the hash. std::size_t num_buckets_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_HASH_MAP_HPP percona-xtradb-cluster-galera/asio/asio/detail/impl/0000755000000000000000000000000012247075736022743 5ustar rootroot00000000000000percona-xtradb-cluster-galera/asio/asio/detail/io_control.hpp0000644000000000000000000000527212247075736024670 0ustar rootroot00000000000000// // detail/io_control.hpp // ~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IO_CONTROL_HPP #define ASIO_DETAIL_IO_CONTROL_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include "asio/detail/socket_types.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { namespace io_control { // IO control command for non-blocking I/O. class non_blocking_io { public: // Default constructor. non_blocking_io() : value_(0) { } // Construct with a specific command value. non_blocking_io(bool value) : value_(value ? 1 : 0) { } // Get the name of the IO control command. // On Linux/PPC this is a 32-bit unsigned value, // GCC 4.4.6 complains about overflow if using signed int unsigned int name() const { return FIONBIO; } // Set the value of the I/O control command. void set(bool value) { value_ = value ? 1 : 0; } // Get the current value of the I/O control command. bool get() const { return value_ != 0; } // Get the address of the command data. detail::ioctl_arg_type* data() { return &value_; } // Get the address of the command data. const detail::ioctl_arg_type* data() const { return &value_; } private: detail::ioctl_arg_type value_; }; // I/O control command for getting number of bytes available. class bytes_readable { public: // Default constructor. bytes_readable() : value_(0) { } // Construct with a specific command value. bytes_readable(std::size_t value) : value_(static_cast(value)) { } // Get the name of the IO control command. int name() const { return FIONREAD; } // Set the value of the I/O control command. void set(std::size_t value) { value_ = static_cast(value); } // Get the current value of the I/O control command. std::size_t get() const { return static_cast(value_); } // Get the address of the command data. detail::ioctl_arg_type* data() { return &value_; } // Get the address of the command data. const detail::ioctl_arg_type* data() const { return &value_; } private: detail::ioctl_arg_type value_; }; } // namespace io_control } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_IO_CONTROL_HPP percona-xtradb-cluster-galera/asio/asio/detail/kqueue_reactor.hpp0000644000000000000000000001337012247075736025535 0ustar rootroot00000000000000// // detail/kqueue_reactor.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2005 Stefan Arentz (stefan at soze dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_KQUEUE_REACTOR_HPP #define ASIO_DETAIL_KQUEUE_REACTOR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_KQUEUE) #include #include #include #include #include "asio/detail/kqueue_reactor_fwd.hpp" #include "asio/detail/mutex.hpp" #include "asio/detail/object_pool.hpp" #include "asio/detail/op_queue.hpp" #include "asio/detail/reactor_op.hpp" #include "asio/detail/select_interrupter.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/timer_op.hpp" #include "asio/detail/timer_queue_base.hpp" #include "asio/detail/timer_queue_fwd.hpp" #include "asio/detail/timer_queue_set.hpp" #include "asio/error.hpp" #include "asio/io_service.hpp" // Older versions of Mac OS X may not define EV_OOBAND. #if !defined(EV_OOBAND) # define EV_OOBAND EV_FLAG1 #endif // !defined(EV_OOBAND) #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class kqueue_reactor : public asio::detail::service_base { public: enum op_types { read_op = 0, write_op = 1, connect_op = 1, except_op = 2, max_ops = 3 }; // Per-descriptor queues. struct descriptor_state { friend class kqueue_reactor; friend class object_pool_access; mutex mutex_; op_queue op_queue_[max_ops]; bool shutdown_; descriptor_state* next_; descriptor_state* prev_; }; // Per-descriptor data. typedef descriptor_state* per_descriptor_data; // Constructor. ASIO_DECL kqueue_reactor(asio::io_service& io_service); // Destructor. ASIO_DECL ~kqueue_reactor(); // Destroy all user-defined handler objects owned by the service. ASIO_DECL void shutdown_service(); // Initialise the task. ASIO_DECL void init_task(); // Register a socket with the reactor. Returns 0 on success, system error // code on failure. ASIO_DECL int register_descriptor(socket_type descriptor, per_descriptor_data& descriptor_data); // Post a reactor operation for immediate completion. void post_immediate_completion(reactor_op* op) { io_service_.post_immediate_completion(op); } // Start a new operation. The reactor operation will be performed when the // given descriptor is flagged as ready, or an error has occurred. ASIO_DECL void start_op(int op_type, socket_type descriptor, per_descriptor_data& descriptor_data, reactor_op* op, bool allow_speculative); // Cancel all operations associated with the given descriptor. The // handlers associated with the descriptor will be invoked with the // operation_aborted error. ASIO_DECL void cancel_ops(socket_type descriptor, per_descriptor_data& descriptor_data); // Cancel any operations that are running against the descriptor and remove // its registration from the reactor. ASIO_DECL void close_descriptor(socket_type descriptor, per_descriptor_data& descriptor_data); // Add a new timer queue to the reactor. template void add_timer_queue(timer_queue& queue); // Remove a timer queue from the reactor. template void remove_timer_queue(timer_queue& queue); // Schedule a new operation in the given timer queue to expire at the // specified absolute time. template void schedule_timer(timer_queue& queue, const typename Time_Traits::time_type& time, typename timer_queue::per_timer_data& timer, timer_op* op); // Cancel the timer operations associated with the given token. Returns the // number of operations that have been posted or dispatched. template std::size_t cancel_timer(timer_queue& queue, typename timer_queue::per_timer_data& timer); // Run the kqueue loop. ASIO_DECL void run(bool block, op_queue& ops); // Interrupt the kqueue loop. ASIO_DECL void interrupt(); private: // Create the kqueue file descriptor. Throws an exception if the descriptor // cannot be created. ASIO_DECL static int do_kqueue_create(); // Helper function to add a new timer queue. ASIO_DECL void do_add_timer_queue(timer_queue_base& queue); // Helper function to remove a timer queue. ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue); // Get the timeout value for the kevent call. ASIO_DECL timespec* get_timeout(timespec& ts); // The io_service implementation used to post completions. io_service_impl& io_service_; // Mutex to protect access to internal data. mutex mutex_; // The kqueue file descriptor. int kqueue_fd_; // The interrupter is used to break a blocking kevent call. select_interrupter interrupter_; // The timer queues. timer_queue_set timer_queues_; // Whether the service has been shut down. bool shutdown_; // Mutex to protect access to the registered descriptors. mutex registered_descriptors_mutex_; // Keep track of all registered descriptors. object_pool registered_descriptors_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #include "asio/detail/impl/kqueue_reactor.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/kqueue_reactor.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // defined(ASIO_HAS_KQUEUE) #endif // ASIO_DETAIL_KQUEUE_REACTOR_HPP percona-xtradb-cluster-galera/asio/asio/detail/kqueue_reactor_fwd.hpp0000644000000000000000000000146112247075736026373 0ustar rootroot00000000000000// // detail/kqueue_reactor_fwd.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2005 Stefan Arentz (stefan at soze dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_KQUEUE_REACTOR_FWD_HPP #define ASIO_DETAIL_KQUEUE_REACTOR_FWD_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_KQUEUE) namespace asio { namespace detail { class kqueue_reactor; } // namespace detail } // namespace asio #endif // defined(ASIO_HAS_KQUEUE) #endif // ASIO_DETAIL_KQUEUE_REACTOR_FWD_HPP percona-xtradb-cluster-galera/asio/asio/detail/local_free_on_block_exit.hpp0000644000000000000000000000241512247075736027507 0ustar rootroot00000000000000// // detail/local_free_on_block_exit.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_LOCAL_FREE_ON_BLOCK_EXIT_HPP #define ASIO_DETAIL_LOCAL_FREE_ON_BLOCK_EXIT_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) #include "asio/detail/noncopyable.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class local_free_on_block_exit : private noncopyable { public: // Constructor blocks all signals for the calling thread. explicit local_free_on_block_exit(void* p) : p_(p) { } // Destructor restores the previous signal mask. ~local_free_on_block_exit() { ::LocalFree(p_); } private: void* p_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) #endif // ASIO_DETAIL_LOCAL_FREE_ON_BLOCK_EXIT_HPP percona-xtradb-cluster-galera/asio/asio/detail/macos_fenced_block.hpp0000644000000000000000000000207312247075736026275 0ustar rootroot00000000000000// // detail/macos_fenced_block.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_MACOS_FENCED_BLOCK_HPP #define ASIO_DETAIL_MACOS_FENCED_BLOCK_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(__MACH__) && defined(__APPLE__) #include #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class macos_fenced_block : private noncopyable { public: // Constructor. macos_fenced_block() { OSMemoryBarrier(); } // Destructor. ~macos_fenced_block() { OSMemoryBarrier(); } }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(__MACH__) && defined(__APPLE__) #endif // ASIO_DETAIL_MACOS_FENCED_BLOCK_HPP percona-xtradb-cluster-galera/asio/asio/detail/mutex.hpp0000644000000000000000000000214112247075736023653 0ustar rootroot00000000000000// // detail/mutex.hpp // ~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_MUTEX_HPP #define ASIO_DETAIL_MUTEX_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) # include "asio/detail/null_mutex.hpp" #elif defined(BOOST_WINDOWS) # include "asio/detail/win_mutex.hpp" #elif defined(BOOST_HAS_PTHREADS) # include "asio/detail/posix_mutex.hpp" #else # error Only Windows and POSIX are supported! #endif namespace asio { namespace detail { #if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) typedef null_mutex mutex; #elif defined(BOOST_WINDOWS) typedef win_mutex mutex; #elif defined(BOOST_HAS_PTHREADS) typedef posix_mutex mutex; #endif } // namespace detail } // namespace asio #endif // ASIO_DETAIL_MUTEX_HPP percona-xtradb-cluster-galera/asio/asio/detail/noncopyable.hpp0000644000000000000000000000237712247075736025035 0ustar rootroot00000000000000// // detail/noncopyable.hpp // ~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_NONCOPYABLE_HPP #define ASIO_DETAIL_NONCOPYABLE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include "asio/detail/push_options.hpp" namespace asio { namespace detail { #if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) // Redefine the noncopyable class for Borland C++ since that compiler does not // apply the empty base optimisation unless the base class contains a dummy // char data member. class noncopyable { protected: noncopyable() {} ~noncopyable() {} private: noncopyable(const noncopyable&); const noncopyable& operator=(const noncopyable&); char dummy_; }; #else using boost::noncopyable; #endif } // namespace detail using asio::detail::noncopyable; } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_NONCOPYABLE_HPP percona-xtradb-cluster-galera/asio/asio/detail/null_buffers_op.hpp0000644000000000000000000000437112247075736025704 0ustar rootroot00000000000000// // null_buffers_op.hpp // ~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_NULL_BUFFERS_OP_HPP #define ASIO_DETAIL_NULL_BUFFERS_OP_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/push_options.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/reactor_op.hpp" namespace asio { namespace detail { template class null_buffers_op : public reactor_op { public: null_buffers_op(Handler handler) : reactor_op(&null_buffers_op::do_perform, &null_buffers_op::do_complete), handler_(handler) { } static bool do_perform(reactor_op*) { return true; } static void do_complete(io_service_impl* owner, operation* base, asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) { // Take ownership of the handler object. null_buffers_op* o(static_cast(base)); typedef handler_alloc_traits alloc_traits; handler_ptr ptr(o->handler_, o); // Make the upcall if required. if (owner) { // Make a copy of the handler so that the memory can be deallocated // before the upcall is made. Even if we're not about to make an upcall, // a sub-object of the handler may be the true owner of the memory // associated with the handler. Consequently, a local copy of the handler // is required to ensure that any owning sub-object remains valid until // after we have deallocated the memory here. detail::binder2 handler(o->handler_, o->ec_, o->bytes_transferred_); ptr.reset(); asio::detail::fenced_block b; asio_handler_invoke_helpers::invoke(handler, handler); } } private: Handler handler_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_NULL_BUFFERS_OP_HPP percona-xtradb-cluster-galera/asio/asio/detail/null_event.hpp0000644000000000000000000000256212247075736024673 0ustar rootroot00000000000000// // detail/null_event.hpp // ~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_NULL_EVENT_HPP #define ASIO_DETAIL_NULL_EVENT_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) #include "asio/detail/noncopyable.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class null_event : private noncopyable { public: // Constructor. null_event() { } // Destructor. ~null_event() { } // Signal the event. template void signal(Lock&) { } // Signal the event and unlock the mutex. template void signal_and_unlock(Lock&) { } // Reset the event. template void clear(Lock&) { } // Wait for the event to become signalled. template void wait(Lock&) { } }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) #endif // ASIO_DETAIL_NULL_EVENT_HPP percona-xtradb-cluster-galera/asio/asio/detail/null_fenced_block.hpp0000644000000000000000000000154312247075736026146 0ustar rootroot00000000000000// // detail/null_fenced_block.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_NULL_FENCED_BLOCK_HPP #define ASIO_DETAIL_NULL_FENCED_BLOCK_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class null_fenced_block : private noncopyable { public: // Constructor. null_fenced_block() { } // Destructor. ~null_fenced_block() { } }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_NULL_FENCED_BLOCK_HPP percona-xtradb-cluster-galera/asio/asio/detail/null_mutex.hpp0000644000000000000000000000230112247075736024703 0ustar rootroot00000000000000// // detail/null_mutex.hpp // ~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_NULL_MUTEX_HPP #define ASIO_DETAIL_NULL_MUTEX_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) #include "asio/detail/noncopyable.hpp" #include "asio/detail/scoped_lock.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class null_mutex : private noncopyable { public: typedef asio::detail::scoped_lock scoped_lock; // Constructor. null_mutex() { } // Destructor. ~null_mutex() { } // Lock the mutex. void lock() { } // Unlock the mutex. void unlock() { } }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) #endif // ASIO_DETAIL_NULL_MUTEX_HPP percona-xtradb-cluster-galera/asio/asio/detail/null_signal_blocker.hpp0000644000000000000000000000275212247075736026531 0ustar rootroot00000000000000// // detail/null_signal_blocker.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP #define ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(BOOST_HAS_THREADS) \ || defined(ASIO_DISABLE_THREADS) \ || defined(BOOST_WINDOWS) \ || defined(__CYGWIN__) \ || defined(__SYMBIAN32__) #include "asio/detail/noncopyable.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class null_signal_blocker : private noncopyable { public: // Constructor blocks all signals for the calling thread. null_signal_blocker() { } // Destructor restores the previous signal mask. ~null_signal_blocker() { } // Block all signals for the calling thread. void block() { } // Restore the previous signal mask. void unblock() { } }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // !defined(BOOST_HAS_THREADS) // || defined(ASIO_DISABLE_THREADS) // || defined(BOOST_WINDOWS) // || defined(__CYGWIN__) // || defined(__SYMBIAN32__) #endif // ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP percona-xtradb-cluster-galera/asio/asio/detail/null_thread.hpp0000644000000000000000000000242312247075736025015 0ustar rootroot00000000000000// // detail/null_thread.hpp // ~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_NULL_THREAD_HPP #define ASIO_DETAIL_NULL_THREAD_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) #include "asio/detail/noncopyable.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class null_thread : private noncopyable { public: // Constructor. template null_thread(Function, unsigned int = 0) { asio::detail::throw_error( asio::error::operation_not_supported, "thread"); } // Destructor. ~null_thread() { } // Wait for the thread to exit. void join() { } }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) #endif // ASIO_DETAIL_NULL_THREAD_HPP percona-xtradb-cluster-galera/asio/asio/detail/null_tss_ptr.hpp0000644000000000000000000000233712247075736025250 0ustar rootroot00000000000000// // detail/null_tss_ptr.hpp // ~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_NULL_TSS_PTR_HPP #define ASIO_DETAIL_NULL_TSS_PTR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) #include "asio/detail/noncopyable.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class null_tss_ptr : private noncopyable { public: // Constructor. null_tss_ptr() : value_(0) { } // Destructor. ~null_tss_ptr() { } // Get the value. operator T*() const { return value_; } // Set the value. void operator=(T* value) { value_ = value; } private: T* value_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) #endif // ASIO_DETAIL_NULL_TSS_PTR_HPP percona-xtradb-cluster-galera/asio/asio/detail/object_pool.hpp0000644000000000000000000000546012247075736025017 0ustar rootroot00000000000000// // detail/object_pool.hpp // ~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_OBJECT_POOL_HPP #define ASIO_DETAIL_OBJECT_POOL_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/noncopyable.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class object_pool; class object_pool_access { public: template static Object* create() { return new Object; } template static void destroy(Object* o) { delete o; } template static Object*& next(Object* o) { return o->next_; } template static Object*& prev(Object* o) { return o->prev_; } }; template class object_pool : private noncopyable { public: // Constructor. object_pool() : live_list_(0), free_list_(0) { } // Destructor destroys all objects. ~object_pool() { destroy_list(live_list_); destroy_list(free_list_); } // Get the object at the start of the live list. Object* first() { return live_list_; } // Allocate a new object. Object* alloc() { Object* o = free_list_; if (o) free_list_ = object_pool_access::next(free_list_); else o = object_pool_access::create(); object_pool_access::next(o) = live_list_; object_pool_access::prev(o) = 0; if (live_list_) object_pool_access::prev(live_list_) = o; live_list_ = o; return o; } // Free an object. Moves it to the free list. No destructors are run. void free(Object* o) { if (live_list_ == o) live_list_ = object_pool_access::next(o); if (object_pool_access::prev(o)) { object_pool_access::next(object_pool_access::prev(o)) = object_pool_access::next(o); } if (object_pool_access::next(o)) { object_pool_access::prev(object_pool_access::next(o)) = object_pool_access::prev(o); } object_pool_access::next(o) = free_list_; object_pool_access::prev(o) = 0; free_list_ = o; } private: // Helper function to destroy all elements in a list. void destroy_list(Object* list) { while (list) { Object* o = list; list = object_pool_access::next(o); object_pool_access::destroy(o); } } // The list of live objects. Object* live_list_; // The free list. Object* free_list_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_OBJECT_POOL_HPP percona-xtradb-cluster-galera/asio/asio/detail/old_win_sdk_compat.hpp0000644000000000000000000001712612247075736026361 0ustar rootroot00000000000000// // detail/old_win_sdk_compat.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP #define ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) // Guess whether we are building against on old Platform SDK. #if !defined(IN6ADDR_ANY_INIT) #define ASIO_HAS_OLD_WIN_SDK 1 #endif // !defined(IN6ADDR_ANY_INIT) #if defined(ASIO_HAS_OLD_WIN_SDK) // Emulation of types that are missing from old Platform SDKs. // // N.B. this emulation is also used if building for a Windows 2000 target with // a recent (i.e. Vista or later) SDK, as the SDK does not provide IPv6 support // in that case. #include "asio/detail/push_options.hpp" namespace asio { namespace detail { enum { sockaddr_storage_maxsize = 128, // Maximum size. sockaddr_storage_alignsize = (sizeof(__int64)), // Desired alignment. sockaddr_storage_pad1size = (sockaddr_storage_alignsize - sizeof(short)), sockaddr_storage_pad2size = (sockaddr_storage_maxsize - (sizeof(short) + sockaddr_storage_pad1size + sockaddr_storage_alignsize)) }; struct sockaddr_storage_emulation { short ss_family; char __ss_pad1[sockaddr_storage_pad1size]; __int64 __ss_align; char __ss_pad2[sockaddr_storage_pad2size]; }; struct in6_addr_emulation { union { u_char Byte[16]; u_short Word[8]; } u; }; #if !defined(s6_addr) # define _S6_un u # define _S6_u8 Byte # define s6_addr _S6_un._S6_u8 #endif // !defined(s6_addr) struct sockaddr_in6_emulation { short sin6_family; u_short sin6_port; u_long sin6_flowinfo; in6_addr_emulation sin6_addr; u_long sin6_scope_id; }; struct ipv6_mreq_emulation { in6_addr_emulation ipv6mr_multiaddr; unsigned int ipv6mr_interface; }; #if !defined(IN6ADDR_ANY_INIT) # define IN6ADDR_ANY_INIT { 0 } #endif #if !defined(IN6ADDR_LOOPBACK_INIT) # define IN6ADDR_LOOPBACK_INIT { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } #endif struct addrinfo_emulation { int ai_flags; int ai_family; int ai_socktype; int ai_protocol; size_t ai_addrlen; char* ai_canonname; sockaddr* ai_addr; addrinfo_emulation* ai_next; }; #if !defined(AI_PASSIVE) # define AI_PASSIVE 0x1 #endif #if !defined(AI_CANONNAME) # define AI_CANONNAME 0x2 #endif #if !defined(AI_NUMERICHOST) # define AI_NUMERICHOST 0x4 #endif #if !defined(EAI_AGAIN) # define EAI_AGAIN WSATRY_AGAIN #endif #if !defined(EAI_BADFLAGS) # define EAI_BADFLAGS WSAEINVAL #endif #if !defined(EAI_FAIL) # define EAI_FAIL WSANO_RECOVERY #endif #if !defined(EAI_FAMILY) # define EAI_FAMILY WSAEAFNOSUPPORT #endif #if !defined(EAI_MEMORY) # define EAI_MEMORY WSA_NOT_ENOUGH_MEMORY #endif #if !defined(EAI_NODATA) # define EAI_NODATA WSANO_DATA #endif #if !defined(EAI_NONAME) # define EAI_NONAME WSAHOST_NOT_FOUND #endif #if !defined(EAI_SERVICE) # define EAI_SERVICE WSATYPE_NOT_FOUND #endif #if !defined(EAI_SOCKTYPE) # define EAI_SOCKTYPE WSAESOCKTNOSUPPORT #endif #if !defined(NI_NOFQDN) # define NI_NOFQDN 0x01 #endif #if !defined(NI_NUMERICHOST) # define NI_NUMERICHOST 0x02 #endif #if !defined(NI_NAMEREQD) # define NI_NAMEREQD 0x04 #endif #if !defined(NI_NUMERICSERV) # define NI_NUMERICSERV 0x08 #endif #if !defined(NI_DGRAM) # define NI_DGRAM 0x10 #endif #if !defined(IPPROTO_IPV6) # define IPPROTO_IPV6 41 #endif #if !defined(IPV6_UNICAST_HOPS) # define IPV6_UNICAST_HOPS 4 #endif #if !defined(IPV6_MULTICAST_IF) # define IPV6_MULTICAST_IF 9 #endif #if !defined(IPV6_MULTICAST_HOPS) # define IPV6_MULTICAST_HOPS 10 #endif #if !defined(IPV6_MULTICAST_LOOP) # define IPV6_MULTICAST_LOOP 11 #endif #if !defined(IPV6_JOIN_GROUP) # define IPV6_JOIN_GROUP 12 #endif #if !defined(IPV6_LEAVE_GROUP) # define IPV6_LEAVE_GROUP 13 #endif inline int IN6_IS_ADDR_UNSPECIFIED(const in6_addr_emulation* a) { return ((a->s6_addr[0] == 0) && (a->s6_addr[1] == 0) && (a->s6_addr[2] == 0) && (a->s6_addr[3] == 0) && (a->s6_addr[4] == 0) && (a->s6_addr[5] == 0) && (a->s6_addr[6] == 0) && (a->s6_addr[7] == 0) && (a->s6_addr[8] == 0) && (a->s6_addr[9] == 0) && (a->s6_addr[10] == 0) && (a->s6_addr[11] == 0) && (a->s6_addr[12] == 0) && (a->s6_addr[13] == 0) && (a->s6_addr[14] == 0) && (a->s6_addr[15] == 0)); } inline int IN6_IS_ADDR_LOOPBACK(const in6_addr_emulation* a) { return ((a->s6_addr[0] == 0) && (a->s6_addr[1] == 0) && (a->s6_addr[2] == 0) && (a->s6_addr[3] == 0) && (a->s6_addr[4] == 0) && (a->s6_addr[5] == 0) && (a->s6_addr[6] == 0) && (a->s6_addr[7] == 0) && (a->s6_addr[8] == 0) && (a->s6_addr[9] == 0) && (a->s6_addr[10] == 0) && (a->s6_addr[11] == 0) && (a->s6_addr[12] == 0) && (a->s6_addr[13] == 0) && (a->s6_addr[14] == 0) && (a->s6_addr[15] == 1)); } inline int IN6_IS_ADDR_MULTICAST(const in6_addr_emulation* a) { return (a->s6_addr[0] == 0xff); } inline int IN6_IS_ADDR_LINKLOCAL(const in6_addr_emulation* a) { return ((a->s6_addr[0] == 0xfe) && ((a->s6_addr[1] & 0xc0) == 0x80)); } inline int IN6_IS_ADDR_SITELOCAL(const in6_addr_emulation* a) { return ((a->s6_addr[0] == 0xfe) && ((a->s6_addr[1] & 0xc0) == 0xc0)); } inline int IN6_IS_ADDR_V4MAPPED(const in6_addr_emulation* a) { return ((a->s6_addr[0] == 0) && (a->s6_addr[1] == 0) && (a->s6_addr[2] == 0) && (a->s6_addr[3] == 0) && (a->s6_addr[4] == 0) && (a->s6_addr[5] == 0) && (a->s6_addr[6] == 0) && (a->s6_addr[7] == 0) && (a->s6_addr[8] == 0) && (a->s6_addr[9] == 0) && (a->s6_addr[10] == 0xff) && (a->s6_addr[11] == 0xff)); } inline int IN6_IS_ADDR_V4COMPAT(const in6_addr_emulation* a) { return ((a->s6_addr[0] == 0) && (a->s6_addr[1] == 0) && (a->s6_addr[2] == 0) && (a->s6_addr[3] == 0) && (a->s6_addr[4] == 0) && (a->s6_addr[5] == 0) && (a->s6_addr[6] == 0) && (a->s6_addr[7] == 0) && (a->s6_addr[8] == 0) && (a->s6_addr[9] == 0) && (a->s6_addr[10] == 0xff) && (a->s6_addr[11] == 0xff) && !((a->s6_addr[12] == 0) && (a->s6_addr[13] == 0) && (a->s6_addr[14] == 0) && ((a->s6_addr[15] == 0) || (a->s6_addr[15] == 1)))); } inline int IN6_IS_ADDR_MC_NODELOCAL(const in6_addr_emulation* a) { return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 1); } inline int IN6_IS_ADDR_MC_LINKLOCAL(const in6_addr_emulation* a) { return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 2); } inline int IN6_IS_ADDR_MC_SITELOCAL(const in6_addr_emulation* a) { return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 5); } inline int IN6_IS_ADDR_MC_ORGLOCAL(const in6_addr_emulation* a) { return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 8); } inline int IN6_IS_ADDR_MC_GLOBAL(const in6_addr_emulation* a) { return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 0xe); } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_OLD_WIN_SDK) // Even newer Platform SDKs that support IPv6 may not define IPV6_V6ONLY. #if !defined(IPV6_V6ONLY) # define IPV6_V6ONLY 27 #endif // Some SDKs (e.g. Windows CE) don't define IPPROTO_ICMPV6. #if !defined(IPPROTO_ICMPV6) # define IPPROTO_ICMPV6 58 #endif #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) #endif // ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP percona-xtradb-cluster-galera/asio/asio/detail/op_queue.hpp0000644000000000000000000000601212247075736024334 0ustar rootroot00000000000000// // detail/op_queue.hpp // ~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_OP_QUEUE_HPP #define ASIO_DETAIL_OP_QUEUE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/noncopyable.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class op_queue; class op_queue_access { public: template static Operation* next(Operation* o) { return static_cast(o->next_); } template static void next(Operation1*& o1, Operation2* o2) { o1->next_ = o2; } template static void destroy(Operation* o) { o->destroy(); } template static Operation*& front(op_queue& q) { return q.front_; } template static Operation*& back(op_queue& q) { return q.back_; } }; template class op_queue : private noncopyable { public: // Constructor. op_queue() : front_(0), back_(0) { } // Destructor destroys all operations. ~op_queue() { while (Operation* op = front_) { pop(); op_queue_access::destroy(op); } } // Get the operation at the front of the queue. Operation* front() { return front_; } // Pop an operation from the front of the queue. void pop() { if (front_) { Operation* tmp = front_; front_ = op_queue_access::next(front_); if (front_ == 0) back_ = 0; op_queue_access::next(tmp, static_cast(0)); } } // Push an operation on to the back of the queue. void push(Operation* h) { op_queue_access::next(h, static_cast(0)); if (back_) { op_queue_access::next(back_, h); back_ = h; } else { front_ = back_ = h; } } // Push all operations from another queue on to the back of the queue. The // source queue may contain operations of a derived type. template void push(op_queue& q) { if (Operation* other_front = op_queue_access::front(q)) { if (back_) op_queue_access::next(back_, other_front); else front_ = other_front; back_ = op_queue_access::back(q); op_queue_access::front(q) = 0; op_queue_access::back(q) = 0; } } // Whether the queue is empty. bool empty() const { return front_ == 0; } private: friend class op_queue_access; // The front of the queue. Operation* front_; // The back of the queue. Operation* back_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_OP_QUEUE_HPP percona-xtradb-cluster-galera/asio/asio/detail/operation.hpp0000644000000000000000000000157012247075736024516 0ustar rootroot00000000000000// // detail/operation.hpp // ~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_OPERATION_HPP #define ASIO_DETAIL_OPERATION_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) # include "asio/detail/win_iocp_operation.hpp" #else # include "asio/detail/task_io_service_operation.hpp" #endif namespace asio { namespace detail { #if defined(ASIO_HAS_IOCP) typedef win_iocp_operation operation; #else typedef task_io_service_operation operation; #endif } // namespace detail } // namespace asio #endif // ASIO_DETAIL_OPERATION_HPP percona-xtradb-cluster-galera/asio/asio/detail/pipe_select_interrupter.hpp0000644000000000000000000000410112247075736027446 0ustar rootroot00000000000000// // detail/pipe_select_interrupter.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP #define ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(BOOST_WINDOWS) #if !defined(__CYGWIN__) #if !defined(__SYMBIAN32__) #if !defined(ASIO_HAS_EVENTFD) #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class pipe_select_interrupter { public: // Constructor. ASIO_DECL pipe_select_interrupter(); // Destructor. ASIO_DECL ~pipe_select_interrupter(); // Interrupt the select call. ASIO_DECL void interrupt(); // Reset the select interrupt. Returns true if the call was interrupted. ASIO_DECL bool reset(); // Get the read descriptor to be passed to select. int read_descriptor() const { return read_descriptor_; } private: // The read end of a connection used to interrupt the select call. This file // descriptor is passed to select such that when it is time to stop, a single // byte will be written on the other end of the connection and this // descriptor will become readable. int read_descriptor_; // The write end of a connection used to interrupt the select call. A single // byte may be written to this to wake up the select which is waiting for the // other end to become readable. int write_descriptor_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/pipe_select_interrupter.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // !defined(ASIO_HAS_EVENTFD) #endif // !defined(__SYMBIAN32__) #endif // !defined(__CYGWIN__) #endif // !defined(BOOST_WINDOWS) #endif // ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP percona-xtradb-cluster-galera/asio/asio/detail/pop_options.hpp0000644000000000000000000000305012247075736025062 0ustar rootroot00000000000000// // detail/pop_options.hpp // ~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // No header guard #if defined(__COMO__) // Comeau C++ #elif defined(__DMC__) // Digital Mars C++ #elif defined(__INTEL_COMPILER) || defined(__ICL) \ || defined(__ICC) || defined(__ECC) // Intel C++ #elif defined(__GNUC__) // GNU C++ # if defined(__MINGW32__) || defined(__CYGWIN__) # pragma pack (pop) # endif # if defined(__OBJC__) # if !defined(__APPLE_CC__) || (__APPLE_CC__ <= 1) # if defined(ASIO_OBJC_WORKAROUND) # undef Protocol # undef id # undef ASIO_OBJC_WORKAROUND # endif # endif # endif #elif defined(__KCC) // Kai C++ #elif defined(__sgi) // SGI MIPSpro C++ #elif defined(__DECCXX) // Compaq Tru64 Unix cxx #elif defined(__ghs) // Greenhills C++ #elif defined(__BORLANDC__) // Borland C++ # pragma option pop # pragma nopushoptwarn # pragma nopackwarning #elif defined(__MWERKS__) // Metrowerks CodeWarrior #elif defined(__SUNPRO_CC) // Sun Workshop Compiler C++ #elif defined(__HP_aCC) // HP aCC #elif defined(__MRC__) || defined(__SC__) // MPW MrCpp or SCpp #elif defined(__IBMCPP__) // IBM Visual Age #elif defined(_MSC_VER) // Microsoft Visual C++ // // Must remain the last #elif since some other vendors (Metrowerks, for example) // also #define _MSC_VER # pragma warning (pop) # pragma pack (pop) #endif percona-xtradb-cluster-galera/asio/asio/detail/posix_event.hpp0000644000000000000000000000413112247075736025055 0ustar rootroot00000000000000// // detail/posix_event.hpp // ~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_POSIX_EVENT_HPP #define ASIO_DETAIL_POSIX_EVENT_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) #include #include #include "asio/detail/noncopyable.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class posix_event : private noncopyable { public: // Constructor. ASIO_DECL posix_event(); // Destructor. ~posix_event() { ::pthread_cond_destroy(&cond_); } // Signal the event. template void signal(Lock& lock) { BOOST_ASSERT(lock.locked()); (void)lock; signalled_ = true; ::pthread_cond_signal(&cond_); // Ignore EINVAL. } // Signal the event and unlock the mutex. template void signal_and_unlock(Lock& lock) { BOOST_ASSERT(lock.locked()); signalled_ = true; lock.unlock(); ::pthread_cond_signal(&cond_); // Ignore EINVAL. } // Reset the event. template void clear(Lock& lock) { BOOST_ASSERT(lock.locked()); (void)lock; signalled_ = false; } // Wait for the event to become signalled. template void wait(Lock& lock) { BOOST_ASSERT(lock.locked()); while (!signalled_) ::pthread_cond_wait(&cond_, &lock.mutex().mutex_); // Ignore EINVAL. } private: ::pthread_cond_t cond_; bool signalled_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/posix_event.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) #endif // ASIO_DETAIL_POSIX_EVENT_HPP percona-xtradb-cluster-galera/asio/asio/detail/posix_fd_set_adapter.hpp0000644000000000000000000000336712247075736026712 0ustar rootroot00000000000000// // detail/posix_fd_set_adapter.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP #define ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) #include #include "asio/detail/socket_types.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { // Adapts the FD_SET type to meet the Descriptor_Set concept's requirements. class posix_fd_set_adapter { public: posix_fd_set_adapter() : max_descriptor_(invalid_socket) { using namespace std; // Needed for memset on Solaris. FD_ZERO(&fd_set_); } bool set(socket_type descriptor) { if (descriptor < (socket_type)FD_SETSIZE) { if (max_descriptor_ == invalid_socket || descriptor > max_descriptor_) max_descriptor_ = descriptor; FD_SET(descriptor, &fd_set_); return true; } return false; } bool is_set(socket_type descriptor) const { return FD_ISSET(descriptor, &fd_set_) != 0; } operator fd_set*() { return &fd_set_; } socket_type max_descriptor() const { return max_descriptor_; } private: mutable fd_set fd_set_; socket_type max_descriptor_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) #endif // ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP percona-xtradb-cluster-galera/asio/asio/detail/posix_mutex.hpp0000644000000000000000000000313112247075736025075 0ustar rootroot00000000000000// // detail/posix_mutex.hpp // ~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_POSIX_MUTEX_HPP #define ASIO_DETAIL_POSIX_MUTEX_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) #include #include "asio/detail/noncopyable.hpp" #include "asio/detail/scoped_lock.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class posix_event; class posix_mutex : private noncopyable { public: typedef asio::detail::scoped_lock scoped_lock; // Constructor. ASIO_DECL posix_mutex(); // Destructor. ~posix_mutex() { ::pthread_mutex_destroy(&mutex_); // Ignore EBUSY. } // Lock the mutex. void lock() { (void)::pthread_mutex_lock(&mutex_); // Ignore EINVAL. } // Unlock the mutex. void unlock() { (void)::pthread_mutex_unlock(&mutex_); // Ignore EINVAL. } private: friend class posix_event; ::pthread_mutex_t mutex_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/posix_mutex.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) #endif // ASIO_DETAIL_POSIX_MUTEX_HPP percona-xtradb-cluster-galera/asio/asio/detail/posix_signal_blocker.hpp0000644000000000000000000000364012247075736026716 0ustar rootroot00000000000000// // detail/posix_signal_blocker.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP #define ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) #include #include #include #include "asio/detail/noncopyable.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class posix_signal_blocker : private noncopyable { public: // Constructor blocks all signals for the calling thread. posix_signal_blocker() : blocked_(false) { sigset_t new_mask; sigfillset(&new_mask); blocked_ = (pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask_) == 0); } // Destructor restores the previous signal mask. ~posix_signal_blocker() { if (blocked_) pthread_sigmask(SIG_SETMASK, &old_mask_, 0); } // Block all signals for the calling thread. void block() { if (!blocked_) { sigset_t new_mask; sigfillset(&new_mask); blocked_ = (pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask_) == 0); } } // Restore the previous signal mask. void unblock() { if (blocked_) blocked_ = (pthread_sigmask(SIG_SETMASK, &old_mask_, 0) != 0); } private: // Have signals been blocked. bool blocked_; // The previous signal mask. sigset_t old_mask_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) #endif // ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP percona-xtradb-cluster-galera/asio/asio/detail/posix_thread.hpp0000644000000000000000000000370212247075736025206 0ustar rootroot00000000000000// // detail/posix_thread.hpp // ~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_POSIX_THREAD_HPP #define ASIO_DETAIL_POSIX_THREAD_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) #include #include "asio/detail/noncopyable.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { extern "C" { ASIO_DECL void* asio_detail_posix_thread_function(void* arg); } class posix_thread : private noncopyable { public: // Constructor. template posix_thread(Function f) : joined_(false) { start_thread(new func(f)); } // Destructor. ASIO_DECL ~posix_thread(); // Wait for the thread to exit. ASIO_DECL void join(); private: friend void* asio_detail_posix_thread_function(void* arg); class func_base { public: virtual ~func_base() {} virtual void run() = 0; }; struct auto_func_base_ptr { func_base* ptr; ~auto_func_base_ptr() { delete ptr; } }; template class func : public func_base { public: func(Function f) : f_(f) { } virtual void run() { f_(); } private: Function f_; }; ASIO_DECL void start_thread(func_base* arg); ::pthread_t thread_; bool joined_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/posix_thread.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) #endif // ASIO_DETAIL_POSIX_THREAD_HPP percona-xtradb-cluster-galera/asio/asio/detail/posix_tss_ptr.hpp0000644000000000000000000000333212247075736025434 0ustar rootroot00000000000000// // detail/posix_tss_ptr.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_POSIX_TSS_PTR_HPP #define ASIO_DETAIL_POSIX_TSS_PTR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) #include #include "asio/detail/noncopyable.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { // Helper function to create thread-specific storage. ASIO_DECL void posix_tss_ptr_create(pthread_key_t& key); template class posix_tss_ptr : private noncopyable { public: // Constructor. posix_tss_ptr() { posix_tss_ptr_create(tss_key_); } // Destructor. ~posix_tss_ptr() { ::pthread_key_delete(tss_key_); } // Get the value. operator T*() const { return static_cast(::pthread_getspecific(tss_key_)); } // Set the value. void operator=(T* value) { ::pthread_setspecific(tss_key_, value); } private: // Thread-specific storage to allow unlocked access to determine whether a // thread is a member of the pool. pthread_key_t tss_key_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/posix_tss_ptr.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) #endif // ASIO_DETAIL_POSIX_TSS_PTR_HPP percona-xtradb-cluster-galera/asio/asio/detail/push_options.hpp0000644000000000000000000000524612247075736025254 0ustar rootroot00000000000000// // detail/push_options.hpp // ~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // No header guard #if defined(__COMO__) // Comeau C++ #elif defined(__DMC__) // Digital Mars C++ #elif defined(__INTEL_COMPILER) || defined(__ICL) \ || defined(__ICC) || defined(__ECC) // Intel C++ #elif defined(__GNUC__) // GNU C++ # if defined(__MINGW32__) || defined(__CYGWIN__) # pragma pack (push, 8) # endif # if defined(__OBJC__) # if !defined(__APPLE_CC__) || (__APPLE_CC__ <= 1) # if !defined(ASIO_DISABLE_OBJC_WORKAROUND) # if !defined(Protocol) && !defined(id) # define Protocol cpp_Protocol # define id cpp_id # define ASIO_OBJC_WORKAROUND # endif # endif # endif # endif #elif defined(__KCC) // Kai C++ #elif defined(__sgi) // SGI MIPSpro C++ #elif defined(__DECCXX) // Compaq Tru64 Unix cxx #elif defined(__ghs) // Greenhills C++ #elif defined(__BORLANDC__) // Borland C++ # pragma option push -a8 -b -Ve- -Vx- -w-inl -vi- # pragma nopushoptwarn # pragma nopackwarning # if !defined(__MT__) # error Multithreaded RTL must be selected. # endif // !defined(__MT__) #elif defined(__MWERKS__) // Metrowerks CodeWarrior #elif defined(__SUNPRO_CC) // Sun Workshop Compiler C++ #elif defined(__HP_aCC) // HP aCC #elif defined(__MRC__) || defined(__SC__) // MPW MrCpp or SCpp #elif defined(__IBMCPP__) // IBM Visual Age #elif defined(_MSC_VER) // Microsoft Visual C++ // // Must remain the last #elif since some other vendors (Metrowerks, for example) // also #define _MSC_VER # pragma warning (disable:4103) # pragma warning (push) # pragma warning (disable:4127) # pragma warning (disable:4244) # pragma warning (disable:4355) # pragma warning (disable:4512) # pragma warning (disable:4675) # if defined(_M_IX86) && defined(_Wp64) // The /Wp64 option is broken. If you want to check 64 bit portability, use a // 64 bit compiler! # pragma warning (disable:4311) # pragma warning (disable:4312) # endif // defined(_M_IX86) && defined(_Wp64) # pragma pack (push, 8) // Note that if the /Og optimisation flag is enabled with MSVC6, the compiler // has a tendency to incorrectly optimise away some calls to member template // functions, even though those functions contain code that should not be // optimised away! Therefore we will always disable this optimisation option // for the MSVC6 compiler. # if (_MSC_VER < 1300) # pragma optimize ("g", off) # endif # if !defined(_MT) # error Multithreaded RTL must be selected. # endif // !defined(_MT) #endif percona-xtradb-cluster-galera/asio/asio/detail/reactive_descriptor_service.hpp0000644000000000000000000002001212247075736030266 0ustar rootroot00000000000000// // detail/reactive_descriptor_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_REACTIVE_DESCRIPTOR_SERVICE_HPP #define ASIO_DETAIL_REACTIVE_DESCRIPTOR_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) #include #include "asio/buffer.hpp" #include "asio/io_service.hpp" #include "asio/detail/bind_handler.hpp" #include "asio/detail/buffer_sequence_adapter.hpp" #include "asio/detail/descriptor_ops.hpp" #include "asio/detail/descriptor_read_op.hpp" #include "asio/detail/descriptor_write_op.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/noncopyable.hpp" #include "asio/detail/reactive_null_buffers_op.hpp" #include "asio/detail/reactor.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class reactive_descriptor_service { public: // The native type of a descriptor. typedef int native_type; // The implementation type of the descriptor. class implementation_type : private asio::detail::noncopyable { public: // Default constructor. implementation_type() : descriptor_(-1), state_(0) { } private: // Only this service will have access to the internal values. friend class reactive_descriptor_service; // The native descriptor representation. int descriptor_; // The current state of the descriptor. descriptor_ops::state_type state_; // Per-descriptor data used by the reactor. reactor::per_descriptor_data reactor_data_; }; // Constructor. ASIO_DECL reactive_descriptor_service( asio::io_service& io_service); // Destroy all user-defined handler objects owned by the service. ASIO_DECL void shutdown_service(); // Construct a new descriptor implementation. ASIO_DECL void construct(implementation_type& impl); // Destroy a descriptor implementation. ASIO_DECL void destroy(implementation_type& impl); // Assign a native descriptor to a descriptor implementation. ASIO_DECL asio::error_code assign(implementation_type& impl, const native_type& native_descriptor, asio::error_code& ec); // Determine whether the descriptor is open. bool is_open(const implementation_type& impl) const { return impl.descriptor_ != -1; } // Destroy a descriptor implementation. ASIO_DECL asio::error_code close(implementation_type& impl, asio::error_code& ec); // Get the native descriptor representation. native_type native(const implementation_type& impl) const { return impl.descriptor_; } // Cancel all operations associated with the descriptor. ASIO_DECL asio::error_code cancel(implementation_type& impl, asio::error_code& ec); // Perform an IO control command on the descriptor. template asio::error_code io_control(implementation_type& impl, IO_Control_Command& command, asio::error_code& ec) { descriptor_ops::ioctl(impl.descriptor_, impl.state_, command.name(), static_cast(command.data()), ec); return ec; } // Write some data to the descriptor. template size_t write_some(implementation_type& impl, const ConstBufferSequence& buffers, asio::error_code& ec) { buffer_sequence_adapter bufs(buffers); return descriptor_ops::sync_write(impl.descriptor_, impl.state_, bufs.buffers(), bufs.count(), bufs.all_empty(), ec); } // Wait until data can be written without blocking. size_t write_some(implementation_type& impl, const null_buffers&, asio::error_code& ec) { // Wait for descriptor to become ready. descriptor_ops::poll_write(impl.descriptor_, ec); return 0; } // Start an asynchronous write. The data being sent must be valid for the // lifetime of the asynchronous operation. template void async_write_some(implementation_type& impl, const ConstBufferSequence& buffers, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef descriptor_write_op op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(impl.descriptor_, buffers, handler); start_op(impl, reactor::write_op, p.p, true, buffer_sequence_adapter::all_empty(buffers)); p.v = p.p = 0; } // Start an asynchronous wait until data can be written without blocking. template void async_write_some(implementation_type& impl, const null_buffers&, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef reactive_null_buffers_op op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(handler); start_op(impl, reactor::write_op, p.p, false, false); p.v = p.p = 0; } // Read some data from the stream. Returns the number of bytes read. template size_t read_some(implementation_type& impl, const MutableBufferSequence& buffers, asio::error_code& ec) { buffer_sequence_adapter bufs(buffers); return descriptor_ops::sync_read(impl.descriptor_, impl.state_, bufs.buffers(), bufs.count(), bufs.all_empty(), ec); } // Wait until data can be read without blocking. size_t read_some(implementation_type& impl, const null_buffers&, asio::error_code& ec) { // Wait for descriptor to become ready. descriptor_ops::poll_read(impl.descriptor_, ec); return 0; } // Start an asynchronous read. The buffer for the data being read must be // valid for the lifetime of the asynchronous operation. template void async_read_some(implementation_type& impl, const MutableBufferSequence& buffers, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef descriptor_read_op op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(impl.descriptor_, buffers, handler); start_op(impl, reactor::read_op, p.p, true, buffer_sequence_adapter::all_empty(buffers)); p.v = p.p = 0; } // Wait until data can be read without blocking. template void async_read_some(implementation_type& impl, const null_buffers&, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef reactive_null_buffers_op op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(handler); start_op(impl, reactor::read_op, p.p, false, false); p.v = p.p = 0; } private: // Start the asynchronous operation. ASIO_DECL void start_op(implementation_type& impl, int op_type, reactor_op* op, bool non_blocking, bool noop); // The selector that performs event demultiplexing for the service. reactor& reactor_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/reactive_descriptor_service.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) #endif // ASIO_DETAIL_REACTIVE_DESCRIPTOR_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/detail/reactive_null_buffers_op.hpp0000644000000000000000000000471612247075736027571 0ustar rootroot00000000000000// // detail/reactive_null_buffers_op.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_REACTIVE_NULL_BUFFERS_OP_HPP #define ASIO_DETAIL_REACTIVE_NULL_BUFFERS_OP_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/detail/fenced_block.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/reactor_op.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class reactive_null_buffers_op : public reactor_op { public: ASIO_DEFINE_HANDLER_PTR(reactive_null_buffers_op); reactive_null_buffers_op(Handler handler) : reactor_op(&reactive_null_buffers_op::do_perform, &reactive_null_buffers_op::do_complete), handler_(handler) { } static bool do_perform(reactor_op*) { return true; } static void do_complete(io_service_impl* owner, operation* base, asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) { // Take ownership of the handler object. reactive_null_buffers_op* o(static_cast(base)); ptr p = { boost::addressof(o->handler_), o, o }; // Make a copy of the handler so that the memory can be deallocated before // the upcall is made. Even if we're not about to make an upcall, a // sub-object of the handler may be the true owner of the memory associated // with the handler. Consequently, a local copy of the handler is required // to ensure that any owning sub-object remains valid until after we have // deallocated the memory here. detail::binder2 handler(o->handler_, o->ec_, o->bytes_transferred_); p.h = boost::addressof(handler.handler_); p.reset(); // Make the upcall if required. if (owner) { asio::detail::fenced_block b; asio_handler_invoke_helpers::invoke(handler, handler.handler_); } } private: Handler handler_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_REACTIVE_NULL_BUFFERS_OP_HPP percona-xtradb-cluster-galera/asio/asio/detail/reactive_serial_port_service.hpp0000644000000000000000000001572312247075736030450 0ustar rootroot00000000000000// // detail/reactive_serial_port_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_REACTIVE_SERIAL_PORT_SERVICE_HPP #define ASIO_DETAIL_REACTIVE_SERIAL_PORT_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_SERIAL_PORT) #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) #include #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/serial_port_base.hpp" #include "asio/detail/descriptor_ops.hpp" #include "asio/detail/reactive_descriptor_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { // Extend reactive_descriptor_service to provide serial port support. class reactive_serial_port_service { public: // The native type of a serial port. typedef reactive_descriptor_service::native_type native_type; // The implementation type of the serial port. typedef reactive_descriptor_service::implementation_type implementation_type; ASIO_DECL reactive_serial_port_service( asio::io_service& io_service); // Destroy all user-defined handler objects owned by the service. ASIO_DECL void shutdown_service(); // Construct a new serial port implementation. void construct(implementation_type& impl) { descriptor_service_.construct(impl); } // Destroy a serial port implementation. void destroy(implementation_type& impl) { descriptor_service_.destroy(impl); } // Open the serial port using the specified device name. ASIO_DECL asio::error_code open(implementation_type& impl, const std::string& device, asio::error_code& ec); // Assign a native descriptor to a serial port implementation. asio::error_code assign(implementation_type& impl, const native_type& native_descriptor, asio::error_code& ec) { return descriptor_service_.assign(impl, native_descriptor, ec); } // Determine whether the serial port is open. bool is_open(const implementation_type& impl) const { return descriptor_service_.is_open(impl); } // Destroy a serial port implementation. asio::error_code close(implementation_type& impl, asio::error_code& ec) { return descriptor_service_.close(impl, ec); } // Get the native serial port representation. native_type native(implementation_type& impl) { return descriptor_service_.native(impl); } // Cancel all operations associated with the serial port. asio::error_code cancel(implementation_type& impl, asio::error_code& ec) { return descriptor_service_.cancel(impl, ec); } // Set an option on the serial port. template asio::error_code set_option(implementation_type& impl, const SettableSerialPortOption& option, asio::error_code& ec) { return do_set_option(impl, &reactive_serial_port_service::store_option, &option, ec); } // Get an option from the serial port. template asio::error_code get_option(const implementation_type& impl, GettableSerialPortOption& option, asio::error_code& ec) const { return do_get_option(impl, &reactive_serial_port_service::load_option, &option, ec); } // Send a break sequence to the serial port. asio::error_code send_break(implementation_type& impl, asio::error_code& ec) { errno = 0; descriptor_ops::error_wrapper(::tcsendbreak( descriptor_service_.native(impl), 0), ec); return ec; } // Write the given data. Returns the number of bytes sent. template size_t write_some(implementation_type& impl, const ConstBufferSequence& buffers, asio::error_code& ec) { return descriptor_service_.write_some(impl, buffers, ec); } // Start an asynchronous write. The data being written must be valid for the // lifetime of the asynchronous operation. template void async_write_some(implementation_type& impl, const ConstBufferSequence& buffers, Handler handler) { descriptor_service_.async_write_some(impl, buffers, handler); } // Read some data. Returns the number of bytes received. template size_t read_some(implementation_type& impl, const MutableBufferSequence& buffers, asio::error_code& ec) { return descriptor_service_.read_some(impl, buffers, ec); } // Start an asynchronous read. The buffer for the data being received must be // valid for the lifetime of the asynchronous operation. template void async_read_some(implementation_type& impl, const MutableBufferSequence& buffers, Handler handler) { descriptor_service_.async_read_some(impl, buffers, handler); } private: // Function pointer type for storing a serial port option. typedef asio::error_code (*store_function_type)( const void*, termios&, asio::error_code&); // Helper function template to store a serial port option. template static asio::error_code store_option(const void* option, termios& storage, asio::error_code& ec) { return static_cast(option)->store( storage, ec); } // Helper function to set a serial port option. ASIO_DECL asio::error_code do_set_option( implementation_type& impl, store_function_type store, const void* option, asio::error_code& ec); // Function pointer type for loading a serial port option. typedef asio::error_code (*load_function_type)( void*, const termios&, asio::error_code&); // Helper function template to load a serial port option. template static asio::error_code load_option(void* option, const termios& storage, asio::error_code& ec) { return static_cast(option)->load(storage, ec); } // Helper function to get a serial port option. ASIO_DECL asio::error_code do_get_option( const implementation_type& impl, load_function_type load, void* option, asio::error_code& ec) const; // The implementation used for initiating asynchronous operations. reactive_descriptor_service descriptor_service_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/reactive_serial_port_service.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) #endif // defined(ASIO_HAS_SERIAL_PORT) #endif // ASIO_DETAIL_REACTIVE_SERIAL_PORT_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/detail/reactive_socket_accept_op.hpp0000644000000000000000000001025312247075736027703 0ustar rootroot00000000000000// // detail/reactive_socket_accept_op.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_REACTIVE_SOCKET_ACCEPT_OP_HPP #define ASIO_DETAIL_REACTIVE_SOCKET_ACCEPT_OP_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/detail/bind_handler.hpp" #include "asio/detail/buffer_sequence_adapter.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/reactor_op.hpp" #include "asio/detail/socket_holder.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class reactive_socket_accept_op_base : public reactor_op { public: reactive_socket_accept_op_base(socket_type socket, socket_ops::state_type state, Socket& peer, const Protocol& protocol, typename Protocol::endpoint* peer_endpoint, func_type complete_func) : reactor_op(&reactive_socket_accept_op_base::do_perform, complete_func), socket_(socket), state_(state), peer_(peer), protocol_(protocol), peer_endpoint_(peer_endpoint) { } static bool do_perform(reactor_op* base) { reactive_socket_accept_op_base* o( static_cast(base)); std::size_t addrlen = o->peer_endpoint_ ? o->peer_endpoint_->capacity() : 0; socket_type new_socket = invalid_socket; bool result = socket_ops::non_blocking_accept(o->socket_, o->state_, o->peer_endpoint_ ? o->peer_endpoint_->data() : 0, o->peer_endpoint_ ? &addrlen : 0, o->ec_, new_socket); // On success, assign new connection to peer socket object. if (new_socket >= 0) { socket_holder new_socket_holder(new_socket); if (o->peer_endpoint_) o->peer_endpoint_->resize(addrlen); if (!o->peer_.assign(o->protocol_, new_socket, o->ec_)) new_socket_holder.release(); } return result; } private: socket_type socket_; socket_ops::state_type state_; Socket& peer_; Protocol protocol_; typename Protocol::endpoint* peer_endpoint_; }; template class reactive_socket_accept_op : public reactive_socket_accept_op_base { public: ASIO_DEFINE_HANDLER_PTR(reactive_socket_accept_op); reactive_socket_accept_op(socket_type socket, socket_ops::state_type state, Socket& peer, const Protocol& protocol, typename Protocol::endpoint* peer_endpoint, Handler handler) : reactive_socket_accept_op_base(socket, state, peer, protocol, peer_endpoint, &reactive_socket_accept_op::do_complete), handler_(handler) { } static void do_complete(io_service_impl* owner, operation* base, asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) { // Take ownership of the handler object. reactive_socket_accept_op* o(static_cast(base)); ptr p = { boost::addressof(o->handler_), o, o }; // Make a copy of the handler so that the memory can be deallocated before // the upcall is made. Even if we're not about to make an upcall, a // sub-object of the handler may be the true owner of the memory associated // with the handler. Consequently, a local copy of the handler is required // to ensure that any owning sub-object remains valid until after we have // deallocated the memory here. detail::binder1 handler(o->handler_, o->ec_); p.h = boost::addressof(handler.handler_); p.reset(); // Make the upcall if required. if (owner) { asio::detail::fenced_block b; asio_handler_invoke_helpers::invoke(handler, handler.handler_); } } private: Handler handler_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_REACTIVE_SOCKET_ACCEPT_OP_HPP percona-xtradb-cluster-galera/asio/asio/detail/reactive_socket_connect_op.hpp0000644000000000000000000000567412247075736030110 0ustar rootroot00000000000000// // detail/reactive_socket_connect_op.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_REACTIVE_SOCKET_CONNECT_OP_HPP #define ASIO_DETAIL_REACTIVE_SOCKET_CONNECT_OP_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/detail/bind_handler.hpp" #include "asio/detail/buffer_sequence_adapter.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/reactor_op.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class reactive_socket_connect_op_base : public reactor_op { public: reactive_socket_connect_op_base(socket_type socket, func_type complete_func) : reactor_op(&reactive_socket_connect_op_base::do_perform, complete_func), socket_(socket) { } static bool do_perform(reactor_op* base) { reactive_socket_connect_op_base* o( static_cast(base)); return socket_ops::non_blocking_connect(o->socket_, o->ec_); } private: socket_type socket_; }; template class reactive_socket_connect_op : public reactive_socket_connect_op_base { public: ASIO_DEFINE_HANDLER_PTR(reactive_socket_connect_op); reactive_socket_connect_op(socket_type socket, Handler handler) : reactive_socket_connect_op_base(socket, &reactive_socket_connect_op::do_complete), handler_(handler) { } static void do_complete(io_service_impl* owner, operation* base, asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) { // Take ownership of the handler object. reactive_socket_connect_op* o (static_cast(base)); ptr p = { boost::addressof(o->handler_), o, o }; // Make a copy of the handler so that the memory can be deallocated before // the upcall is made. Even if we're not about to make an upcall, a // sub-object of the handler may be the true owner of the memory associated // with the handler. Consequently, a local copy of the handler is required // to ensure that any owning sub-object remains valid until after we have // deallocated the memory here. detail::binder1 handler(o->handler_, o->ec_); p.h = boost::addressof(handler.handler_); p.reset(); // Make the upcall if required. if (owner) { asio::detail::fenced_block b; asio_handler_invoke_helpers::invoke(handler, handler); } } private: Handler handler_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_REACTIVE_SOCKET_CONNECT_OP_HPP percona-xtradb-cluster-galera/asio/asio/detail/reactive_socket_recv_op.hpp0000644000000000000000000000726112247075736027410 0ustar rootroot00000000000000// // detail/reactive_socket_recv_op.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_REACTIVE_SOCKET_RECV_OP_HPP #define ASIO_DETAIL_REACTIVE_SOCKET_RECV_OP_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/detail/bind_handler.hpp" #include "asio/detail/buffer_sequence_adapter.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/reactor_op.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class reactive_socket_recv_op_base : public reactor_op { public: reactive_socket_recv_op_base(socket_type socket, socket_ops::state_type state, const MutableBufferSequence& buffers, socket_base::message_flags flags, func_type complete_func) : reactor_op(&reactive_socket_recv_op_base::do_perform, complete_func), socket_(socket), state_(state), buffers_(buffers), flags_(flags) { } static bool do_perform(reactor_op* base) { reactive_socket_recv_op_base* o( static_cast(base)); buffer_sequence_adapter bufs(o->buffers_); return socket_ops::non_blocking_recv(o->socket_, bufs.buffers(), bufs.count(), o->flags_, (o->state_ & socket_ops::stream_oriented), o->ec_, o->bytes_transferred_); } private: socket_type socket_; socket_ops::state_type state_; MutableBufferSequence buffers_; socket_base::message_flags flags_; }; template class reactive_socket_recv_op : public reactive_socket_recv_op_base { public: ASIO_DEFINE_HANDLER_PTR(reactive_socket_recv_op); reactive_socket_recv_op(socket_type socket, socket_ops::state_type state, const MutableBufferSequence& buffers, socket_base::message_flags flags, Handler handler) : reactive_socket_recv_op_base(socket, state, buffers, flags, &reactive_socket_recv_op::do_complete), handler_(handler) { } static void do_complete(io_service_impl* owner, operation* base, asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) { // Take ownership of the handler object. reactive_socket_recv_op* o(static_cast(base)); ptr p = { boost::addressof(o->handler_), o, o }; // Make a copy of the handler so that the memory can be deallocated before // the upcall is made. Even if we're not about to make an upcall, a // sub-object of the handler may be the true owner of the memory associated // with the handler. Consequently, a local copy of the handler is required // to ensure that any owning sub-object remains valid until after we have // deallocated the memory here. detail::binder2 handler(o->handler_, o->ec_, o->bytes_transferred_); p.h = boost::addressof(handler.handler_); p.reset(); // Make the upcall if required. if (owner) { asio::detail::fenced_block b; asio_handler_invoke_helpers::invoke(handler, handler.handler_); } } private: Handler handler_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_REACTIVE_SOCKET_RECV_OP_HPP percona-xtradb-cluster-galera/asio/asio/detail/reactive_socket_recvfrom_op.hpp0000644000000000000000000001011412247075736030263 0ustar rootroot00000000000000// // detail/reactive_socket_recvfrom_op.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_REACTIVE_SOCKET_RECVFROM_OP_HPP #define ASIO_DETAIL_REACTIVE_SOCKET_RECVFROM_OP_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/detail/bind_handler.hpp" #include "asio/detail/buffer_sequence_adapter.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/reactor_op.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class reactive_socket_recvfrom_op_base : public reactor_op { public: reactive_socket_recvfrom_op_base(socket_type socket, int protocol_type, const MutableBufferSequence& buffers, Endpoint& endpoint, socket_base::message_flags flags, func_type complete_func) : reactor_op(&reactive_socket_recvfrom_op_base::do_perform, complete_func), socket_(socket), protocol_type_(protocol_type), buffers_(buffers), sender_endpoint_(endpoint), flags_(flags) { } static bool do_perform(reactor_op* base) { reactive_socket_recvfrom_op_base* o( static_cast(base)); buffer_sequence_adapter bufs(o->buffers_); std::size_t addr_len = o->sender_endpoint_.capacity(); bool result = socket_ops::non_blocking_recvfrom(o->socket_, bufs.buffers(), bufs.count(), o->flags_, o->sender_endpoint_.data(), &addr_len, o->ec_, o->bytes_transferred_); if (result && !o->ec_) o->sender_endpoint_.resize(addr_len); return result; } private: socket_type socket_; int protocol_type_; MutableBufferSequence buffers_; Endpoint& sender_endpoint_; socket_base::message_flags flags_; }; template class reactive_socket_recvfrom_op : public reactive_socket_recvfrom_op_base { public: ASIO_DEFINE_HANDLER_PTR(reactive_socket_recvfrom_op); reactive_socket_recvfrom_op(socket_type socket, int protocol_type, const MutableBufferSequence& buffers, Endpoint& endpoint, socket_base::message_flags flags, Handler handler) : reactive_socket_recvfrom_op_base( socket, protocol_type, buffers, endpoint, flags, &reactive_socket_recvfrom_op::do_complete), handler_(handler) { } static void do_complete(io_service_impl* owner, operation* base, asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) { // Take ownership of the handler object. reactive_socket_recvfrom_op* o( static_cast(base)); ptr p = { boost::addressof(o->handler_), o, o }; // Make a copy of the handler so that the memory can be deallocated before // the upcall is made. Even if we're not about to make an upcall, a // sub-object of the handler may be the true owner of the memory associated // with the handler. Consequently, a local copy of the handler is required // to ensure that any owning sub-object remains valid until after we have // deallocated the memory here. detail::binder2 handler(o->handler_, o->ec_, o->bytes_transferred_); p.h = boost::addressof(handler.handler_); p.reset(); // Make the upcall if required. if (owner) { asio::detail::fenced_block b; asio_handler_invoke_helpers::invoke(handler, handler.handler_); } } private: Handler handler_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_REACTIVE_SOCKET_RECVFROM_OP_HPP percona-xtradb-cluster-galera/asio/asio/detail/reactive_socket_send_op.hpp0000644000000000000000000000676712247075736027414 0ustar rootroot00000000000000// // detail/reactive_socket_send_op.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_REACTIVE_SOCKET_SEND_OP_HPP #define ASIO_DETAIL_REACTIVE_SOCKET_SEND_OP_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/detail/bind_handler.hpp" #include "asio/detail/buffer_sequence_adapter.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/reactor_op.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class reactive_socket_send_op_base : public reactor_op { public: reactive_socket_send_op_base(socket_type socket, const ConstBufferSequence& buffers, socket_base::message_flags flags, func_type complete_func) : reactor_op(&reactive_socket_send_op_base::do_perform, complete_func), socket_(socket), buffers_(buffers), flags_(flags) { } static bool do_perform(reactor_op* base) { reactive_socket_send_op_base* o( static_cast(base)); buffer_sequence_adapter bufs(o->buffers_); return socket_ops::non_blocking_send(o->socket_, bufs.buffers(), bufs.count(), o->flags_, o->ec_, o->bytes_transferred_); } private: socket_type socket_; ConstBufferSequence buffers_; socket_base::message_flags flags_; }; template class reactive_socket_send_op : public reactive_socket_send_op_base { public: ASIO_DEFINE_HANDLER_PTR(reactive_socket_send_op); reactive_socket_send_op(socket_type socket, const ConstBufferSequence& buffers, socket_base::message_flags flags, Handler handler) : reactive_socket_send_op_base(socket, buffers, flags, &reactive_socket_send_op::do_complete), handler_(handler) { } static void do_complete(io_service_impl* owner, operation* base, asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) { // Take ownership of the handler object. reactive_socket_send_op* o(static_cast(base)); ptr p = { boost::addressof(o->handler_), o, o }; // Make a copy of the handler so that the memory can be deallocated before // the upcall is made. Even if we're not about to make an upcall, a // sub-object of the handler may be the true owner of the memory associated // with the handler. Consequently, a local copy of the handler is required // to ensure that any owning sub-object remains valid until after we have // deallocated the memory here. detail::binder2 handler(o->handler_, o->ec_, o->bytes_transferred_); p.h = boost::addressof(handler.handler_); p.reset(); // Make the upcall if required. if (owner) { asio::detail::fenced_block b; asio_handler_invoke_helpers::invoke(handler, handler.handler_); } } private: Handler handler_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_REACTIVE_SOCKET_SEND_OP_HPP percona-xtradb-cluster-galera/asio/asio/detail/reactive_socket_sendto_op.hpp0000644000000000000000000000740612247075736027746 0ustar rootroot00000000000000// // detail/reactive_socket_sendto_op.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_REACTIVE_SOCKET_SENDTO_OP_HPP #define ASIO_DETAIL_REACTIVE_SOCKET_SENDTO_OP_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/detail/bind_handler.hpp" #include "asio/detail/buffer_sequence_adapter.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/reactor_op.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class reactive_socket_sendto_op_base : public reactor_op { public: reactive_socket_sendto_op_base(socket_type socket, const ConstBufferSequence& buffers, const Endpoint& endpoint, socket_base::message_flags flags, func_type complete_func) : reactor_op(&reactive_socket_sendto_op_base::do_perform, complete_func), socket_(socket), buffers_(buffers), destination_(endpoint), flags_(flags) { } static bool do_perform(reactor_op* base) { reactive_socket_sendto_op_base* o( static_cast(base)); buffer_sequence_adapter bufs(o->buffers_); return socket_ops::non_blocking_sendto(o->socket_, bufs.buffers(), bufs.count(), o->flags_, o->destination_.data(), o->destination_.size(), o->ec_, o->bytes_transferred_); } private: socket_type socket_; ConstBufferSequence buffers_; Endpoint destination_; socket_base::message_flags flags_; }; template class reactive_socket_sendto_op : public reactive_socket_sendto_op_base { public: ASIO_DEFINE_HANDLER_PTR(reactive_socket_sendto_op); reactive_socket_sendto_op(socket_type socket, const ConstBufferSequence& buffers, const Endpoint& endpoint, socket_base::message_flags flags, Handler handler) : reactive_socket_sendto_op_base(socket, buffers, endpoint, flags, &reactive_socket_sendto_op::do_complete), handler_(handler) { } static void do_complete(io_service_impl* owner, operation* base, asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) { // Take ownership of the handler object. reactive_socket_sendto_op* o(static_cast(base)); ptr p = { boost::addressof(o->handler_), o, o }; // Make a copy of the handler so that the memory can be deallocated before // the upcall is made. Even if we're not about to make an upcall, a // sub-object of the handler may be the true owner of the memory associated // with the handler. Consequently, a local copy of the handler is required // to ensure that any owning sub-object remains valid until after we have // deallocated the memory here. detail::binder2 handler(o->handler_, o->ec_, o->bytes_transferred_); p.h = boost::addressof(handler.handler_); p.reset(); // Make the upcall if required. if (owner) { asio::detail::fenced_block b; asio_handler_invoke_helpers::invoke(handler, handler.handler_); } } private: Handler handler_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_REACTIVE_SOCKET_SENDTO_OP_HPP percona-xtradb-cluster-galera/asio/asio/detail/reactive_socket_service.hpp0000644000000000000000000003114312247075736027407 0ustar rootroot00000000000000// // detail/reactive_socket_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP #define ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(ASIO_HAS_IOCP) #include #include "asio/buffer.hpp" #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/socket_base.hpp" #include "asio/detail/buffer_sequence_adapter.hpp" #include "asio/detail/noncopyable.hpp" #include "asio/detail/reactive_null_buffers_op.hpp" #include "asio/detail/reactive_socket_accept_op.hpp" #include "asio/detail/reactive_socket_connect_op.hpp" #include "asio/detail/reactive_socket_recvfrom_op.hpp" #include "asio/detail/reactive_socket_sendto_op.hpp" #include "asio/detail/reactive_socket_service_base.hpp" #include "asio/detail/reactor.hpp" #include "asio/detail/reactor_op.hpp" #include "asio/detail/socket_holder.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class reactive_socket_service : public reactive_socket_service_base { public: // The protocol type. typedef Protocol protocol_type; // The endpoint type. typedef typename Protocol::endpoint endpoint_type; // The native type of a socket. typedef socket_type native_type; // The implementation type of the socket. struct implementation_type : reactive_socket_service_base::base_implementation_type { // Default constructor. implementation_type() : protocol_(endpoint_type().protocol()) { } // The protocol associated with the socket. protocol_type protocol_; }; // Constructor. reactive_socket_service(asio::io_service& io_service) : reactive_socket_service_base(io_service) { } // Open a new socket implementation. asio::error_code open(implementation_type& impl, const protocol_type& protocol, asio::error_code& ec) { if (!do_open(impl, protocol.family(), protocol.type(), protocol.protocol(), ec)) impl.protocol_ = protocol; return ec; } // Assign a native socket to a socket implementation. asio::error_code assign(implementation_type& impl, const protocol_type& protocol, const native_type& native_socket, asio::error_code& ec) { if (!do_assign(impl, protocol.type(), native_socket, ec)) impl.protocol_ = protocol; return ec; } // Get the native socket representation. native_type native(implementation_type& impl) { return impl.socket_; } // Bind the socket to the specified local endpoint. asio::error_code bind(implementation_type& impl, const endpoint_type& endpoint, asio::error_code& ec) { socket_ops::bind(impl.socket_, endpoint.data(), endpoint.size(), ec); return ec; } // Set a socket option. template asio::error_code set_option(implementation_type& impl, const Option& option, asio::error_code& ec) { socket_ops::setsockopt(impl.socket_, impl.state_, option.level(impl.protocol_), option.name(impl.protocol_), option.data(impl.protocol_), option.size(impl.protocol_), ec); return ec; } // Set a socket option. template asio::error_code get_option(const implementation_type& impl, Option& option, asio::error_code& ec) const { std::size_t size = option.size(impl.protocol_); socket_ops::getsockopt(impl.socket_, impl.state_, option.level(impl.protocol_), option.name(impl.protocol_), option.data(impl.protocol_), &size, ec); if (!ec) option.resize(impl.protocol_, size); return ec; } // Get the local endpoint. endpoint_type local_endpoint(const implementation_type& impl, asio::error_code& ec) const { endpoint_type endpoint; std::size_t addr_len = endpoint.capacity(); if (socket_ops::getsockname(impl.socket_, endpoint.data(), &addr_len, ec)) return endpoint_type(); endpoint.resize(addr_len); return endpoint; } // Get the remote endpoint. endpoint_type remote_endpoint(const implementation_type& impl, asio::error_code& ec) const { endpoint_type endpoint; std::size_t addr_len = endpoint.capacity(); if (socket_ops::getpeername(impl.socket_, endpoint.data(), &addr_len, false, ec)) return endpoint_type(); endpoint.resize(addr_len); return endpoint; } // Send a datagram to the specified endpoint. Returns the number of bytes // sent. template size_t send_to(implementation_type& impl, const ConstBufferSequence& buffers, const endpoint_type& destination, socket_base::message_flags flags, asio::error_code& ec) { buffer_sequence_adapter bufs(buffers); return socket_ops::sync_sendto(impl.socket_, impl.state_, bufs.buffers(), bufs.count(), flags, destination.data(), destination.size(), ec); } // Wait until data can be sent without blocking. size_t send_to(implementation_type& impl, const null_buffers&, const endpoint_type&, socket_base::message_flags, asio::error_code& ec) { // Wait for socket to become ready. socket_ops::poll_write(impl.socket_, ec); return 0; } // Start an asynchronous send. The data being sent must be valid for the // lifetime of the asynchronous operation. template void async_send_to(implementation_type& impl, const ConstBufferSequence& buffers, const endpoint_type& destination, socket_base::message_flags flags, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef reactive_socket_sendto_op op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(impl.socket_, buffers, destination, flags, handler); start_op(impl, reactor::write_op, p.p, true, false); p.v = p.p = 0; } // Start an asynchronous wait until data can be sent without blocking. template void async_send_to(implementation_type& impl, const null_buffers&, const endpoint_type&, socket_base::message_flags, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef reactive_null_buffers_op op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(handler); start_op(impl, reactor::write_op, p.p, false, false); p.v = p.p = 0; } // Receive a datagram with the endpoint of the sender. Returns the number of // bytes received. template size_t receive_from(implementation_type& impl, const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, socket_base::message_flags flags, asio::error_code& ec) { buffer_sequence_adapter bufs(buffers); std::size_t addr_len = sender_endpoint.capacity(); std::size_t bytes_recvd = socket_ops::sync_recvfrom( impl.socket_, impl.state_, bufs.buffers(), bufs.count(), flags, sender_endpoint.data(), &addr_len, ec); if (!ec) sender_endpoint.resize(addr_len); return bytes_recvd; } // Wait until data can be received without blocking. size_t receive_from(implementation_type& impl, const null_buffers&, endpoint_type& sender_endpoint, socket_base::message_flags, asio::error_code& ec) { // Wait for socket to become ready. socket_ops::poll_read(impl.socket_, ec); // Reset endpoint since it can be given no sensible value at this time. sender_endpoint = endpoint_type(); return 0; } // Start an asynchronous receive. The buffer for the data being received and // the sender_endpoint object must both be valid for the lifetime of the // asynchronous operation. template void async_receive_from(implementation_type& impl, const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, socket_base::message_flags flags, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef reactive_socket_recvfrom_op op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; int protocol_type = impl.protocol_.type(); p.p = new (p.v) op(impl.socket_, protocol_type, buffers, sender_endpoint, flags, handler); start_op(impl, (flags & socket_base::message_out_of_band) ? reactor::except_op : reactor::read_op, p.p, true, false); p.v = p.p = 0; } // Wait until data can be received without blocking. template void async_receive_from(implementation_type& impl, const null_buffers&, endpoint_type& sender_endpoint, socket_base::message_flags flags, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef reactive_null_buffers_op op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(handler); // Reset endpoint since it can be given no sensible value at this time. sender_endpoint = endpoint_type(); start_op(impl, (flags & socket_base::message_out_of_band) ? reactor::except_op : reactor::read_op, p.p, false, false); p.v = p.p = 0; } // Accept a new connection. template asio::error_code accept(implementation_type& impl, Socket& peer, endpoint_type* peer_endpoint, asio::error_code& ec) { // We cannot accept a socket that is already open. if (peer.is_open()) { ec = asio::error::already_open; return ec; } std::size_t addr_len = peer_endpoint ? peer_endpoint->capacity() : 0; socket_holder new_socket(socket_ops::sync_accept(impl.socket_, impl.state_, peer_endpoint ? peer_endpoint->data() : 0, peer_endpoint ? &addr_len : 0, ec)); // On success, assign new connection to peer socket object. if (new_socket.get() != invalid_socket) { if (peer_endpoint) peer_endpoint->resize(addr_len); if (!peer.assign(impl.protocol_, new_socket.get(), ec)) new_socket.release(); } return ec; } // Start an asynchronous accept. The peer and peer_endpoint objects // must be valid until the accept's handler is invoked. template void async_accept(implementation_type& impl, Socket& peer, endpoint_type* peer_endpoint, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef reactive_socket_accept_op op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(impl.socket_, impl.state_, peer, impl.protocol_, peer_endpoint, handler); start_accept_op(impl, p.p, peer.is_open()); p.v = p.p = 0; } // Connect the socket to the specified endpoint. asio::error_code connect(implementation_type& impl, const endpoint_type& peer_endpoint, asio::error_code& ec) { socket_ops::sync_connect(impl.socket_, peer_endpoint.data(), peer_endpoint.size(), ec); return ec; } // Start an asynchronous connect. template void async_connect(implementation_type& impl, const endpoint_type& peer_endpoint, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef reactive_socket_connect_op op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(impl.socket_, handler); start_connect_op(impl, p.p, peer_endpoint.data(), peer_endpoint.size()); p.v = p.p = 0; } }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // !defined(ASIO_HAS_IOCP) #endif // ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/detail/reactive_socket_service_base.hpp0000644000000000000000000002350512247075736030404 0ustar rootroot00000000000000// // detail/reactive_socket_service_base.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_BASE_HPP #define ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_BASE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(ASIO_HAS_IOCP) #include #include "asio/buffer.hpp" #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/socket_base.hpp" #include "asio/detail/buffer_sequence_adapter.hpp" #include "asio/detail/reactive_null_buffers_op.hpp" #include "asio/detail/reactive_socket_recv_op.hpp" #include "asio/detail/reactive_socket_send_op.hpp" #include "asio/detail/reactor.hpp" #include "asio/detail/reactor_op.hpp" #include "asio/detail/socket_holder.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class reactive_socket_service_base { public: // The native type of a socket. typedef socket_type native_type; // The implementation type of the socket. struct base_implementation_type { // The native socket representation. socket_type socket_; // The current state of the socket. socket_ops::state_type state_; // Per-descriptor data used by the reactor. reactor::per_descriptor_data reactor_data_; }; // Constructor. ASIO_DECL reactive_socket_service_base( asio::io_service& io_service); // Destroy all user-defined handler objects owned by the service. ASIO_DECL void shutdown_service(); // Construct a new socket implementation. ASIO_DECL void construct(base_implementation_type& impl); // Destroy a socket implementation. ASIO_DECL void destroy(base_implementation_type& impl); // Determine whether the socket is open. bool is_open(const base_implementation_type& impl) const { return impl.socket_ != invalid_socket; } // Destroy a socket implementation. ASIO_DECL asio::error_code close( base_implementation_type& impl, asio::error_code& ec); // Get the native socket representation. native_type native(base_implementation_type& impl) { return impl.socket_; } // Cancel all operations associated with the socket. ASIO_DECL asio::error_code cancel( base_implementation_type& impl, asio::error_code& ec); // Determine whether the socket is at the out-of-band data mark. bool at_mark(const base_implementation_type& impl, asio::error_code& ec) const { return socket_ops::sockatmark(impl.socket_, ec); } // Determine the number of bytes available for reading. std::size_t available(const base_implementation_type& impl, asio::error_code& ec) const { return socket_ops::available(impl.socket_, ec); } // Place the socket into the state where it will listen for new connections. asio::error_code listen(base_implementation_type& impl, int backlog, asio::error_code& ec) { socket_ops::listen(impl.socket_, backlog, ec); return ec; } // Perform an IO control command on the socket. template asio::error_code io_control(base_implementation_type& impl, IO_Control_Command& command, asio::error_code& ec) { socket_ops::ioctl(impl.socket_, impl.state_, command.name(), static_cast(command.data()), ec); return ec; } /// Disable sends or receives on the socket. asio::error_code shutdown(base_implementation_type& impl, socket_base::shutdown_type what, asio::error_code& ec) { socket_ops::shutdown(impl.socket_, what, ec); return ec; } // Send the given data to the peer. template size_t send(base_implementation_type& impl, const ConstBufferSequence& buffers, socket_base::message_flags flags, asio::error_code& ec) { buffer_sequence_adapter bufs(buffers); return socket_ops::sync_send(impl.socket_, impl.state_, bufs.buffers(), bufs.count(), flags, bufs.all_empty(), ec); } // Wait until data can be sent without blocking. size_t send(base_implementation_type& impl, const null_buffers&, socket_base::message_flags, asio::error_code& ec) { // Wait for socket to become ready. socket_ops::poll_write(impl.socket_, ec); return 0; } // Start an asynchronous send. The data being sent must be valid for the // lifetime of the asynchronous operation. template void async_send(base_implementation_type& impl, const ConstBufferSequence& buffers, socket_base::message_flags flags, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef reactive_socket_send_op op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(impl.socket_, buffers, flags, handler); start_op(impl, reactor::write_op, p.p, true, ((impl.state_ & socket_ops::stream_oriented) && buffer_sequence_adapter::all_empty(buffers))); p.v = p.p = 0; } // Start an asynchronous wait until data can be sent without blocking. template void async_send(base_implementation_type& impl, const null_buffers&, socket_base::message_flags, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef reactive_null_buffers_op op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(handler); start_op(impl, reactor::write_op, p.p, false, false); p.v = p.p = 0; } // Receive some data from the peer. Returns the number of bytes received. template size_t receive(base_implementation_type& impl, const MutableBufferSequence& buffers, socket_base::message_flags flags, asio::error_code& ec) { buffer_sequence_adapter bufs(buffers); return socket_ops::sync_recv(impl.socket_, impl.state_, bufs.buffers(), bufs.count(), flags, bufs.all_empty(), ec); } // Wait until data can be received without blocking. size_t receive(base_implementation_type& impl, const null_buffers&, socket_base::message_flags, asio::error_code& ec) { // Wait for socket to become ready. socket_ops::poll_read(impl.socket_, ec); return 0; } // Start an asynchronous receive. The buffer for the data being received // must be valid for the lifetime of the asynchronous operation. template void async_receive(base_implementation_type& impl, const MutableBufferSequence& buffers, socket_base::message_flags flags, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef reactive_socket_recv_op op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(impl.socket_, impl.state_, buffers, flags, handler); start_op(impl, (flags & socket_base::message_out_of_band) ? reactor::except_op : reactor::read_op, p.p, (flags & socket_base::message_out_of_band) == 0, ((impl.state_ & socket_ops::stream_oriented) && buffer_sequence_adapter::all_empty(buffers))); p.v = p.p = 0; } // Wait until data can be received without blocking. template void async_receive(base_implementation_type& impl, const null_buffers&, socket_base::message_flags flags, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef reactive_null_buffers_op op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(handler); start_op(impl, (flags & socket_base::message_out_of_band) ? reactor::except_op : reactor::read_op, p.p, false, false); p.v = p.p = 0; } protected: // Open a new socket implementation. ASIO_DECL asio::error_code do_open( base_implementation_type& impl, int af, int type, int protocol, asio::error_code& ec); // Assign a native socket to a socket implementation. ASIO_DECL asio::error_code do_assign( base_implementation_type& impl, int type, const native_type& native_socket, asio::error_code& ec); // Start the asynchronous read or write operation. ASIO_DECL void start_op(base_implementation_type& impl, int op_type, reactor_op* op, bool non_blocking, bool noop); // Start the asynchronous accept operation. ASIO_DECL void start_accept_op(base_implementation_type& impl, reactor_op* op, bool peer_is_open); // Start the asynchronous connect operation. ASIO_DECL void start_connect_op(base_implementation_type& impl, reactor_op* op, const socket_addr_type* addr, size_t addrlen); // The selector that performs event demultiplexing for the service. reactor& reactor_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/reactive_socket_service_base.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // !defined(ASIO_HAS_IOCP) #endif // ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_BASE_HPP percona-xtradb-cluster-galera/asio/asio/detail/reactor.hpp0000644000000000000000000000146012247075736024153 0ustar rootroot00000000000000// // detail/reactor.hpp // ~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_REACTOR_HPP #define ASIO_DETAIL_REACTOR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/reactor_fwd.hpp" #if defined(ASIO_HAS_EPOLL) # include "asio/detail/epoll_reactor.hpp" #elif defined(ASIO_HAS_KQUEUE) # include "asio/detail/kqueue_reactor.hpp" #elif defined(ASIO_HAS_DEV_POLL) # include "asio/detail/dev_poll_reactor.hpp" #else # include "asio/detail/select_reactor.hpp" #endif #endif // ASIO_DETAIL_REACTOR_HPP percona-xtradb-cluster-galera/asio/asio/detail/reactor_fwd.hpp0000644000000000000000000000242312247075736025013 0ustar rootroot00000000000000// // detail/reactor_fwd.hpp // ~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_REACTOR_FWD_HPP #define ASIO_DETAIL_REACTOR_FWD_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) # include "asio/detail/select_reactor_fwd.hpp" #elif defined(ASIO_HAS_EPOLL) # include "asio/detail/epoll_reactor_fwd.hpp" #elif defined(ASIO_HAS_KQUEUE) # include "asio/detail/kqueue_reactor_fwd.hpp" #elif defined(ASIO_HAS_DEV_POLL) # include "asio/detail/dev_poll_reactor_fwd.hpp" #else # include "asio/detail/select_reactor_fwd.hpp" #endif namespace asio { namespace detail { #if defined(ASIO_HAS_IOCP) typedef select_reactor reactor; #elif defined(ASIO_HAS_EPOLL) typedef epoll_reactor reactor; #elif defined(ASIO_HAS_KQUEUE) typedef kqueue_reactor reactor; #elif defined(ASIO_HAS_DEV_POLL) typedef dev_poll_reactor reactor; #else typedef select_reactor reactor; #endif } // namespace detail } // namespace asio #endif // ASIO_DETAIL_REACTOR_FWD_HPP percona-xtradb-cluster-galera/asio/asio/detail/reactor_op.hpp0000644000000000000000000000256112247075736024654 0ustar rootroot00000000000000// // detail/reactor_op.hpp // ~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_REACTOR_OP_HPP #define ASIO_DETAIL_REACTOR_OP_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/detail/operation.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class reactor_op : public operation { public: // The error code to be passed to the completion handler. asio::error_code ec_; // The number of bytes transferred, to be passed to the completion handler. std::size_t bytes_transferred_; // Perform the operation. Returns true if it is finished. bool perform() { return perform_func_(this); } protected: typedef bool (*perform_func_type)(reactor_op*); reactor_op(perform_func_type perform_func, func_type complete_func) : operation(complete_func), bytes_transferred_(0), perform_func_(perform_func) { } private: perform_func_type perform_func_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_REACTOR_OP_HPP percona-xtradb-cluster-galera/asio/asio/detail/reactor_op_queue.hpp0000644000000000000000000001307712247075736026064 0ustar rootroot00000000000000// // detail/reactor_op_queue.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_REACTOR_OP_QUEUE_HPP #define ASIO_DETAIL_REACTOR_OP_QUEUE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/detail/hash_map.hpp" #include "asio/detail/noncopyable.hpp" #include "asio/detail/op_queue.hpp" #include "asio/detail/reactor_op.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class reactor_op_queue : private noncopyable { public: // Constructor. reactor_op_queue() : operations_() { } // Add a new operation to the queue. Returns true if this is the only // operation for the given descriptor, in which case the reactor's event // demultiplexing function call may need to be interrupted and restarted. bool enqueue_operation(Descriptor descriptor, reactor_op* op) { typedef typename operations_map::iterator iterator; typedef typename operations_map::value_type value_type; std::pair entry = operations_.insert(value_type(descriptor, operations())); entry.first->second.op_queue_.push(op); return entry.second; } // Cancel all operations associated with the descriptor. Any operations // pending for the descriptor will be notified that they have been cancelled // next time perform_cancellations is called. Returns true if any operations // were cancelled, in which case the reactor's event demultiplexing function // may need to be interrupted and restarted. bool cancel_operations(Descriptor descriptor, op_queue& ops, const asio::error_code& ec = asio::error::operation_aborted) { typename operations_map::iterator i = operations_.find(descriptor); if (i != operations_.end()) { while (reactor_op* op = i->second.op_queue_.front()) { op->ec_ = ec; i->second.op_queue_.pop(); ops.push(op); } operations_.erase(i); return true; } return false; } // Whether there are no operations in the queue. bool empty() const { return operations_.empty(); } // Determine whether there are any operations associated with the descriptor. bool has_operation(Descriptor descriptor) const { return operations_.find(descriptor) != operations_.end(); } // Perform the operations corresponding to the descriptor. Returns true if // there are still unfinished operations queued for the descriptor. bool perform_operations(Descriptor descriptor, op_queue& ops) { typename operations_map::iterator i = operations_.find(descriptor); if (i != operations_.end()) { while (reactor_op* op = i->second.op_queue_.front()) { if (op->perform()) { i->second.op_queue_.pop(); ops.push(op); } else { return true; } } operations_.erase(i); } return false; } // Fill a descriptor set with the descriptors corresponding to each active // operation. The op_queue is used only when descriptors fail to be added to // the descriptor set. template void get_descriptors(Descriptor_Set& descriptors, op_queue& ops) { typename operations_map::iterator i = operations_.begin(); while (i != operations_.end()) { Descriptor descriptor = i->first; ++i; if (!descriptors.set(descriptor)) { asio::error_code ec(error::fd_set_failure); cancel_operations(descriptor, ops, ec); } } } // Perform the operations corresponding to the ready file descriptors // contained in the given descriptor set. template void perform_operations_for_descriptors( const Descriptor_Set& descriptors, op_queue& ops) { typename operations_map::iterator i = operations_.begin(); while (i != operations_.end()) { typename operations_map::iterator op_iter = i++; if (descriptors.is_set(op_iter->first)) { while (reactor_op* op = op_iter->second.op_queue_.front()) { if (op->perform()) { op_iter->second.op_queue_.pop(); ops.push(op); } else { break; } } if (op_iter->second.op_queue_.empty()) operations_.erase(op_iter); } } } // Get all operations owned by the queue. void get_all_operations(op_queue& ops) { typename operations_map::iterator i = operations_.begin(); while (i != operations_.end()) { typename operations_map::iterator op_iter = i++; ops.push(op_iter->second.op_queue_); operations_.erase(op_iter); } } private: struct operations { operations() {} operations(const operations&) {} void operator=(const operations&) {} // The operations waiting on the desccriptor. op_queue op_queue_; }; // The type for a map of operations. typedef hash_map operations_map; // The operations that are currently executing asynchronously. operations_map operations_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_REACTOR_OP_QUEUE_HPP percona-xtradb-cluster-galera/asio/asio/detail/regex_fwd.hpp0000644000000000000000000000134412247075736024467 0ustar rootroot00000000000000// // detail/regex_fwd.hpp // ~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_REGEX_FWD_HPP #define ASIO_DETAIL_REGEX_FWD_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include #include namespace boost { template struct sub_match; template class match_results; } // namespace boost #endif // ASIO_DETAIL_REGEX_FWD_HPP percona-xtradb-cluster-galera/asio/asio/detail/resolve_endpoint_op.hpp0000644000000000000000000000747312247075736026603 0ustar rootroot00000000000000// // detail/resolve_endpoint_op.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_RESOLVER_ENDPOINT_OP_HPP #define ASIO_DETAIL_RESOLVER_ENDPOINT_OP_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/ip/basic_resolver_iterator.hpp" #include "asio/detail/bind_handler.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/operation.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class resolve_endpoint_op : public operation { public: ASIO_DEFINE_HANDLER_PTR(resolve_endpoint_op); typedef typename Protocol::endpoint endpoint_type; typedef asio::ip::basic_resolver_iterator iterator_type; resolve_endpoint_op(socket_ops::weak_cancel_token_type cancel_token, const endpoint_type& endpoint, io_service_impl& ios, Handler handler) : operation(&resolve_endpoint_op::do_complete), cancel_token_(cancel_token), endpoint_(endpoint), io_service_impl_(ios), handler_(handler) { } static void do_complete(io_service_impl* owner, operation* base, asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) { // Take ownership of the operation object. resolve_endpoint_op* o(static_cast(base)); ptr p = { boost::addressof(o->handler_), o, o }; if (owner && owner != &o->io_service_impl_) { // The operation is being run on the worker io_service. Time to perform // the resolver operation. // Perform the blocking endpoint resolution operation. char host_name[NI_MAXHOST]; char service_name[NI_MAXSERV]; socket_ops::background_getnameinfo(o->cancel_token_, o->endpoint_.data(), o->endpoint_.size(), host_name, NI_MAXHOST, service_name, NI_MAXSERV, o->endpoint_.protocol().type(), o->ec_); o->iter_ = iterator_type::create(o->endpoint_, host_name, service_name); // Pass operation back to main io_service for completion. o->io_service_impl_.post_deferred_completion(o); p.v = p.p = 0; } else { // The operation has been returned to the main io_service. The completion // handler is ready to be delivered. // Make a copy of the handler so that the memory can be deallocated // before the upcall is made. Even if we're not about to make an upcall, // a sub-object of the handler may be the true owner of the memory // associated with the handler. Consequently, a local copy of the handler // is required to ensure that any owning sub-object remains valid until // after we have deallocated the memory here. detail::binder2 handler(o->handler_, o->ec_, o->iter_); p.h = boost::addressof(handler.handler_); p.reset(); if (owner) { asio::detail::fenced_block b; asio_handler_invoke_helpers::invoke(handler, handler.handler_); } } } private: socket_ops::weak_cancel_token_type cancel_token_; endpoint_type endpoint_; io_service_impl& io_service_impl_; Handler handler_; asio::error_code ec_; iterator_type iter_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_RESOLVER_ENDPOINT_OP_HPP percona-xtradb-cluster-galera/asio/asio/detail/resolve_op.hpp0000644000000000000000000000755612247075736024705 0ustar rootroot00000000000000// // detail/resolve_op.hpp // ~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_RESOLVE_OP_HPP #define ASIO_DETAIL_RESOLVE_OP_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/ip/basic_resolver_iterator.hpp" #include "asio/ip/basic_resolver_query.hpp" #include "asio/detail/bind_handler.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/operation.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class resolve_op : public operation { public: ASIO_DEFINE_HANDLER_PTR(resolve_op); typedef asio::ip::basic_resolver_query query_type; typedef asio::ip::basic_resolver_iterator iterator_type; resolve_op(socket_ops::weak_cancel_token_type cancel_token, const query_type& query, io_service_impl& ios, Handler handler) : operation(&resolve_op::do_complete), cancel_token_(cancel_token), query_(query), io_service_impl_(ios), handler_(handler), addrinfo_(0) { } ~resolve_op() { if (addrinfo_) socket_ops::freeaddrinfo(addrinfo_); } static void do_complete(io_service_impl* owner, operation* base, asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) { // Take ownership of the operation object. resolve_op* o(static_cast(base)); ptr p = { boost::addressof(o->handler_), o, o }; if (owner && owner != &o->io_service_impl_) { // The operation is being run on the worker io_service. Time to perform // the resolver operation. // Perform the blocking host resolution operation. socket_ops::background_getaddrinfo(o->cancel_token_, o->query_.host_name().c_str(), o->query_.service_name().c_str(), o->query_.hints(), &o->addrinfo_, o->ec_); // Pass operation back to main io_service for completion. o->io_service_impl_.post_deferred_completion(o); p.v = p.p = 0; } else { // The operation has been returned to the main io_service. The completion // handler is ready to be delivered. // Make a copy of the handler so that the memory can be deallocated // before the upcall is made. Even if we're not about to make an upcall, // a sub-object of the handler may be the true owner of the memory // associated with the handler. Consequently, a local copy of the handler // is required to ensure that any owning sub-object remains valid until // after we have deallocated the memory here. detail::binder2 handler(o->handler_, o->ec_, iterator_type()); p.h = boost::addressof(handler.handler_); if (o->addrinfo_) { handler.arg2_ = iterator_type::create(o->addrinfo_, o->query_.host_name(), o->query_.service_name()); } p.reset(); if (owner) { asio::detail::fenced_block b; asio_handler_invoke_helpers::invoke(handler, handler.handler_); } } } private: socket_ops::weak_cancel_token_type cancel_token_; query_type query_; io_service_impl& io_service_impl_; Handler handler_; asio::error_code ec_; asio::detail::addrinfo_type* addrinfo_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_RESOLVE_OP_HPP percona-xtradb-cluster-galera/asio/asio/detail/resolver_service.hpp0000644000000000000000000000727412247075736026106 0ustar rootroot00000000000000// // detail/resolver_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_RESOLVER_SERVICE_HPP #define ASIO_DETAIL_RESOLVER_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/ip/basic_resolver_iterator.hpp" #include "asio/ip/basic_resolver_query.hpp" #include "asio/detail/resolve_endpoint_op.hpp" #include "asio/detail/resolve_op.hpp" #include "asio/detail/resolver_service_base.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class resolver_service : public resolver_service_base { public: // The implementation type of the resolver. A cancellation token is used to // indicate to the background thread that the operation has been cancelled. typedef socket_ops::shared_cancel_token_type implementation_type; // The endpoint type. typedef typename Protocol::endpoint endpoint_type; // The query type. typedef asio::ip::basic_resolver_query query_type; // The iterator type. typedef asio::ip::basic_resolver_iterator iterator_type; // Constructor. resolver_service(asio::io_service& io_service) : resolver_service_base(io_service) { } // Resolve a query to a list of entries. iterator_type resolve(implementation_type&, const query_type& query, asio::error_code& ec) { asio::detail::addrinfo_type* address_info = 0; socket_ops::getaddrinfo(query.host_name().c_str(), query.service_name().c_str(), query.hints(), &address_info, ec); auto_addrinfo auto_address_info(address_info); return ec ? iterator_type() : iterator_type::create( address_info, query.host_name(), query.service_name()); } // Asynchronously resolve a query to a list of entries. template void async_resolve(implementation_type& impl, const query_type& query, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef resolve_op op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(impl, query, io_service_impl_, handler); start_resolve_op(p.p); p.v = p.p = 0; } // Resolve an endpoint to a list of entries. iterator_type resolve(implementation_type&, const endpoint_type& endpoint, asio::error_code& ec) { char host_name[NI_MAXHOST]; char service_name[NI_MAXSERV]; socket_ops::sync_getnameinfo(endpoint.data(), endpoint.size(), host_name, NI_MAXHOST, service_name, NI_MAXSERV, endpoint.protocol().type(), ec); return ec ? iterator_type() : iterator_type::create( endpoint, host_name, service_name); } // Asynchronously resolve an endpoint to a list of entries. template void async_resolve(implementation_type& impl, const endpoint_type& endpoint, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef resolve_endpoint_op op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(impl, endpoint, io_service_impl_, handler); start_resolve_op(p.p); p.v = p.p = 0; } }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_RESOLVER_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/detail/resolver_service_base.hpp0000644000000000000000000000652212247075736027073 0ustar rootroot00000000000000// // detail/resolver_service_base.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_RESOLVER_SERVICE_BASE_HPP #define ASIO_DETAIL_RESOLVER_SERVICE_BASE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/detail/mutex.hpp" #include "asio/detail/noncopyable.hpp" #include "asio/detail/operation.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/thread.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class resolver_service_base { public: // The implementation type of the resolver. A cancellation token is used to // indicate to the background thread that the operation has been cancelled. typedef socket_ops::shared_cancel_token_type implementation_type; // Constructor. ASIO_DECL resolver_service_base(asio::io_service& io_service); // Destructor. ASIO_DECL ~resolver_service_base(); // Destroy all user-defined handler objects owned by the service. ASIO_DECL void shutdown_service(); // Construct a new resolver implementation. ASIO_DECL void construct(implementation_type& impl); // Destroy a resolver implementation. ASIO_DECL void destroy(implementation_type&); // Cancel pending asynchronous operations. ASIO_DECL void cancel(implementation_type& impl); protected: // Helper function to start an asynchronous resolve operation. ASIO_DECL void start_resolve_op(operation* op); // Helper class to perform exception-safe cleanup of addrinfo objects. class auto_addrinfo : private asio::detail::noncopyable { public: explicit auto_addrinfo(asio::detail::addrinfo_type* ai) : ai_(ai) { } ~auto_addrinfo() { if (ai_) socket_ops::freeaddrinfo(ai_); } operator asio::detail::addrinfo_type*() { return ai_; } private: asio::detail::addrinfo_type* ai_; }; // Helper class to run the work io_service in a thread. class work_io_service_runner; // Start the work thread if it's not already running. ASIO_DECL void start_work_thread(); // The io_service implementation used to post completions. io_service_impl& io_service_impl_; private: // Mutex to protect access to internal data. asio::detail::mutex mutex_; // Private io_service used for performing asynchronous host resolution. boost::scoped_ptr work_io_service_; // The work io_service implementation used to post completions. io_service_impl& work_io_service_impl_; // Work for the private io_service to perform. boost::scoped_ptr work_; // Thread used for running the work io_service's run loop. boost::scoped_ptr work_thread_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/resolver_service_base.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // ASIO_DETAIL_RESOLVER_SERVICE_BASE_HPP percona-xtradb-cluster-galera/asio/asio/detail/scoped_lock.hpp0000644000000000000000000000314412247075736025002 0ustar rootroot00000000000000// // detail/scoped_lock.hpp // ~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_SCOPED_LOCK_HPP #define ASIO_DETAIL_SCOPED_LOCK_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/noncopyable.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { // Helper class to lock and unlock a mutex automatically. template class scoped_lock : private noncopyable { public: // Constructor acquires the lock. scoped_lock(Mutex& m) : mutex_(m) { mutex_.lock(); locked_ = true; } // Destructor releases the lock. ~scoped_lock() { if (locked_) mutex_.unlock(); } // Explicitly acquire the lock. void lock() { if (!locked_) { mutex_.lock(); locked_ = true; } } // Explicitly release the lock. void unlock() { if (locked_) { mutex_.unlock(); locked_ = false; } } // Test whether the lock is held. bool locked() const { return locked_; } // Get the underlying mutex. Mutex& mutex() { return mutex_; } private: // The underlying mutex. Mutex& mutex_; // Whether the mutex is currently locked or unlocked. bool locked_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_SCOPED_LOCK_HPP percona-xtradb-cluster-galera/asio/asio/detail/select_interrupter.hpp0000644000000000000000000000232112247075736026433 0ustar rootroot00000000000000// // detail/select_interrupter.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_SELECT_INTERRUPTER_HPP #define ASIO_DETAIL_SELECT_INTERRUPTER_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) || defined(__SYMBIAN32__) # include "asio/detail/socket_select_interrupter.hpp" #elif defined(ASIO_HAS_EVENTFD) # include "asio/detail/eventfd_select_interrupter.hpp" #else # include "asio/detail/pipe_select_interrupter.hpp" #endif namespace asio { namespace detail { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) || defined(__SYMBIAN32__) typedef socket_select_interrupter select_interrupter; #elif defined(ASIO_HAS_EVENTFD) typedef eventfd_select_interrupter select_interrupter; #else typedef pipe_select_interrupter select_interrupter; #endif } // namespace detail } // namespace asio #endif // ASIO_DETAIL_SELECT_INTERRUPTER_HPP percona-xtradb-cluster-galera/asio/asio/detail/select_reactor.hpp0000644000000000000000000001424512247075736025517 0ustar rootroot00000000000000// // detail/select_reactor.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_SELECT_REACTOR_HPP #define ASIO_DETAIL_SELECT_REACTOR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) \ || (!defined(ASIO_HAS_DEV_POLL) \ && !defined(ASIO_HAS_EPOLL) \ && !defined(ASIO_HAS_KQUEUE)) #include #include "asio/detail/mutex.hpp" #include "asio/detail/op_queue.hpp" #include "asio/detail/reactor_op.hpp" #include "asio/detail/reactor_op_queue.hpp" #include "asio/detail/select_interrupter.hpp" #include "asio/detail/select_reactor_fwd.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/timer_op.hpp" #include "asio/detail/timer_queue_base.hpp" #include "asio/detail/timer_queue_fwd.hpp" #include "asio/detail/timer_queue_set.hpp" #include "asio/io_service.hpp" #if defined(ASIO_HAS_IOCP) # include "asio/detail/thread.hpp" #endif // defined(ASIO_HAS_IOCP) #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class select_reactor : public asio::detail::service_base { public: #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) enum { read_op = 0, write_op = 1, except_op = 2, max_select_ops = 3, connect_op = 3, max_ops = 4 }; #else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) enum { read_op = 0, write_op = 1, except_op = 2, max_select_ops = 3, connect_op = 1, max_ops = 3 }; #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) // Per-descriptor data. struct per_descriptor_data { }; // Constructor. ASIO_DECL select_reactor(asio::io_service& io_service); // Destructor. ASIO_DECL ~select_reactor(); // Destroy all user-defined handler objects owned by the service. ASIO_DECL void shutdown_service(); // Initialise the task, but only if the reactor is not in its own thread. ASIO_DECL void init_task(); // Register a socket with the reactor. Returns 0 on success, system error // code on failure. ASIO_DECL int register_descriptor(socket_type, per_descriptor_data&); // Post a reactor operation for immediate completion. void post_immediate_completion(reactor_op* op) { io_service_.post_immediate_completion(op); } // Start a new operation. The reactor operation will be performed when the // given descriptor is flagged as ready, or an error has occurred. ASIO_DECL void start_op(int op_type, socket_type descriptor, per_descriptor_data&, reactor_op* op, bool); // Cancel all operations associated with the given descriptor. The // handlers associated with the descriptor will be invoked with the // operation_aborted error. ASIO_DECL void cancel_ops(socket_type descriptor, per_descriptor_data&); // Cancel any operations that are running against the descriptor and remove // its registration from the reactor. ASIO_DECL void close_descriptor(socket_type descriptor, per_descriptor_data&); // Add a new timer queue to the reactor. template void add_timer_queue(timer_queue& queue); // Remove a timer queue from the reactor. template void remove_timer_queue(timer_queue& queue); // Schedule a new operation in the given timer queue to expire at the // specified absolute time. template void schedule_timer(timer_queue& queue, const typename Time_Traits::time_type& time, typename timer_queue::per_timer_data& timer, timer_op* op); // Cancel the timer operations associated with the given token. Returns the // number of operations that have been posted or dispatched. template std::size_t cancel_timer(timer_queue& queue, typename timer_queue::per_timer_data& timer); // Run select once until interrupted or events are ready to be dispatched. ASIO_DECL void run(bool block, op_queue& ops); // Interrupt the select loop. ASIO_DECL void interrupt(); private: #if defined(ASIO_HAS_IOCP) // Run the select loop in the thread. ASIO_DECL void run_thread(); // Entry point for the select loop thread. ASIO_DECL static void call_run_thread(select_reactor* reactor); #endif // defined(ASIO_HAS_IOCP) // Helper function to add a new timer queue. ASIO_DECL void do_add_timer_queue(timer_queue_base& queue); // Helper function to remove a timer queue. ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue); // Get the timeout value for the select call. ASIO_DECL timeval* get_timeout(timeval& tv); // Cancel all operations associated with the given descriptor. This function // does not acquire the select_reactor's mutex. ASIO_DECL void cancel_ops_unlocked(socket_type descriptor, const asio::error_code& ec); // The io_service implementation used to post completions. io_service_impl& io_service_; // Mutex to protect access to internal data. asio::detail::mutex mutex_; // The interrupter is used to break a blocking select call. select_interrupter interrupter_; // The queues of read, write and except operations. reactor_op_queue op_queue_[max_ops]; // The timer queues. timer_queue_set timer_queues_; #if defined(ASIO_HAS_IOCP) // Does the reactor loop thread need to stop. bool stop_thread_; // The thread that is running the reactor loop. asio::detail::thread* thread_; #endif // defined(ASIO_HAS_IOCP) // Whether the service has been shut down. bool shutdown_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #include "asio/detail/impl/select_reactor.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/select_reactor.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // defined(ASIO_HAS_IOCP) // || (!defined(ASIO_HAS_DEV_POLL) // && !defined(ASIO_HAS_EPOLL) // && !defined(ASIO_HAS_KQUEUE)) #endif // ASIO_DETAIL_SELECT_REACTOR_HPP percona-xtradb-cluster-galera/asio/asio/detail/select_reactor_fwd.hpp0000644000000000000000000000121712247075736026352 0ustar rootroot00000000000000// // detail/select_reactor_fwd.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_SELECT_REACTOR_FWD_HPP #define ASIO_DETAIL_SELECT_REACTOR_FWD_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) namespace asio { namespace detail { class select_reactor; } // namespace detail } // namespace asio #endif // ASIO_DETAIL_SELECT_REACTOR_FWD_HPP percona-xtradb-cluster-galera/asio/asio/detail/service_base.hpp0000644000000000000000000000216312247075736025147 0ustar rootroot00000000000000// // service_base.hpp // ~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_SERVICE_BASE_HPP #define ASIO_DETAIL_SERVICE_BASE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/push_options.hpp" #include "asio/io_service.hpp" #include "asio/detail/service_id.hpp" namespace asio { namespace detail { // Special service base class to keep classes header-file only. template class service_base : public asio::io_service::service { public: static asio::detail::service_id id; // Constructor. service_base(asio::io_service& io_service) : asio::io_service::service(io_service) { } }; template asio::detail::service_id service_base::id; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_SERVICE_BASE_HPP percona-xtradb-cluster-galera/asio/asio/detail/service_id.hpp0000644000000000000000000000151012247075736024624 0ustar rootroot00000000000000// // service_id.hpp // ~~~~~~~~~~~~~~ // // Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_SERVICE_ID_HPP #define ASIO_DETAIL_SERVICE_ID_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/push_options.hpp" #include "asio/io_service.hpp" namespace asio { namespace detail { // Special derived service id type to keep classes header-file only. template class service_id : public asio::io_service::id { }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_SERVICE_ID_HPP percona-xtradb-cluster-galera/asio/asio/detail/service_registry.hpp0000644000000000000000000001126012247075736026103 0ustar rootroot00000000000000// // detail/service_registry.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_SERVICE_REGISTRY_HPP #define ASIO_DETAIL_SERVICE_REGISTRY_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/detail/mutex.hpp" #include "asio/detail/noncopyable.hpp" #include "asio/io_service.hpp" #if defined(BOOST_NO_TYPEID) # if !defined(ASIO_NO_TYPEID) # define ASIO_NO_TYPEID # endif // !defined(ASIO_NO_TYPEID) #endif // defined(BOOST_NO_TYPEID) #include "asio/detail/push_options.hpp" namespace asio { namespace detail { #if defined(__GNUC__) # if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) # pragma GCC visibility push (default) # endif // (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) #endif // defined(__GNUC__) template class typeid_wrapper {}; #if defined(__GNUC__) # if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) # pragma GCC visibility pop # endif // (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) #endif // defined(__GNUC__) class service_registry : private noncopyable { public: // Constructor. ASIO_DECL service_registry(asio::io_service& o); // Destructor. ASIO_DECL ~service_registry(); // Get the service object corresponding to the specified service type. Will // create a new service object automatically if no such object already // exists. Ownership of the service object is not transferred to the caller. template Service& use_service(); // Add a service object. Throws on error, in which case ownership of the // object is retained by the caller. template void add_service(Service* new_service); // Check whether a service object of the specified type already exists. template bool has_service() const; private: // Initialise a service's key based on its id. ASIO_DECL static void init_key( asio::io_service::service::key& key, const asio::io_service::id& id); #if !defined(ASIO_NO_TYPEID) // Initialise a service's key based on its id. template static void init_key(asio::io_service::service::key& key, const asio::detail::service_id& /*id*/); #endif // !defined(ASIO_NO_TYPEID) // Check if a service matches the given id. ASIO_DECL static bool keys_match( const asio::io_service::service::key& key1, const asio::io_service::service::key& key2); // The type of a factory function used for creating a service instance. typedef asio::io_service::service* (*factory_type)(asio::io_service&); // Factory function for creating a service instance. template static asio::io_service::service* create( asio::io_service& owner); // Destroy a service instance. ASIO_DECL static void destroy( asio::io_service::service* service); // Helper class to manage service pointers. struct auto_service_ptr; friend struct auto_service_ptr; struct auto_service_ptr { asio::io_service::service* ptr_; ~auto_service_ptr() { destroy(ptr_); } }; // Get the service object corresponding to the specified service key. Will // create a new service object automatically if no such object already // exists. Ownership of the service object is not transferred to the caller. ASIO_DECL asio::io_service::service* do_use_service( const asio::io_service::service::key& key, factory_type factory); // Add a service object. Returns false on error, in which case ownership of // the object is retained by the caller. ASIO_DECL void do_add_service( const asio::io_service::service::key& key, asio::io_service::service* new_service); // Check whether a service object with the specified key already exists. ASIO_DECL bool do_has_service( const asio::io_service::service::key& key) const; // Mutex to protect access to internal data. mutable asio::detail::mutex mutex_; // The owner of this service registry and the services it contains. asio::io_service& owner_; // The first service in the list of contained services. asio::io_service::service* first_service_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #include "asio/detail/impl/service_registry.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/service_registry.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // ASIO_DETAIL_SERVICE_REGISTRY_HPP percona-xtradb-cluster-galera/asio/asio/detail/service_registry_fwd.hpp0000644000000000000000000000123312247075736026742 0ustar rootroot00000000000000// // detail/service_registry_fwd.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_SERVICE_REGISTRY_FWD_HPP #define ASIO_DETAIL_SERVICE_REGISTRY_FWD_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) namespace asio { namespace detail { class service_registry; } // namespace detail } // namespace asio #endif // ASIO_DETAIL_SERVICE_REGISTRY_FWD_HPP percona-xtradb-cluster-galera/asio/asio/detail/shared_ptr.hpp0000644000000000000000000000151312247075736024646 0ustar rootroot00000000000000// // detail/shared_ptr.hpp // ~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_SHARED_PTR_HPP #define ASIO_DETAIL_SHARED_PTR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(_MSC_VER) && (_MSC_VER >= 1600) # include #else # include #endif namespace asio { namespace detail { #if defined(_MSC_VER) && (_MSC_VER >= 1600) using std::shared_ptr; #else using boost::shared_ptr; #endif } // namespace detail } // namespace asio #endif // ASIO_DETAIL_SHARED_PTR_HPP percona-xtradb-cluster-galera/asio/asio/detail/signal_blocker.hpp0000644000000000000000000000235112247075736025472 0ustar rootroot00000000000000// // detail/signal_blocker.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_SIGNAL_BLOCKER_HPP #define ASIO_DETAIL_SIGNAL_BLOCKER_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) \ || defined(BOOST_WINDOWS) || defined(__CYGWIN__) || defined(__SYMBIAN32__) # include "asio/detail/null_signal_blocker.hpp" #elif defined(BOOST_HAS_PTHREADS) # include "asio/detail/posix_signal_blocker.hpp" #else # error Only Windows and POSIX are supported! #endif namespace asio { namespace detail { #if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) \ || defined(BOOST_WINDOWS) || defined(__CYGWIN__) || defined(__SYMBIAN32__) typedef null_signal_blocker signal_blocker; #elif defined(BOOST_HAS_PTHREADS) typedef posix_signal_blocker signal_blocker; #endif } // namespace detail } // namespace asio #endif // ASIO_DETAIL_SIGNAL_BLOCKER_HPP percona-xtradb-cluster-galera/asio/asio/detail/signal_init.hpp0000644000000000000000000000173112247075736025015 0ustar rootroot00000000000000// // detail/signal_init.hpp // ~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_SIGNAL_INIT_HPP #define ASIO_DETAIL_SIGNAL_INIT_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) #include #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class signal_init { public: // Constructor. signal_init() { std::signal(Signal, SIG_IGN); } }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) #endif // ASIO_DETAIL_SIGNAL_INIT_HPP percona-xtradb-cluster-galera/asio/asio/detail/socket_holder.hpp0000644000000000000000000000377312247075736025352 0ustar rootroot00000000000000// // detail/socket_holder.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_SOCKET_HOLDER_HPP #define ASIO_DETAIL_SOCKET_HOLDER_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/detail/noncopyable.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { // Implement the resource acquisition is initialisation idiom for sockets. class socket_holder : private noncopyable { public: // Construct as an uninitialised socket. socket_holder() : socket_(invalid_socket) { } // Construct to take ownership of the specified socket. explicit socket_holder(socket_type s) : socket_(s) { } // Destructor. ~socket_holder() { if (socket_ != invalid_socket) { asio::error_code ec; socket_ops::state_type state = 0; socket_ops::close(socket_, state, true, ec); } } // Get the underlying socket. socket_type get() const { return socket_; } // Reset to an uninitialised socket. void reset() { if (socket_ != invalid_socket) { asio::error_code ec; socket_ops::state_type state = 0; socket_ops::close(socket_, state, true, ec); socket_ = invalid_socket; } } // Reset to take ownership of the specified socket. void reset(socket_type s) { reset(); socket_ = s; } // Release ownership of the socket. socket_type release() { socket_type tmp = socket_; socket_ = invalid_socket; return tmp; } private: // The underlying socket. socket_type socket_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_SOCKET_HOLDER_HPP percona-xtradb-cluster-galera/asio/asio/detail/socket_ops.hpp0000644000000000000000000002215112247075736024665 0ustar rootroot00000000000000// // detail/socket_ops.hpp // ~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_SOCKET_OPS_HPP #define ASIO_DETAIL_SOCKET_OPS_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/error_code.hpp" #include "asio/detail/shared_ptr.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/weak_ptr.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { namespace socket_ops { // Socket state bits. enum { // The user wants a non-blocking socket. user_set_non_blocking = 1, // The socket has been set non-blocking. internal_non_blocking = 2, // Helper "state" used to determine whether the socket is non-blocking. non_blocking = user_set_non_blocking | internal_non_blocking, // User wants connection_aborted errors, which are disabled by default. enable_connection_aborted = 4, // The user set the linger option. Needs to be checked when closing. user_set_linger = 8, // The socket is stream-oriented. stream_oriented = 16, // The socket is datagram-oriented. datagram_oriented = 32 }; typedef unsigned char state_type; struct noop_deleter { void operator()(void*) {} }; typedef shared_ptr shared_cancel_token_type; typedef weak_ptr weak_cancel_token_type; ASIO_DECL socket_type accept(socket_type s, socket_addr_type* addr, std::size_t* addrlen, asio::error_code& ec); ASIO_DECL socket_type sync_accept(socket_type s, state_type state, socket_addr_type* addr, std::size_t* addrlen, asio::error_code& ec); #if defined(ASIO_HAS_IOCP) ASIO_DECL void complete_iocp_accept(socket_type s, void* output_buffer, DWORD address_length, socket_addr_type* addr, std::size_t* addrlen, socket_type new_socket, asio::error_code& ec); #else // defined(ASIO_HAS_IOCP) ASIO_DECL bool non_blocking_accept(socket_type s, state_type state, socket_addr_type* addr, std::size_t* addrlen, asio::error_code& ec, socket_type& new_socket); #endif // defined(ASIO_HAS_IOCP) ASIO_DECL int bind(socket_type s, const socket_addr_type* addr, std::size_t addrlen, asio::error_code& ec); ASIO_DECL int close(socket_type s, state_type& state, bool destruction, asio::error_code& ec); ASIO_DECL bool set_internal_non_blocking(socket_type s, state_type& state, asio::error_code& ec); ASIO_DECL int shutdown(socket_type s, int what, asio::error_code& ec); ASIO_DECL int connect(socket_type s, const socket_addr_type* addr, std::size_t addrlen, asio::error_code& ec); ASIO_DECL void sync_connect(socket_type s, const socket_addr_type* addr, std::size_t addrlen, asio::error_code& ec); ASIO_DECL bool non_blocking_connect( socket_type s, asio::error_code& ec); ASIO_DECL int socketpair(int af, int type, int protocol, socket_type sv[2], asio::error_code& ec); ASIO_DECL bool sockatmark(socket_type s, asio::error_code& ec); ASIO_DECL size_t available(socket_type s, asio::error_code& ec); ASIO_DECL int listen(socket_type s, int backlog, asio::error_code& ec); #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) typedef WSABUF buf; #else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) typedef iovec buf; #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) ASIO_DECL void init_buf(buf& b, void* data, size_t size); ASIO_DECL void init_buf(buf& b, const void* data, size_t size); ASIO_DECL int recv(socket_type s, buf* bufs, size_t count, int flags, asio::error_code& ec); ASIO_DECL size_t sync_recv(socket_type s, state_type state, buf* bufs, size_t count, int flags, bool all_empty, asio::error_code& ec); #if defined(ASIO_HAS_IOCP) ASIO_DECL void complete_iocp_recv(state_type state, const weak_cancel_token_type& cancel_token, bool all_empty, asio::error_code& ec, size_t bytes_transferred); #else // defined(ASIO_HAS_IOCP) ASIO_DECL bool non_blocking_recv(socket_type s, buf* bufs, size_t count, int flags, bool is_stream, asio::error_code& ec, size_t& bytes_transferred); #endif // defined(ASIO_HAS_IOCP) ASIO_DECL int recvfrom(socket_type s, buf* bufs, size_t count, int flags, socket_addr_type* addr, std::size_t* addrlen, asio::error_code& ec); ASIO_DECL size_t sync_recvfrom(socket_type s, state_type state, buf* bufs, size_t count, int flags, socket_addr_type* addr, std::size_t* addrlen, asio::error_code& ec); #if defined(ASIO_HAS_IOCP) ASIO_DECL void complete_iocp_recvfrom( const weak_cancel_token_type& cancel_token, asio::error_code& ec); #else // defined(ASIO_HAS_IOCP) ASIO_DECL bool non_blocking_recvfrom(socket_type s, buf* bufs, size_t count, int flags, socket_addr_type* addr, std::size_t* addrlen, asio::error_code& ec, size_t& bytes_transferred); #endif // defined(ASIO_HAS_IOCP) ASIO_DECL int send(socket_type s, const buf* bufs, size_t count, int flags, asio::error_code& ec); ASIO_DECL size_t sync_send(socket_type s, state_type state, const buf* bufs, size_t count, int flags, bool all_empty, asio::error_code& ec); #if defined(ASIO_HAS_IOCP) ASIO_DECL void complete_iocp_send( const weak_cancel_token_type& cancel_token, asio::error_code& ec); #else // defined(ASIO_HAS_IOCP) ASIO_DECL bool non_blocking_send(socket_type s, const buf* bufs, size_t count, int flags, asio::error_code& ec, size_t& bytes_transferred); #endif // defined(ASIO_HAS_IOCP) ASIO_DECL int sendto(socket_type s, const buf* bufs, size_t count, int flags, const socket_addr_type* addr, std::size_t addrlen, asio::error_code& ec); ASIO_DECL size_t sync_sendto(socket_type s, state_type state, const buf* bufs, size_t count, int flags, const socket_addr_type* addr, std::size_t addrlen, asio::error_code& ec); #if !defined(ASIO_HAS_IOCP) ASIO_DECL bool non_blocking_sendto(socket_type s, const buf* bufs, size_t count, int flags, const socket_addr_type* addr, std::size_t addrlen, asio::error_code& ec, size_t& bytes_transferred); #endif // !defined(ASIO_HAS_IOCP) ASIO_DECL socket_type socket(int af, int type, int protocol, asio::error_code& ec); ASIO_DECL int setsockopt(socket_type s, state_type& state, int level, int optname, const void* optval, std::size_t optlen, asio::error_code& ec); ASIO_DECL int getsockopt(socket_type s, state_type state, int level, int optname, void* optval, size_t* optlen, asio::error_code& ec); ASIO_DECL int getpeername(socket_type s, socket_addr_type* addr, std::size_t* addrlen, bool cached, asio::error_code& ec); ASIO_DECL int getsockname(socket_type s, socket_addr_type* addr, std::size_t* addrlen, asio::error_code& ec); ASIO_DECL int ioctl(socket_type s, state_type& state, int cmd, ioctl_arg_type* arg, asio::error_code& ec); ASIO_DECL int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, timeval* timeout, asio::error_code& ec); ASIO_DECL int poll_read(socket_type s, asio::error_code& ec); ASIO_DECL int poll_write(socket_type s, asio::error_code& ec); ASIO_DECL int poll_connect(socket_type s, asio::error_code& ec); ASIO_DECL const char* inet_ntop(int af, const void* src, char* dest, size_t length, unsigned long scope_id, asio::error_code& ec); ASIO_DECL int inet_pton(int af, const char* src, void* dest, unsigned long* scope_id, asio::error_code& ec); ASIO_DECL int gethostname(char* name, int namelen, asio::error_code& ec); ASIO_DECL asio::error_code getaddrinfo(const char* host, const char* service, const addrinfo_type& hints, addrinfo_type** result, asio::error_code& ec); ASIO_DECL asio::error_code background_getaddrinfo( const weak_cancel_token_type& cancel_token, const char* host, const char* service, const addrinfo_type& hints, addrinfo_type** result, asio::error_code& ec); ASIO_DECL void freeaddrinfo(addrinfo_type* ai); ASIO_DECL asio::error_code getnameinfo( const socket_addr_type* addr, std::size_t addrlen, char* host, std::size_t hostlen, char* serv, std::size_t servlen, int flags, asio::error_code& ec); ASIO_DECL asio::error_code sync_getnameinfo( const socket_addr_type* addr, std::size_t addrlen, char* host, std::size_t hostlen, char* serv, std::size_t servlen, int sock_type, asio::error_code& ec); ASIO_DECL asio::error_code background_getnameinfo( const weak_cancel_token_type& cancel_token, const socket_addr_type* addr, std::size_t addrlen, char* host, std::size_t hostlen, char* serv, std::size_t servlen, int sock_type, asio::error_code& ec); ASIO_DECL u_long_type network_to_host_long(u_long_type value); ASIO_DECL u_long_type host_to_network_long(u_long_type value); ASIO_DECL u_short_type network_to_host_short(u_short_type value); ASIO_DECL u_short_type host_to_network_short(u_short_type value); } // namespace socket_ops } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/socket_ops.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // ASIO_DETAIL_SOCKET_OPS_HPP percona-xtradb-cluster-galera/asio/asio/detail/socket_option.hpp0000644000000000000000000001415712247075736025403 0ustar rootroot00000000000000// // detail/socket_option.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_SOCKET_OPTION_HPP #define ASIO_DETAIL_SOCKET_OPTION_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include #include #include "asio/detail/socket_types.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { namespace socket_option { // Helper template for implementing boolean-based options. template class boolean { public: // Default constructor. boolean() : value_(0) { } // Construct with a specific option value. explicit boolean(bool v) : value_(v ? 1 : 0) { } // Set the current value of the boolean. boolean& operator=(bool v) { value_ = v ? 1 : 0; return *this; } // Get the current value of the boolean. bool value() const { return !!value_; } // Convert to bool. operator bool() const { return !!value_; } // Test for false. bool operator!() const { return !value_; } // Get the level of the socket option. template int level(const Protocol&) const { return Level; } // Get the name of the socket option. template int name(const Protocol&) const { return Name; } // Get the address of the boolean data. template int* data(const Protocol&) { return &value_; } // Get the address of the boolean data. template const int* data(const Protocol&) const { return &value_; } // Get the size of the boolean data. template std::size_t size(const Protocol&) const { return sizeof(value_); } // Set the size of the boolean data. template void resize(const Protocol&, std::size_t s) { // On some platforms (e.g. Windows Vista), the getsockopt function will // return the size of a boolean socket option as one byte, even though a // four byte integer was passed in. switch (s) { case sizeof(char): value_ = *reinterpret_cast(&value_) ? 1 : 0; break; case sizeof(value_): break; default: { std::length_error ex("boolean socket option resize"); boost::throw_exception(ex); } } } private: int value_; }; // Helper template for implementing integer options. template class integer { public: // Default constructor. integer() : value_(0) { } // Construct with a specific option value. explicit integer(int v) : value_(v) { } // Set the value of the int option. integer& operator=(int v) { value_ = v; return *this; } // Get the current value of the int option. int value() const { return value_; } // Get the level of the socket option. template int level(const Protocol&) const { return Level; } // Get the name of the socket option. template int name(const Protocol&) const { return Name; } // Get the address of the int data. template int* data(const Protocol&) { return &value_; } // Get the address of the int data. template const int* data(const Protocol&) const { return &value_; } // Get the size of the int data. template std::size_t size(const Protocol&) const { return sizeof(value_); } // Set the size of the int data. template void resize(const Protocol&, std::size_t s) { if (s != sizeof(value_)) { std::length_error ex("integer socket option resize"); boost::throw_exception(ex); } } private: int value_; }; // Helper template for implementing linger options. template class linger { public: // Default constructor. linger() { value_.l_onoff = 0; value_.l_linger = 0; } // Construct with specific option values. linger(bool e, int t) { enabled(e); timeout BOOST_PREVENT_MACRO_SUBSTITUTION(t); } // Set the value for whether linger is enabled. void enabled(bool value) { value_.l_onoff = value ? 1 : 0; } // Get the value for whether linger is enabled. bool enabled() const { return value_.l_onoff != 0; } // Set the value for the linger timeout. void timeout BOOST_PREVENT_MACRO_SUBSTITUTION(int value) { #if defined(WIN32) value_.l_linger = static_cast(value); #else value_.l_linger = value; #endif } // Get the value for the linger timeout. int timeout BOOST_PREVENT_MACRO_SUBSTITUTION() const { return static_cast(value_.l_linger); } // Get the level of the socket option. template int level(const Protocol&) const { return Level; } // Get the name of the socket option. template int name(const Protocol&) const { return Name; } // Get the address of the linger data. template ::linger* data(const Protocol&) { return &value_; } // Get the address of the linger data. template const ::linger* data(const Protocol&) const { return &value_; } // Get the size of the linger data. template std::size_t size(const Protocol&) const { return sizeof(value_); } // Set the size of the int data. template void resize(const Protocol&, std::size_t s) { if (s != sizeof(value_)) { std::length_error ex("linger socket option resize"); boost::throw_exception(ex); } } private: ::linger value_; }; } // namespace socket_option } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_SOCKET_OPTION_HPP percona-xtradb-cluster-galera/asio/asio/detail/socket_select_interrupter.hpp0000644000000000000000000000412612247075736030010 0ustar rootroot00000000000000// // detail/socket_select_interrupter.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP #define ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) \ || defined(__CYGWIN__) \ || defined(__SYMBIAN32__) #include "asio/detail/socket_types.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class socket_select_interrupter { public: // Constructor. ASIO_DECL socket_select_interrupter(); // Destructor. ASIO_DECL ~socket_select_interrupter(); // Interrupt the select call. ASIO_DECL void interrupt(); // Reset the select interrupt. Returns true if the call was interrupted. ASIO_DECL bool reset(); // Get the read descriptor to be passed to select. socket_type read_descriptor() const { return read_descriptor_; } private: // The read end of a connection used to interrupt the select call. This file // descriptor is passed to select such that when it is time to stop, a single // byte will be written on the other end of the connection and this // descriptor will become readable. socket_type read_descriptor_; // The write end of a connection used to interrupt the select call. A single // byte may be written to this to wake up the select which is waiting for the // other end to become readable. socket_type write_descriptor_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/socket_select_interrupter.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // defined(BOOST_WINDOWS) // || defined(__CYGWIN__) // || defined(__SYMBIAN32__) #endif // ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP percona-xtradb-cluster-galera/asio/asio/detail/socket_types.hpp0000644000000000000000000001204612247075736025232 0ustar rootroot00000000000000// // detail/socket_types.hpp // ~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_SOCKET_TYPES_HPP #define ASIO_DETAIL_SOCKET_TYPES_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) # if defined(_WINSOCKAPI_) && !defined(_WINSOCK2API_) # error WinSock.h has already been included # endif // defined(_WINSOCKAPI_) && !defined(_WINSOCK2API_) # if defined(__BORLANDC__) # include // Needed for __errno # if !defined(_WSPIAPI_H_) # define _WSPIAPI_H_ # define ASIO_WSPIAPI_H_DEFINED # endif // !defined(_WSPIAPI_H_) # endif // defined(__BORLANDC__) # include # include # include # if defined(ASIO_WSPIAPI_H_DEFINED) # undef _WSPIAPI_H_ # undef ASIO_WSPIAPI_H_DEFINED # endif // defined(ASIO_WSPIAPI_H_DEFINED) # if !defined(ASIO_NO_DEFAULT_LINKED_LIBS) # if defined(UNDER_CE) # pragma comment(lib, "ws2.lib") # elif defined(_MSC_VER) || defined(__BORLANDC__) # pragma comment(lib, "ws2_32.lib") # pragma comment(lib, "mswsock.lib") # endif // defined(_MSC_VER) || defined(__BORLANDC__) # endif // !defined(ASIO_NO_DEFAULT_LINKED_LIBS) # include "asio/detail/old_win_sdk_compat.hpp" #else # include # if !defined(__SYMBIAN32__) # include # endif # include # include # include # if defined(__hpux) # include # endif # if !defined(__hpux) || defined(__SELECT) # include # endif # include # include # include # include # if !defined(__SYMBIAN32__) # include # endif # include # include # include # include # if defined(__sun) # include # include # endif #endif #include "asio/detail/push_options.hpp" namespace asio { namespace detail { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) typedef SOCKET socket_type; const SOCKET invalid_socket = INVALID_SOCKET; const int socket_error_retval = SOCKET_ERROR; const int max_addr_v4_str_len = 256; const int max_addr_v6_str_len = 256; typedef sockaddr socket_addr_type; typedef in_addr in4_addr_type; typedef ip_mreq in4_mreq_type; typedef sockaddr_in sockaddr_in4_type; # if defined(ASIO_HAS_OLD_WIN_SDK) typedef in6_addr_emulation in6_addr_type; typedef ipv6_mreq_emulation in6_mreq_type; typedef sockaddr_in6_emulation sockaddr_in6_type; typedef sockaddr_storage_emulation sockaddr_storage_type; typedef addrinfo_emulation addrinfo_type; # else typedef in6_addr in6_addr_type; typedef ipv6_mreq in6_mreq_type; typedef sockaddr_in6 sockaddr_in6_type; typedef sockaddr_storage sockaddr_storage_type; typedef addrinfo addrinfo_type; # endif typedef unsigned long ioctl_arg_type; typedef u_long u_long_type; typedef u_short u_short_type; const int shutdown_receive = SD_RECEIVE; const int shutdown_send = SD_SEND; const int shutdown_both = SD_BOTH; const int message_peek = MSG_PEEK; const int message_out_of_band = MSG_OOB; const int message_do_not_route = MSG_DONTROUTE; # if defined (_WIN32_WINNT) const int max_iov_len = 64; # else const int max_iov_len = 16; # endif #else typedef int socket_type; const int invalid_socket = -1; const int socket_error_retval = -1; const int max_addr_v4_str_len = INET_ADDRSTRLEN; #if defined(INET6_ADDRSTRLEN) const int max_addr_v6_str_len = INET6_ADDRSTRLEN + 1 + IF_NAMESIZE; #else // defined(INET6_ADDRSTRLEN) const int max_addr_v6_str_len = 256; #endif // defined(INET6_ADDRSTRLEN) typedef sockaddr socket_addr_type; typedef in_addr in4_addr_type; # if defined(__hpux) // HP-UX doesn't provide ip_mreq when _XOPEN_SOURCE_EXTENDED is defined. struct in4_mreq_type { struct in_addr imr_multiaddr; struct in_addr imr_interface; }; # else typedef ip_mreq in4_mreq_type; # endif typedef sockaddr_in sockaddr_in4_type; typedef in6_addr in6_addr_type; typedef ipv6_mreq in6_mreq_type; typedef sockaddr_in6 sockaddr_in6_type; typedef sockaddr_storage sockaddr_storage_type; typedef sockaddr_un sockaddr_un_type; typedef addrinfo addrinfo_type; typedef int ioctl_arg_type; typedef uint32_t u_long_type; typedef uint16_t u_short_type; const int shutdown_receive = SHUT_RD; const int shutdown_send = SHUT_WR; const int shutdown_both = SHUT_RDWR; const int message_peek = MSG_PEEK; const int message_out_of_band = MSG_OOB; const int message_do_not_route = MSG_DONTROUTE; # if defined(IOV_MAX) const int max_iov_len = IOV_MAX; # else // POSIX platforms are not required to define IOV_MAX. const int max_iov_len = 16; # endif #endif const int custom_socket_option_level = 0xA5100000; const int enable_connection_aborted_option = 1; const int always_fail_option = 2; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_SOCKET_TYPES_HPP percona-xtradb-cluster-galera/asio/asio/detail/solaris_fenced_block.hpp0000644000000000000000000000201712247075736026645 0ustar rootroot00000000000000// // detail/solaris_fenced_block.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_SOLARIS_FENCED_BLOCK_HPP #define ASIO_DETAIL_SOLARIS_FENCED_BLOCK_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(__sun) #include #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class solaris_fenced_block : private noncopyable { public: // Constructor. solaris_fenced_block() { membar_consumer(); } // Destructor. ~solaris_fenced_block() { membar_producer(); } }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(__sun) #endif // ASIO_DETAIL_SOLARIS_FENCED_BLOCK_HPP percona-xtradb-cluster-galera/asio/asio/detail/strand_service.hpp0000644000000000000000000000655412247075736025540 0ustar rootroot00000000000000// // detail/strand_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_STRAND_SERVICE_HPP #define ASIO_DETAIL_STRAND_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/io_service.hpp" #include "asio/detail/mutex.hpp" #include "asio/detail/op_queue.hpp" #include "asio/detail/operation.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { // Default service implementation for a strand. class strand_service : public asio::detail::service_base { private: // Helper class to re-post the strand on exit. struct on_do_complete_exit; // Helper class to re-post the strand on exit. struct on_dispatch_exit; public: // The underlying implementation of a strand. class strand_impl : public operation { public: strand_impl(); private: // Only this service will have access to the internal values. friend class strand_service; friend struct on_do_complete_exit; friend struct on_dispatch_exit; // Mutex to protect access to internal data. asio::detail::mutex mutex_; // The count of handlers in the strand, including the upcall (if any). std::size_t count_; // The handlers waiting on the strand. op_queue queue_; }; typedef strand_impl* implementation_type; // Construct a new strand service for the specified io_service. ASIO_DECL explicit strand_service(asio::io_service& io_service); // Destroy all user-defined handler objects owned by the service. ASIO_DECL void shutdown_service(); // Construct a new strand implementation. ASIO_DECL void construct(implementation_type& impl); // Destroy a strand implementation. void destroy(implementation_type& impl); // Request the io_service to invoke the given handler. template void dispatch(implementation_type& impl, Handler handler); // Request the io_service to invoke the given handler and return immediately. template void post(implementation_type& impl, Handler handler); private: ASIO_DECL static void do_complete(io_service_impl* owner, operation* base, asio::error_code ec, std::size_t bytes_transferred); // The io_service implementation used to post completions. io_service_impl& io_service_; // Mutex to protect access to the array of implementations. asio::detail::mutex mutex_; // Number of implementations shared between all strand objects. enum { num_implementations = 193 }; // The head of a linked list of all implementations. boost::scoped_ptr implementations_[num_implementations]; // Extra value used when hashing to prevent recycled memory locations from // getting the same strand implementation. std::size_t salt_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #include "asio/detail/impl/strand_service.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/strand_service.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // ASIO_DETAIL_STRAND_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/detail/task_io_service.hpp0000644000000000000000000001220412247075736025663 0ustar rootroot00000000000000// // detail/task_io_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_TASK_IO_SERVICE_HPP #define ASIO_DETAIL_TASK_IO_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(ASIO_HAS_IOCP) #include #include "asio/error_code.hpp" #include "asio/io_service.hpp" #include "asio/detail/mutex.hpp" #include "asio/detail/op_queue.hpp" #include "asio/detail/reactor_fwd.hpp" #include "asio/detail/task_io_service_fwd.hpp" #include "asio/detail/task_io_service_operation.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class task_io_service : public asio::detail::service_base { public: typedef task_io_service_operation operation; // Constructor. ASIO_DECL task_io_service(asio::io_service& io_service); // How many concurrent threads are likely to run the io_service. ASIO_DECL void init(std::size_t concurrency_hint); // Destroy all user-defined handler objects owned by the service. ASIO_DECL void shutdown_service(); // Initialise the task, if required. ASIO_DECL void init_task(); // Run the event loop until interrupted or no more work. ASIO_DECL std::size_t run(asio::error_code& ec); // Run until interrupted or one operation is performed. ASIO_DECL std::size_t run_one(asio::error_code& ec); // Poll for operations without blocking. ASIO_DECL std::size_t poll(asio::error_code& ec); // Poll for one operation without blocking. ASIO_DECL std::size_t poll_one(asio::error_code& ec); // Interrupt the event processing loop. ASIO_DECL void stop(); // Reset in preparation for a subsequent run invocation. ASIO_DECL void reset(); // Notify that some work has started. void work_started() { ++outstanding_work_; } // Notify that some work has finished. void work_finished() { if (--outstanding_work_ == 0) stop(); } // Request invocation of the given handler. template void dispatch(Handler handler); // Request invocation of the given handler and return immediately. template void post(Handler handler); // Request invocation of the given operation and return immediately. Assumes // that work_started() has not yet been called for the operation. ASIO_DECL void post_immediate_completion(operation* op); // Request invocation of the given operation and return immediately. Assumes // that work_started() was previously called for the operation. ASIO_DECL void post_deferred_completion(operation* op); // Request invocation of the given operations and return immediately. Assumes // that work_started() was previously called for each operation. ASIO_DECL void post_deferred_completions(op_queue& ops); private: // Structure containing information about an idle thread. struct idle_thread_info; // Run at most one operation. Blocks only if this_idle_thread is non-null. ASIO_DECL std::size_t do_one(mutex::scoped_lock& lock, idle_thread_info* this_idle_thread); // Stop the task and all idle threads. ASIO_DECL void stop_all_threads(mutex::scoped_lock& lock); // Wakes a single idle thread and unlocks the mutex. Returns true if an idle // thread was found. If there is no idle thread, returns false and leaves the // mutex locked. ASIO_DECL bool wake_one_idle_thread_and_unlock( mutex::scoped_lock& lock); // Wake a single idle thread, or the task, and always unlock the mutex. ASIO_DECL void wake_one_thread_and_unlock( mutex::scoped_lock& lock); // Helper class to perform task-related operations on block exit. struct task_cleanup; friend struct task_cleanup; // Helper class to call work_finished() on block exit. struct work_finished_on_block_exit; // Mutex to protect access to internal data. mutex mutex_; // The task to be run by this service. reactor* task_; // Operation object to represent the position of the task in the queue. struct task_operation : operation { task_operation() : operation(0) {} } task_operation_; // Whether the task has been interrupted. bool task_interrupted_; // The count of unfinished work. boost::detail::atomic_count outstanding_work_; // The queue of handlers that are ready to be delivered. op_queue op_queue_; // Flag to indicate that the dispatcher has been stopped. bool stopped_; // Flag to indicate that the dispatcher has been shut down. bool shutdown_; // The threads that are currently idle. idle_thread_info* first_idle_thread_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #include "asio/detail/impl/task_io_service.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/task_io_service.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // !defined(ASIO_HAS_IOCP) #endif // ASIO_DETAIL_TASK_IO_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/detail/task_io_service_fwd.hpp0000644000000000000000000000122512247075736026524 0ustar rootroot00000000000000// // detail/task_io_service_fwd.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_TASK_IO_SERVICE_FWD_HPP #define ASIO_DETAIL_TASK_IO_SERVICE_FWD_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) namespace asio { namespace detail { class task_io_service; } // namespace detail } // namespace asio #endif // ASIO_DETAIL_TASK_IO_SERVICE_FWD_HPP percona-xtradb-cluster-galera/asio/asio/detail/task_io_service_operation.hpp0000644000000000000000000000307512247075736027751 0ustar rootroot00000000000000// // detail/task_io_service_operation.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_TASK_IO_SERVICE_OPERATION_HPP #define ASIO_DETAIL_TASK_IO_SERVICE_OPERATION_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/error_code.hpp" #include "asio/detail/op_queue.hpp" #include "asio/detail/task_io_service_fwd.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { // Base class for all operations. A function pointer is used instead of virtual // functions to avoid the associated overhead. class task_io_service_operation { public: void complete(task_io_service& owner) { func_(&owner, this, asio::error_code(), 0); } void destroy() { func_(0, this, asio::error_code(), 0); } protected: typedef void (*func_type)(task_io_service*, task_io_service_operation*, asio::error_code, std::size_t); task_io_service_operation(func_type func) : next_(0), func_(func) { } // Prevents deletion through this type. ~task_io_service_operation() { } private: friend class op_queue_access; task_io_service_operation* next_; func_type func_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_TASK_IO_SERVICE_OPERATION_HPP percona-xtradb-cluster-galera/asio/asio/detail/thread.hpp0000644000000000000000000000240312247075736023761 0ustar rootroot00000000000000// // detail/thread.hpp // ~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_THREAD_HPP #define ASIO_DETAIL_THREAD_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) # include "asio/detail/null_thread.hpp" #elif defined(BOOST_WINDOWS) # if defined(UNDER_CE) # include "asio/detail/wince_thread.hpp" # else # include "asio/detail/win_thread.hpp" # endif #elif defined(BOOST_HAS_PTHREADS) # include "asio/detail/posix_thread.hpp" #else # error Only Windows and POSIX are supported! #endif namespace asio { namespace detail { #if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) typedef null_thread thread; #elif defined(BOOST_WINDOWS) # if defined(UNDER_CE) typedef wince_thread thread; # else typedef win_thread thread; # endif #elif defined(BOOST_HAS_PTHREADS) typedef posix_thread thread; #endif } // namespace detail } // namespace asio #endif // ASIO_DETAIL_THREAD_HPP percona-xtradb-cluster-galera/asio/asio/detail/throw_error.hpp0000644000000000000000000000231412247075736025067 0ustar rootroot00000000000000// // detail/throw_error.hpp // ~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_THROW_ERROR_HPP #define ASIO_DETAIL_THROW_ERROR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/error_code.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { ASIO_DECL void do_throw_error(const asio::error_code& err); ASIO_DECL void do_throw_error(const asio::error_code& err, const char* location); inline void throw_error(const asio::error_code& err) { if (err) do_throw_error(err); } inline void throw_error(const asio::error_code& err, const char* location) { if (err) do_throw_error(err, location); } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/throw_error.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // ASIO_DETAIL_THROW_ERROR_HPP percona-xtradb-cluster-galera/asio/asio/detail/timer_op.hpp0000644000000000000000000000165212247075736024335 0ustar rootroot00000000000000// // detail/timer_op.hpp // ~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_TIMER_OP_HPP #define ASIO_DETAIL_TIMER_OP_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/detail/operation.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class timer_op : public operation { public: // The error code to be passed to the completion handler. asio::error_code ec_; protected: timer_op(func_type func) : operation(func) { } }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_TIMER_OP_HPP percona-xtradb-cluster-galera/asio/asio/detail/timer_queue.hpp0000644000000000000000000002453612247075736025051 0ustar rootroot00000000000000// // detail/timer_queue.hpp // ~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_TIMER_QUEUE_HPP #define ASIO_DETAIL_TIMER_QUEUE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include #include #include "asio/detail/op_queue.hpp" #include "asio/detail/timer_op.hpp" #include "asio/detail/timer_queue_base.hpp" #include "asio/error.hpp" #include "asio/time_traits.hpp" #include "asio/detail/push_options.hpp" #include #include "asio/detail/pop_options.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class timer_queue : public timer_queue_base { public: // The time type. typedef typename Time_Traits::time_type time_type; // The duration type. typedef typename Time_Traits::duration_type duration_type; // Per-timer data. class per_timer_data { public: per_timer_data() : next_(0), prev_(0) {} private: friend class timer_queue; // The operations waiting on the timer. op_queue op_queue_; // The index of the timer in the heap. std::size_t heap_index_; // Pointers to adjacent timers in a linked list. per_timer_data* next_; per_timer_data* prev_; }; // Constructor. timer_queue() : timers_(), heap_() { } // Add a new timer to the queue. Returns true if this is the timer that is // earliest in the queue, in which case the reactor's event demultiplexing // function call may need to be interrupted and restarted. bool enqueue_timer(const time_type& time, per_timer_data& timer, timer_op* op) { // Enqueue the timer object. if (timer.prev_ == 0 && &timer != timers_) { if (this->is_positive_infinity(time)) { // No heap entry is required for timers that never expire. timer.heap_index_ = (std::numeric_limits::max)(); } else { // Put the new timer at the correct position in the heap. This is done // first since push_back() can throw due to allocation failure. timer.heap_index_ = heap_.size(); heap_entry entry = { time, &timer }; heap_.push_back(entry); up_heap(heap_.size() - 1); } // Insert the new timer into the linked list of active timers. timer.next_ = timers_; timer.prev_ = 0; if (timers_) timers_->prev_ = &timer; timers_ = &timer; } // Enqueue the individual timer operation. timer.op_queue_.push(op); // Interrupt reactor only if newly added timer is first to expire. return timer.heap_index_ == 0 && timer.op_queue_.front() == op; } // Whether there are no timers in the queue. virtual bool empty() const { return timers_ == 0; } // Get the time for the timer that is earliest in the queue. virtual long wait_duration_msec(long max_duration) const { if (heap_.empty()) return max_duration; boost::posix_time::time_duration duration = Time_Traits::to_posix_duration( Time_Traits::subtract(heap_[0].time_, Time_Traits::now())); if (duration > boost::posix_time::milliseconds(max_duration)) duration = boost::posix_time::milliseconds(max_duration); else if (duration <= boost::posix_time::milliseconds(0)) duration = boost::posix_time::milliseconds(0); else if (duration < boost::posix_time::milliseconds(1)) duration = boost::posix_time::milliseconds(1); return duration.total_milliseconds(); } // Get the time for the timer that is earliest in the queue. virtual long wait_duration_usec(long max_duration) const { if (heap_.empty()) return max_duration; boost::posix_time::time_duration duration = Time_Traits::to_posix_duration( Time_Traits::subtract(heap_[0].time_, Time_Traits::now())); if (duration > boost::posix_time::microseconds(max_duration)) duration = boost::posix_time::microseconds(max_duration); else if (duration <= boost::posix_time::microseconds(0)) duration = boost::posix_time::microseconds(0); else if (duration < boost::posix_time::microseconds(1)) duration = boost::posix_time::microseconds(1); return duration.total_microseconds(); } // Dequeue all timers not later than the current time. virtual void get_ready_timers(op_queue& ops) { const time_type now = Time_Traits::now(); while (!heap_.empty() && !Time_Traits::less_than(now, heap_[0].time_)) { per_timer_data* timer = heap_[0].timer_; ops.push(timer->op_queue_); remove_timer(*timer); } } // Dequeue all timers. virtual void get_all_timers(op_queue& ops) { while (timers_) { per_timer_data* timer = timers_; timers_ = timers_->next_; ops.push(timer->op_queue_); timer->next_ = 0; timer->prev_ = 0; } heap_.clear(); } // Cancel and dequeue the timers with the given token. std::size_t cancel_timer(per_timer_data& timer, op_queue& ops) { std::size_t num_cancelled = 0; if (timer.prev_ != 0 || &timer == timers_) { while (timer_op* op = timer.op_queue_.front()) { op->ec_ = asio::error::operation_aborted; timer.op_queue_.pop(); ops.push(op); ++num_cancelled; } remove_timer(timer); } return num_cancelled; } private: // Move the item at the given index up the heap to its correct position. void up_heap(std::size_t index) { std::size_t parent = (index - 1) / 2; while (index > 0 && Time_Traits::less_than(heap_[index].time_, heap_[parent].time_)) { swap_heap(index, parent); index = parent; parent = (index - 1) / 2; } } // Move the item at the given index down the heap to its correct position. void down_heap(std::size_t index) { std::size_t child = index * 2 + 1; while (child < heap_.size()) { std::size_t min_child = (child + 1 == heap_.size() || Time_Traits::less_than( heap_[child].time_, heap_[child + 1].time_)) ? child : child + 1; if (Time_Traits::less_than(heap_[index].time_, heap_[min_child].time_)) break; swap_heap(index, min_child); index = min_child; child = index * 2 + 1; } } // Swap two entries in the heap. void swap_heap(std::size_t index1, std::size_t index2) { heap_entry tmp = heap_[index1]; heap_[index1] = heap_[index2]; heap_[index2] = tmp; heap_[index1].timer_->heap_index_ = index1; heap_[index2].timer_->heap_index_ = index2; } // Remove a timer from the heap and list of timers. void remove_timer(per_timer_data& timer) { // Remove the timer from the heap. std::size_t index = timer.heap_index_; if (!heap_.empty() && index < heap_.size()) { if (index == heap_.size() - 1) { heap_.pop_back(); } else { swap_heap(index, heap_.size() - 1); heap_.pop_back(); std::size_t parent = (index - 1) / 2; if (index > 0 && Time_Traits::less_than( heap_[index].time_, heap_[parent].time_)) up_heap(index); else down_heap(index); } } // Remove the timer from the linked list of active timers. if (timers_ == &timer) timers_ = timer.next_; if (timer.prev_) timer.prev_->next_ = timer.next_; if (timer.next_) timer.next_->prev_= timer.prev_; timer.next_ = 0; timer.prev_ = 0; } // Determine if the specified absolute time is positive infinity. template static bool is_positive_infinity(const Time_Type&) { return false; } // Determine if the specified absolute time is positive infinity. static bool is_positive_infinity(const boost::posix_time::ptime& time) { return time == boost::posix_time::pos_infin; } // The head of a linked list of all active timers. per_timer_data* timers_; struct heap_entry { // The time when the timer should fire. time_type time_; // The associated timer with enqueued operations. per_timer_data* timer_; }; // The heap of timers, with the earliest timer at the front. std::vector heap_; }; #if !defined(ASIO_HEADER_ONLY) struct forwarding_posix_time_traits : time_traits {}; // Template specialisation for the commonly used instantation. template <> class timer_queue > : public timer_queue_base { public: // The time type. typedef boost::posix_time::ptime time_type; // The duration type. typedef boost::posix_time::time_duration duration_type; // Per-timer data. typedef timer_queue::per_timer_data per_timer_data; // Constructor. ASIO_DECL timer_queue(); // Destructor. ASIO_DECL virtual ~timer_queue(); // Add a new timer to the queue. Returns true if this is the timer that is // earliest in the queue, in which case the reactor's event demultiplexing // function call may need to be interrupted and restarted. ASIO_DECL bool enqueue_timer(const time_type& time, per_timer_data& timer, timer_op* op); // Whether there are no timers in the queue. ASIO_DECL virtual bool empty() const; // Get the time for the timer that is earliest in the queue. ASIO_DECL virtual long wait_duration_msec(long max_duration) const; // Get the time for the timer that is earliest in the queue. ASIO_DECL virtual long wait_duration_usec(long max_duration) const; // Dequeue all timers not later than the current time. ASIO_DECL virtual void get_ready_timers(op_queue& ops); // Dequeue all timers. ASIO_DECL virtual void get_all_timers(op_queue& ops); // Cancel and dequeue the timers with the given token. ASIO_DECL std::size_t cancel_timer( per_timer_data& timer, op_queue& ops); private: timer_queue impl_; }; #endif // !defined(ASIO_HEADER_ONLY) } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_TIMER_QUEUE_HPP percona-xtradb-cluster-galera/asio/asio/detail/timer_queue_base.hpp0000644000000000000000000000311112247075736026025 0ustar rootroot00000000000000// // detail/timer_queue_base.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_TIMER_QUEUE_BASE_HPP #define ASIO_DETAIL_TIMER_QUEUE_BASE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/detail/noncopyable.hpp" #include "asio/detail/op_queue.hpp" #include "asio/detail/operation.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class timer_queue_base : private noncopyable { public: // Constructor. timer_queue_base() : next_(0) {} // Destructor. virtual ~timer_queue_base() {} // Whether there are no timers in the queue. virtual bool empty() const = 0; // Get the time to wait until the next timer. virtual long wait_duration_msec(long max_duration) const = 0; // Get the time to wait until the next timer. virtual long wait_duration_usec(long max_duration) const = 0; // Dequeue all ready timers. virtual void get_ready_timers(op_queue& ops) = 0; // Dequeue all timers. virtual void get_all_timers(op_queue& ops) = 0; private: friend class timer_queue_set; // Next timer queue in the set. timer_queue_base* next_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_TIMER_QUEUE_BASE_HPP percona-xtradb-cluster-galera/asio/asio/detail/timer_queue_fwd.hpp0000644000000000000000000000123512247075736025700 0ustar rootroot00000000000000// // detail/timer_queue_fwd.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_TIMER_QUEUE_FWD_HPP #define ASIO_DETAIL_TIMER_QUEUE_FWD_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) namespace asio { namespace detail { template class timer_queue; } // namespace detail } // namespace asio #endif // ASIO_DETAIL_TIMER_QUEUE_FWD_HPP percona-xtradb-cluster-galera/asio/asio/detail/timer_queue_set.hpp0000644000000000000000000000317212247075736025715 0ustar rootroot00000000000000// // detail/timer_queue_set.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_TIMER_QUEUE_SET_HPP #define ASIO_DETAIL_TIMER_QUEUE_SET_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/detail/timer_queue_base.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class timer_queue_set { public: // Constructor. ASIO_DECL timer_queue_set(); // Add a timer queue to the set. ASIO_DECL void insert(timer_queue_base* q); // Remove a timer queue from the set. ASIO_DECL void erase(timer_queue_base* q); // Determine whether all queues are empty. ASIO_DECL bool all_empty() const; // Get the wait duration in milliseconds. ASIO_DECL long wait_duration_msec(long max_duration) const; // Get the wait duration in microseconds. ASIO_DECL long wait_duration_usec(long max_duration) const; // Dequeue all ready timers. ASIO_DECL void get_ready_timers(op_queue& ops); // Dequeue all timers. ASIO_DECL void get_all_timers(op_queue& ops); private: timer_queue_base* first_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/timer_queue_set.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // ASIO_DETAIL_TIMER_QUEUE_SET_HPP percona-xtradb-cluster-galera/asio/asio/detail/timer_scheduler.hpp0000644000000000000000000000171712247075736025677 0ustar rootroot00000000000000// // detail/timer_scheduler.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_TIMER_SCHEDULER_HPP #define ASIO_DETAIL_TIMER_SCHEDULER_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/detail/timer_scheduler_fwd.hpp" #if defined(ASIO_HAS_IOCP) # include "asio/detail/win_iocp_io_service.hpp" #elif defined(ASIO_HAS_EPOLL) # include "asio/detail/epoll_reactor.hpp" #elif defined(ASIO_HAS_KQUEUE) # include "asio/detail/kqueue_reactor.hpp" #elif defined(ASIO_HAS_DEV_POLL) # include "asio/detail/dev_poll_reactor.hpp" #else # include "asio/detail/select_reactor.hpp" #endif #endif // ASIO_DETAIL_TIMER_SCHEDULER_HPP percona-xtradb-cluster-galera/asio/asio/detail/timer_scheduler_fwd.hpp0000644000000000000000000000255512247075736026540 0ustar rootroot00000000000000// // detail/timer_scheduler_fwd.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_TIMER_SCHEDULER_FWD_HPP #define ASIO_DETAIL_TIMER_SCHEDULER_FWD_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) # include "asio/detail/win_iocp_io_service_fwd.hpp" #elif defined(ASIO_HAS_EPOLL) # include "asio/detail/epoll_reactor_fwd.hpp" #elif defined(ASIO_HAS_KQUEUE) # include "asio/detail/kqueue_reactor_fwd.hpp" #elif defined(ASIO_HAS_DEV_POLL) # include "asio/detail/dev_poll_reactor_fwd.hpp" #else # include "asio/detail/select_reactor_fwd.hpp" #endif namespace asio { namespace detail { #if defined(ASIO_HAS_IOCP) typedef win_iocp_io_service timer_scheduler; #elif defined(ASIO_HAS_EPOLL) typedef epoll_reactor timer_scheduler; #elif defined(ASIO_HAS_KQUEUE) typedef kqueue_reactor timer_scheduler; #elif defined(ASIO_HAS_DEV_POLL) typedef dev_poll_reactor timer_scheduler; #else typedef select_reactor timer_scheduler; #endif } // namespace detail } // namespace asio #endif // ASIO_DETAIL_TIMER_SCHEDULER_FWD_HPP percona-xtradb-cluster-galera/asio/asio/detail/tss_ptr.hpp0000644000000000000000000000302512247075736024211 0ustar rootroot00000000000000// // detail/tss_ptr.hpp // ~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_TSS_PTR_HPP #define ASIO_DETAIL_TSS_PTR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) # include "asio/detail/null_tss_ptr.hpp" #elif defined(BOOST_WINDOWS) # include "asio/detail/win_tss_ptr.hpp" #elif defined(BOOST_HAS_PTHREADS) # include "asio/detail/posix_tss_ptr.hpp" #else # error Only Windows and POSIX are supported! #endif #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class tss_ptr #if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) : public null_tss_ptr #elif defined(BOOST_WINDOWS) : public win_tss_ptr #elif defined(BOOST_HAS_PTHREADS) : public posix_tss_ptr #endif { public: void operator=(T* value) { #if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) null_tss_ptr::operator=(value); #elif defined(BOOST_WINDOWS) win_tss_ptr::operator=(value); #elif defined(BOOST_HAS_PTHREADS) posix_tss_ptr::operator=(value); #endif } }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_TSS_PTR_HPP percona-xtradb-cluster-galera/asio/asio/detail/wait_handler.hpp0000644000000000000000000000417612247075736025164 0ustar rootroot00000000000000// // detail/wait_handler.hpp // ~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_WAIT_HANDLER_HPP #define ASIO_DETAIL_WAIT_HANDLER_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/timer_op.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class wait_handler : public timer_op { public: ASIO_DEFINE_HANDLER_PTR(wait_handler); wait_handler(Handler h) : timer_op(&wait_handler::do_complete), handler_(h) { } static void do_complete(io_service_impl* owner, operation* base, asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) { // Take ownership of the handler object. wait_handler* h(static_cast(base)); ptr p = { boost::addressof(h->handler_), h, h }; // Make a copy of the handler so that the memory can be deallocated before // the upcall is made. Even if we're not about to make an upcall, a // sub-object of the handler may be the true owner of the memory associated // with the handler. Consequently, a local copy of the handler is required // to ensure that any owning sub-object remains valid until after we have // deallocated the memory here. detail::binder1 handler(h->handler_, h->ec_); p.h = boost::addressof(handler.handler_); p.reset(); // Make the upcall if required. if (owner) { asio::detail::fenced_block b; asio_handler_invoke_helpers::invoke(handler, handler.handler_); } } private: Handler handler_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_WAIT_HANDLER_HPP percona-xtradb-cluster-galera/asio/asio/detail/weak_ptr.hpp0000644000000000000000000000153012247075736024326 0ustar rootroot00000000000000// // detail/weak_ptr.hpp // ~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_WEAK_PTR_HPP #define ASIO_DETAIL_WEAK_PTR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #if defined(_MSC_VER) && (_MSC_VER >= 1600) # include #else # include #endif namespace asio { namespace detail { #if defined(_MSC_VER) && (_MSC_VER >= 1600) using std::weak_ptr; #else using boost::weak_ptr; #endif } // namespace detail } // namespace asio #endif // ASIO_DETAIL_WEAK_PTR_HPP percona-xtradb-cluster-galera/asio/asio/detail/win_event.hpp0000644000000000000000000000355712247075736024523 0ustar rootroot00000000000000// // detail/win_event.hpp // ~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_WIN_EVENT_HPP #define ASIO_DETAIL_WIN_EVENT_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) #include #include "asio/detail/noncopyable.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class win_event : private noncopyable { public: // Constructor. ASIO_DECL win_event(); // Destructor. ~win_event() { ::CloseHandle(event_); } // Signal the event. template void signal(Lock& lock) { BOOST_ASSERT(lock.locked()); (void)lock; ::SetEvent(event_); } // Signal the event and unlock the mutex. template void signal_and_unlock(Lock& lock) { BOOST_ASSERT(lock.locked()); lock.unlock(); ::SetEvent(event_); } // Reset the event. template void clear(Lock& lock) { BOOST_ASSERT(lock.locked()); (void)lock; ::ResetEvent(event_); } // Wait for the event to become signalled. template void wait(Lock& lock) { BOOST_ASSERT(lock.locked()); lock.unlock(); ::WaitForSingleObject(event_, INFINITE); lock.lock(); } private: HANDLE event_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/win_event.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // defined(BOOST_WINDOWS) #endif // ASIO_DETAIL_WIN_EVENT_HPP percona-xtradb-cluster-galera/asio/asio/detail/win_fd_set_adapter.hpp0000644000000000000000000000402412247075736026334 0ustar rootroot00000000000000// // detail/win_fd_set_adapter.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_WIN_FD_SET_ADAPTER_HPP #define ASIO_DETAIL_WIN_FD_SET_ADAPTER_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) #include "asio/detail/socket_types.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { // Adapts the FD_SET type to meet the Descriptor_Set concept's requirements. class win_fd_set_adapter { public: enum { win_fd_set_size = 1024 }; win_fd_set_adapter() : max_descriptor_(invalid_socket) { fd_set_.fd_count = 0; } bool set(socket_type descriptor) { for (u_int i = 0; i < fd_set_.fd_count; ++i) if (fd_set_.fd_array[i] == descriptor) return true; if (fd_set_.fd_count < win_fd_set_size) { fd_set_.fd_array[fd_set_.fd_count++] = descriptor; return true; } return false; } bool is_set(socket_type descriptor) const { return !!__WSAFDIsSet(descriptor, const_cast(reinterpret_cast(&fd_set_))); } operator fd_set*() { return reinterpret_cast(&fd_set_); } socket_type max_descriptor() const { return max_descriptor_; } private: // This structure is defined to be compatible with the Windows API fd_set // structure, but without being dependent on the value of FD_SETSIZE. struct win_fd_set { u_int fd_count; SOCKET fd_array[win_fd_set_size]; }; win_fd_set fd_set_; socket_type max_descriptor_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) #endif // ASIO_DETAIL_WIN_FD_SET_ADAPTER_HPP percona-xtradb-cluster-galera/asio/asio/detail/win_fenced_block.hpp0000644000000000000000000000340712247075736025772 0ustar rootroot00000000000000// // detail/win_fenced_block.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_WIN_FENCED_BLOCK_HPP #define ASIO_DETAIL_WIN_FENCED_BLOCK_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) && !defined(UNDER_CE) #include "asio/detail/socket_types.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class win_fenced_block : private noncopyable { public: // Constructor. win_fenced_block() { #if defined(__BORLANDC__) LONG barrier = 0; ::InterlockedExchange(&barrier, 1); #elif defined(BOOST_MSVC) && ((BOOST_MSVC < 1400) || !defined(MemoryBarrier)) # if defined(_M_IX86) # pragma warning(push) # pragma warning(disable:4793) LONG barrier; __asm { xchg barrier, eax } # pragma warning(pop) # endif // defined(_M_IX86) #else MemoryBarrier(); #endif } // Destructor. ~win_fenced_block() { #if defined(__BORLANDC__) LONG barrier = 0; ::InterlockedExchange(&barrier, 1); #elif defined(BOOST_MSVC) && ((BOOST_MSVC < 1400) || !defined(MemoryBarrier)) # if defined(_M_IX86) # pragma warning(push) # pragma warning(disable:4793) LONG barrier; __asm { xchg barrier, eax } # pragma warning(pop) # endif // defined(_M_IX86) #else MemoryBarrier(); #endif } }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(BOOST_WINDOWS) && !defined(UNDER_CE) #endif // ASIO_DETAIL_WIN_FENCED_BLOCK_HPP percona-xtradb-cluster-galera/asio/asio/detail/win_iocp_handle_read_op.hpp0000644000000000000000000000613212247075736027330 0ustar rootroot00000000000000// // detail/win_iocp_handle_read_op.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_WIN_IOCP_HANDLE_READ_OP_HPP #define ASIO_DETAIL_WIN_IOCP_HANDLE_READ_OP_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) #include "asio/error.hpp" #include #include "asio/detail/bind_handler.hpp" #include "asio/detail/buffer_sequence_adapter.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/operation.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class win_iocp_handle_read_op : public operation { public: ASIO_DEFINE_HANDLER_PTR(win_iocp_handle_read_op); win_iocp_handle_read_op(const MutableBufferSequence& buffers, Handler handler) : operation(&win_iocp_handle_read_op::do_complete), buffers_(buffers), handler_(handler) { } static void do_complete(io_service_impl* owner, operation* base, asio::error_code ec, std::size_t bytes_transferred) { // Take ownership of the operation object. win_iocp_handle_read_op* o(static_cast(base)); ptr p = { boost::addressof(o->handler_), o, o }; #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) if (owner) { // Check whether buffers are still valid. buffer_sequence_adapter::validate(o->buffers_); } #endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) // Map non-portable errors to their portable counterparts. if (ec.value() == ERROR_HANDLE_EOF) ec = asio::error::eof; // Make a copy of the handler so that the memory can be deallocated before // the upcall is made. Even if we're not about to make an upcall, a // sub-object of the handler may be the true owner of the memory associated // with the handler. Consequently, a local copy of the handler is required // to ensure that any owning sub-object remains valid until after we have // deallocated the memory here. detail::binder2 handler(o->handler_, ec, bytes_transferred); p.h = boost::addressof(handler.handler_); p.reset(); // Make the upcall if required. if (owner) { asio::detail::fenced_block b; asio_handler_invoke_helpers::invoke(handler, handler.handler_); } } private: MutableBufferSequence buffers_; Handler handler_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_IOCP) #endif // ASIO_DETAIL_WIN_IOCP_HANDLE_READ_OP_HPP percona-xtradb-cluster-galera/asio/asio/detail/win_iocp_handle_service.hpp0000644000000000000000000002361512247075736027364 0ustar rootroot00000000000000// // detail/win_iocp_handle_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_WIN_IOCP_HANDLE_SERVICE_HPP #define ASIO_DETAIL_WIN_IOCP_HANDLE_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) #include #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/detail/buffer_sequence_adapter.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/mutex.hpp" #include "asio/detail/operation.hpp" #include "asio/detail/win_iocp_handle_read_op.hpp" #include "asio/detail/win_iocp_handle_write_op.hpp" #include "asio/detail/win_iocp_io_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class win_iocp_handle_service { public: // The native type of a stream handle. typedef HANDLE native_type; // The implementation type of the stream handle. class implementation_type { public: // Default constructor. implementation_type() : handle_(INVALID_HANDLE_VALUE), safe_cancellation_thread_id_(0), next_(0), prev_(0) { } private: // Only this service will have access to the internal values. friend class win_iocp_handle_service; // The native stream handle representation. native_type handle_; // The ID of the thread from which it is safe to cancel asynchronous // operations. 0 means no asynchronous operations have been started yet. // ~0 means asynchronous operations have been started from more than one // thread, and cancellation is not supported for the handle. DWORD safe_cancellation_thread_id_; // Pointers to adjacent handle implementations in linked list. implementation_type* next_; implementation_type* prev_; }; ASIO_DECL win_iocp_handle_service(asio::io_service& io_service); // Destroy all user-defined handler objects owned by the service. ASIO_DECL void shutdown_service(); // Construct a new handle implementation. ASIO_DECL void construct(implementation_type& impl); // Destroy a handle implementation. ASIO_DECL void destroy(implementation_type& impl); // Assign a native handle to a handle implementation. ASIO_DECL asio::error_code assign(implementation_type& impl, const native_type& native_handle, asio::error_code& ec); // Determine whether the handle is open. bool is_open(const implementation_type& impl) const { return impl.handle_ != INVALID_HANDLE_VALUE; } // Destroy a handle implementation. ASIO_DECL asio::error_code close(implementation_type& impl, asio::error_code& ec); // Get the native handle representation. native_type native(const implementation_type& impl) const { return impl.handle_; } // Cancel all operations associated with the handle. ASIO_DECL asio::error_code cancel(implementation_type& impl, asio::error_code& ec); // Write the given data. Returns the number of bytes written. template size_t write_some(implementation_type& impl, const ConstBufferSequence& buffers, asio::error_code& ec) { return write_some_at(impl, 0, buffers, ec); } // Write the given data at the specified offset. Returns the number of bytes // written. template size_t write_some_at(implementation_type& impl, boost::uint64_t offset, const ConstBufferSequence& buffers, asio::error_code& ec) { asio::const_buffer buffer = buffer_sequence_adapter::first(buffers); return do_write(impl, offset, buffer, ec); } // Start an asynchronous write. The data being written must be valid for the // lifetime of the asynchronous operation. template void async_write_some(implementation_type& impl, const ConstBufferSequence& buffers, Handler handler) { async_write_some_at(impl, 0, buffers, handler); } // Start an asynchronous write at a specified offset. The data being written // must be valid for the lifetime of the asynchronous operation. template void async_write_some_at(implementation_type& impl, boost::uint64_t offset, const ConstBufferSequence& buffers, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef win_iocp_handle_write_op op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(buffers, handler); start_write_op(impl, offset, buffer_sequence_adapter::first(buffers), p.p); p.v = p.p = 0; } // Read some data. Returns the number of bytes received. template size_t read_some(implementation_type& impl, const MutableBufferSequence& buffers, asio::error_code& ec) { return read_some_at(impl, 0, buffers, ec); } // Read some data at a specified offset. Returns the number of bytes received. template size_t read_some_at(implementation_type& impl, boost::uint64_t offset, const MutableBufferSequence& buffers, asio::error_code& ec) { asio::mutable_buffer buffer = buffer_sequence_adapter::first(buffers); return do_read(impl, offset, buffer, ec); } // Start an asynchronous read. The buffer for the data being received must be // valid for the lifetime of the asynchronous operation. template void async_read_some(implementation_type& impl, const MutableBufferSequence& buffers, Handler handler) { async_read_some_at(impl, 0, buffers, handler); } // Start an asynchronous read at a specified offset. The buffer for the data // being received must be valid for the lifetime of the asynchronous // operation. template void async_read_some_at(implementation_type& impl, boost::uint64_t offset, const MutableBufferSequence& buffers, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef win_iocp_handle_read_op op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(buffers, handler); start_read_op(impl, offset, buffer_sequence_adapter::first(buffers), p.p); p.v = p.p = 0; } private: // Prevent the use of the null_buffers type with this service. size_t write_some(implementation_type& impl, const null_buffers& buffers, asio::error_code& ec); size_t write_some_at(implementation_type& impl, boost::uint64_t offset, const null_buffers& buffers, asio::error_code& ec); template void async_write_some(implementation_type& impl, const null_buffers& buffers, Handler handler); template void async_write_some_at(implementation_type& impl, boost::uint64_t offset, const null_buffers& buffers, Handler handler); size_t read_some(implementation_type& impl, const null_buffers& buffers, asio::error_code& ec); size_t read_some_at(implementation_type& impl, boost::uint64_t offset, const null_buffers& buffers, asio::error_code& ec); template void async_read_some(implementation_type& impl, const null_buffers& buffers, Handler handler); template void async_read_some_at(implementation_type& impl, boost::uint64_t offset, const null_buffers& buffers, Handler handler); // Helper class for waiting for synchronous operations to complete. class overlapped_wrapper; // Helper function to perform a synchronous write operation. ASIO_DECL size_t do_write(implementation_type& impl, boost::uint64_t offset, const asio::const_buffer& buffer, asio::error_code& ec); // Helper function to start a write operation. ASIO_DECL void start_write_op(implementation_type& impl, boost::uint64_t offset, const asio::const_buffer& buffer, operation* op); // Helper function to perform a synchronous write operation. ASIO_DECL size_t do_read(implementation_type& impl, boost::uint64_t offset, const asio::mutable_buffer& buffer, asio::error_code& ec); // Helper function to start a read operation. ASIO_DECL void start_read_op(implementation_type& impl, boost::uint64_t offset, const asio::mutable_buffer& buffer, operation* op); // Update the ID of the thread from which cancellation is safe. ASIO_DECL void update_cancellation_thread_id(implementation_type& impl); // Helper function to close a handle when the associated object is being // destroyed. ASIO_DECL void close_for_destruction(implementation_type& impl); // The IOCP service used for running asynchronous operations and dispatching // handlers. win_iocp_io_service& iocp_service_; // Mutex to protect access to the linked list of implementations. mutex mutex_; // The head of a linked list of all implementations. implementation_type* impl_list_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/win_iocp_handle_service.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // defined(ASIO_HAS_IOCP) #endif // ASIO_DETAIL_WIN_IOCP_HANDLE_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/detail/win_iocp_handle_write_op.hpp0000644000000000000000000000572612247075736027557 0ustar rootroot00000000000000// // detail/win_iocp_handle_write_op.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_WIN_IOCP_HANDLE_WRITE_OP_HPP #define ASIO_DETAIL_WIN_IOCP_HANDLE_WRITE_OP_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) #include "asio/error.hpp" #include #include "asio/detail/bind_handler.hpp" #include "asio/detail/buffer_sequence_adapter.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/operation.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class win_iocp_handle_write_op : public operation { public: ASIO_DEFINE_HANDLER_PTR(win_iocp_handle_write_op); win_iocp_handle_write_op(const ConstBufferSequence& buffers, Handler handler) : operation(&win_iocp_handle_write_op::do_complete), buffers_(buffers), handler_(handler) { } static void do_complete(io_service_impl* owner, operation* base, asio::error_code ec, std::size_t bytes_transferred) { // Take ownership of the operation object. win_iocp_handle_write_op* o(static_cast(base)); ptr p = { boost::addressof(o->handler_), o, o }; #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) if (owner) { // Check whether buffers are still valid. buffer_sequence_adapter::validate(o->buffers_); } #endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) // Make a copy of the handler so that the memory can be deallocated before // the upcall is made. Even if we're not about to make an upcall, a // sub-object of the handler may be the true owner of the memory associated // with the handler. Consequently, a local copy of the handler is required // to ensure that any owning sub-object remains valid until after we have // deallocated the memory here. detail::binder2 handler(o->handler_, ec, bytes_transferred); p.h = boost::addressof(handler.handler_); p.reset(); // Make the upcall if required. if (owner) { asio::detail::fenced_block b; asio_handler_invoke_helpers::invoke(handler, handler.handler_); } } private: ConstBufferSequence buffers_; Handler handler_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_IOCP) #endif // ASIO_DETAIL_WIN_IOCP_HANDLE_WRITE_OP_HPP percona-xtradb-cluster-galera/asio/asio/detail/win_iocp_io_service.hpp0000644000000000000000000002046312247075736026536 0ustar rootroot00000000000000// // detail/win_iocp_io_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_WIN_IOCP_IO_SERVICE_HPP #define ASIO_DETAIL_WIN_IOCP_IO_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) #include #include "asio/io_service.hpp" #include "asio/detail/mutex.hpp" #include "asio/detail/op_queue.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/timer_op.hpp" #include "asio/detail/timer_queue_base.hpp" #include "asio/detail/timer_queue_fwd.hpp" #include "asio/detail/timer_queue_set.hpp" #include "asio/detail/win_iocp_io_service_fwd.hpp" #include "asio/detail/win_iocp_operation.hpp" #include "asio/detail/thread.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class timer_op; class win_iocp_io_service : public asio::detail::service_base { public: // Constructor. ASIO_DECL win_iocp_io_service(asio::io_service& io_service); ASIO_DECL void init(size_t concurrency_hint); // Destroy all user-defined handler objects owned by the service. ASIO_DECL void shutdown_service(); // Initialise the task. Nothing to do here. void init_task() { } // Register a handle with the IO completion port. ASIO_DECL asio::error_code register_handle( HANDLE handle, asio::error_code& ec); // Run the event loop until stopped or no more work. ASIO_DECL size_t run(asio::error_code& ec); // Run until stopped or one operation is performed. ASIO_DECL size_t run_one(asio::error_code& ec); // Poll for operations without blocking. ASIO_DECL size_t poll(asio::error_code& ec); // Poll for one operation without blocking. ASIO_DECL size_t poll_one(asio::error_code& ec); // Stop the event processing loop. ASIO_DECL void stop(); // Reset in preparation for a subsequent run invocation. void reset() { ::InterlockedExchange(&stopped_, 0); } // Notify that some work has started. void work_started() { ::InterlockedIncrement(&outstanding_work_); } // Notify that some work has finished. void work_finished() { if (::InterlockedDecrement(&outstanding_work_) == 0) stop(); } // Request invocation of the given handler. template void dispatch(Handler handler); // Request invocation of the given handler and return immediately. template void post(Handler handler); // Request invocation of the given operation and return immediately. Assumes // that work_started() has not yet been called for the operation. void post_immediate_completion(win_iocp_operation* op) { work_started(); post_deferred_completion(op); } // Request invocation of the given operation and return immediately. Assumes // that work_started() was previously called for the operation. ASIO_DECL void post_deferred_completion(win_iocp_operation* op); // Request invocation of the given operation and return immediately. Assumes // that work_started() was previously called for the operations. ASIO_DECL void post_deferred_completions( op_queue& ops); // Called after starting an overlapped I/O operation that did not complete // immediately. The caller must have already called work_started() prior to // starting the operation. ASIO_DECL void on_pending(win_iocp_operation* op); // Called after starting an overlapped I/O operation that completed // immediately. The caller must have already called work_started() prior to // starting the operation. ASIO_DECL void on_completion(win_iocp_operation* op, DWORD last_error = 0, DWORD bytes_transferred = 0); // Called after starting an overlapped I/O operation that completed // immediately. The caller must have already called work_started() prior to // starting the operation. ASIO_DECL void on_completion(win_iocp_operation* op, const asio::error_code& ec, DWORD bytes_transferred = 0); // Add a new timer queue to the service. template void add_timer_queue(timer_queue& timer_queue); // Remove a timer queue from the service. template void remove_timer_queue(timer_queue& timer_queue); // Schedule a new operation in the given timer queue to expire at the // specified absolute time. template void schedule_timer(timer_queue& queue, const typename Time_Traits::time_type& time, typename timer_queue::per_timer_data& timer, timer_op* op); // Cancel the timer associated with the given token. Returns the number of // handlers that have been posted or dispatched. template std::size_t cancel_timer(timer_queue& queue, typename timer_queue::per_timer_data& timer); private: #if defined(WINVER) && (WINVER < 0x0500) typedef DWORD dword_ptr_t; typedef ULONG ulong_ptr_t; #else // defined(WINVER) && (WINVER < 0x0500) typedef DWORD_PTR dword_ptr_t; typedef ULONG_PTR ulong_ptr_t; #endif // defined(WINVER) && (WINVER < 0x0500) // Dequeues at most one operation from the I/O completion port, and then // executes it. Returns the number of operations that were dequeued (i.e. // either 0 or 1). ASIO_DECL size_t do_one(bool block, asio::error_code& ec); // Helper function to add a new timer queue. ASIO_DECL void do_add_timer_queue(timer_queue_base& queue); // Helper function to remove a timer queue. ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue); // Called to recalculate and update the timeout. ASIO_DECL void update_timeout(); // Helper class to call work_finished() on block exit. struct work_finished_on_block_exit; // Helper class for managing a HANDLE. struct auto_handle { HANDLE handle; auto_handle() : handle(0) {} ~auto_handle() { if (handle) ::CloseHandle(handle); } }; // The IO completion port used for queueing operations. auto_handle iocp_; // The count of unfinished work. long outstanding_work_; // Flag to indicate whether the event loop has been stopped. long stopped_; // Flag to indicate whether the service has been shut down. long shutdown_; enum { // Timeout to use with GetQueuedCompletionStatus. Some versions of windows // have a "bug" where a call to GetQueuedCompletionStatus can appear stuck // even though there are events waiting on the queue. Using a timeout helps // to work around the issue. gqcs_timeout = 500, // Maximum waitable timer timeout, in milliseconds. max_timeout_msec = 5 * 60 * 1000, // Maximum waitable timer timeout, in microseconds. max_timeout_usec = max_timeout_msec * 1000, // Completion key value used to wake up a thread to dispatch timers or // completed operations. wake_for_dispatch = 1, // Completion key value to indicate that an operation has posted with the // original last_error and bytes_transferred values stored in the fields of // the OVERLAPPED structure. overlapped_contains_result = 2 }; // Function object for processing timeouts in a background thread. struct timer_thread_function; friend struct timer_thread_function; // Background thread used for processing timeouts. boost::scoped_ptr timer_thread_; // A waitable timer object used for waiting for timeouts. auto_handle waitable_timer_; // Non-zero if timers or completed operations need to be dispatched. long dispatch_required_; // Mutex for protecting access to the timer queues and completed operations. mutex dispatch_mutex_; // The timer queues. timer_queue_set timer_queues_; // The operations that are ready to dispatch. op_queue completed_ops_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #include "asio/detail/impl/win_iocp_io_service.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/win_iocp_io_service.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // defined(ASIO_HAS_IOCP) #endif // ASIO_DETAIL_WIN_IOCP_IO_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/detail/win_iocp_io_service_fwd.hpp0000644000000000000000000000145512247075736027376 0ustar rootroot00000000000000// // detail/win_iocp_io_service_fwd.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_WIN_IOCP_IO_SERVICE_FWD_HPP #define ASIO_DETAIL_WIN_IOCP_IO_SERVICE_FWD_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) namespace asio { namespace detail { class win_iocp_io_service; class win_iocp_overlapped_ptr; } // namespace detail } // namespace asio #endif // defined(ASIO_HAS_IOCP) #endif // ASIO_DETAIL_WIN_IOCP_IO_SERVICE_FWD_HPP percona-xtradb-cluster-galera/asio/asio/detail/win_iocp_null_buffers_op.hpp0000644000000000000000000000637712247075736027603 0ustar rootroot00000000000000// // detail/win_iocp_null_buffers_op.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_WIN_IOCP_NULL_BUFFERS_OP_HPP #define ASIO_DETAIL_WIN_IOCP_NULL_BUFFERS_OP_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) #include #include "asio/detail/bind_handler.hpp" #include "asio/detail/buffer_sequence_adapter.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/reactor_op.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class win_iocp_null_buffers_op : public reactor_op { public: ASIO_DEFINE_HANDLER_PTR(win_iocp_null_buffers_op); win_iocp_null_buffers_op(socket_ops::weak_cancel_token_type cancel_token, Handler handler) : reactor_op(&win_iocp_null_buffers_op::do_perform, &win_iocp_null_buffers_op::do_complete), cancel_token_(cancel_token), handler_(handler) { } static bool do_perform(reactor_op*) { return true; } static void do_complete(io_service_impl* owner, operation* base, asio::error_code ec, std::size_t bytes_transferred) { // Take ownership of the operation object. win_iocp_null_buffers_op* o(static_cast(base)); ptr p = { boost::addressof(o->handler_), o, o }; // The reactor may have stored a result in the operation object. if (o->ec_) ec = o->ec_; // Map non-portable errors to their portable counterparts. if (ec.value() == ERROR_NETNAME_DELETED) { if (o->cancel_token_.expired()) ec = asio::error::operation_aborted; else ec = asio::error::connection_reset; } else if (ec.value() == ERROR_PORT_UNREACHABLE) { ec = asio::error::connection_refused; } // Make a copy of the handler so that the memory can be deallocated before // the upcall is made. Even if we're not about to make an upcall, a // sub-object of the handler may be the true owner of the memory associated // with the handler. Consequently, a local copy of the handler is required // to ensure that any owning sub-object remains valid until after we have // deallocated the memory here. detail::binder2 handler(o->handler_, ec, bytes_transferred); p.h = boost::addressof(handler.handler_); p.reset(); // Make the upcall if required. if (owner) { asio::detail::fenced_block b; asio_handler_invoke_helpers::invoke(handler, handler.handler_); } } private: socket_ops::weak_cancel_token_type cancel_token_; Handler handler_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_IOCP) #endif // ASIO_DETAIL_WIN_IOCP_NULL_BUFFERS_OP_HPP percona-xtradb-cluster-galera/asio/asio/detail/win_iocp_operation.hpp0000644000000000000000000000363012247075736026404 0ustar rootroot00000000000000// // detail/win_iocp_operation.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_WIN_IOCP_OPERATION_HPP #define ASIO_DETAIL_WIN_IOCP_OPERATION_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) #include "asio/detail/op_queue.hpp" #include "asio/detail/win_iocp_io_service_fwd.hpp" #include "asio/error_code.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { // Base class for all operations. A function pointer is used instead of virtual // functions to avoid the associated overhead. class win_iocp_operation : public OVERLAPPED { public: void complete(win_iocp_io_service& owner, const asio::error_code& ec = asio::error_code(), std::size_t bytes_transferred = 0) { func_(&owner, this, ec, bytes_transferred); } void destroy() { func_(0, this, asio::error_code(), 0); } protected: typedef void (*func_type)(win_iocp_io_service*, win_iocp_operation*, asio::error_code, std::size_t); win_iocp_operation(func_type func) : next_(0), func_(func) { reset(); } // Prevents deletion through this type. ~win_iocp_operation() { } void reset() { Internal = 0; InternalHigh = 0; Offset = 0; OffsetHigh = 0; hEvent = 0; ready_ = 0; } private: friend class op_queue_access; friend class win_iocp_io_service; win_iocp_operation* next_; func_type func_; long ready_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_IOCP) #endif // ASIO_DETAIL_WIN_IOCP_OPERATION_HPP percona-xtradb-cluster-galera/asio/asio/detail/win_iocp_overlapped_op.hpp0000644000000000000000000000467112247075736027251 0ustar rootroot00000000000000// // detail/win_iocp_overlapped_op.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_WIN_IOCP_OVERLAPPED_OP_HPP #define ASIO_DETAIL_WIN_IOCP_OVERLAPPED_OP_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) #include "asio/error.hpp" #include #include "asio/detail/bind_handler.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/operation.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class win_iocp_overlapped_op : public operation { public: ASIO_DEFINE_HANDLER_PTR(win_iocp_overlapped_op); win_iocp_overlapped_op(Handler handler) : operation(&win_iocp_overlapped_op::do_complete), handler_(handler) { } static void do_complete(io_service_impl* owner, operation* base, asio::error_code ec, std::size_t bytes_transferred) { // Take ownership of the operation object. win_iocp_overlapped_op* o(static_cast(base)); ptr p = { boost::addressof(o->handler_), o, o }; // Make a copy of the handler so that the memory can be deallocated before // the upcall is made. Even if we're not about to make an upcall, a // sub-object of the handler may be the true owner of the memory associated // with the handler. Consequently, a local copy of the handler is required // to ensure that any owning sub-object remains valid until after we have // deallocated the memory here. detail::binder2 handler(o->handler_, ec, bytes_transferred); p.h = boost::addressof(handler.handler_); p.reset(); // Make the upcall if required. if (owner) { asio::detail::fenced_block b; asio_handler_invoke_helpers::invoke(handler, handler.handler_); } } private: Handler handler_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_IOCP) #endif // ASIO_DETAIL_WIN_IOCP_OVERLAPPED_OP_HPP percona-xtradb-cluster-galera/asio/asio/detail/win_iocp_overlapped_ptr.hpp0000644000000000000000000000631712247075736027437 0ustar rootroot00000000000000// // detail/win_iocp_overlapped_ptr.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_WIN_IOCP_OVERLAPPED_PTR_HPP #define ASIO_DETAIL_WIN_IOCP_OVERLAPPED_PTR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) #include #include "asio/io_service.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/noncopyable.hpp" #include "asio/detail/win_iocp_overlapped_op.hpp" #include "asio/detail/win_iocp_io_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { // Wraps a handler to create an OVERLAPPED object for use with overlapped I/O. class win_iocp_overlapped_ptr : private noncopyable { public: // Construct an empty win_iocp_overlapped_ptr. win_iocp_overlapped_ptr() : ptr_(0), iocp_service_(0) { } // Construct an win_iocp_overlapped_ptr to contain the specified handler. template explicit win_iocp_overlapped_ptr( asio::io_service& io_service, Handler handler) : ptr_(0), iocp_service_(0) { this->reset(io_service, handler); } // Destructor automatically frees the OVERLAPPED object unless released. ~win_iocp_overlapped_ptr() { reset(); } // Reset to empty. void reset() { if (ptr_) { ptr_->destroy(); ptr_ = 0; iocp_service_->work_finished(); iocp_service_ = 0; } } // Reset to contain the specified handler, freeing any current OVERLAPPED // object. template void reset(asio::io_service& io_service, Handler handler) { typedef win_iocp_overlapped_op op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(handler); io_service.impl_.work_started(); reset(); ptr_ = p.p; p.v = p.p = 0; iocp_service_ = &io_service.impl_; } // Get the contained OVERLAPPED object. OVERLAPPED* get() { return ptr_; } // Get the contained OVERLAPPED object. const OVERLAPPED* get() const { return ptr_; } // Release ownership of the OVERLAPPED object. OVERLAPPED* release() { if (ptr_) iocp_service_->on_pending(ptr_); OVERLAPPED* tmp = ptr_; ptr_ = 0; iocp_service_ = 0; return tmp; } // Post completion notification for overlapped operation. Releases ownership. void complete(const asio::error_code& ec, std::size_t bytes_transferred) { if (ptr_) { iocp_service_->on_completion(ptr_, ec, static_cast(bytes_transferred)); ptr_ = 0; iocp_service_ = 0; } } private: win_iocp_operation* ptr_; win_iocp_io_service* iocp_service_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_IOCP) #endif // ASIO_DETAIL_WIN_IOCP_OVERLAPPED_PTR_HPP percona-xtradb-cluster-galera/asio/asio/detail/win_iocp_serial_port_service.hpp0000644000000000000000000001526412247075736030455 0ustar rootroot00000000000000// // detail/win_iocp_serial_port_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_WIN_IOCP_SERIAL_PORT_SERVICE_HPP #define ASIO_DETAIL_WIN_IOCP_SERIAL_PORT_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) && defined(ASIO_HAS_SERIAL_PORT) #include #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/detail/win_iocp_handle_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { // Extend win_iocp_handle_service to provide serial port support. class win_iocp_serial_port_service { public: // The native type of a serial port. typedef win_iocp_handle_service::native_type native_type; // The implementation type of the serial port. typedef win_iocp_handle_service::implementation_type implementation_type; // Constructor. ASIO_DECL win_iocp_serial_port_service( asio::io_service& io_service); // Destroy all user-defined handler objects owned by the service. ASIO_DECL void shutdown_service(); // Construct a new serial port implementation. void construct(implementation_type& impl) { handle_service_.construct(impl); } // Destroy a serial port implementation. void destroy(implementation_type& impl) { handle_service_.destroy(impl); } // Open the serial port using the specified device name. ASIO_DECL asio::error_code open(implementation_type& impl, const std::string& device, asio::error_code& ec); // Assign a native handle to a serial port implementation. asio::error_code assign(implementation_type& impl, const native_type& native_handle, asio::error_code& ec) { return handle_service_.assign(impl, native_handle, ec); } // Determine whether the serial port is open. bool is_open(const implementation_type& impl) const { return handle_service_.is_open(impl); } // Destroy a serial port implementation. asio::error_code close(implementation_type& impl, asio::error_code& ec) { return handle_service_.close(impl, ec); } // Get the native serial port representation. native_type native(implementation_type& impl) { return handle_service_.native(impl); } // Cancel all operations associated with the handle. asio::error_code cancel(implementation_type& impl, asio::error_code& ec) { return handle_service_.cancel(impl, ec); } // Set an option on the serial port. template asio::error_code set_option(implementation_type& impl, const SettableSerialPortOption& option, asio::error_code& ec) { return do_set_option(impl, &win_iocp_serial_port_service::store_option, &option, ec); } // Get an option from the serial port. template asio::error_code get_option(const implementation_type& impl, GettableSerialPortOption& option, asio::error_code& ec) const { return do_get_option(impl, &win_iocp_serial_port_service::load_option, &option, ec); } // Send a break sequence to the serial port. asio::error_code send_break(implementation_type&, asio::error_code& ec) { ec = asio::error::operation_not_supported; return ec; } // Write the given data. Returns the number of bytes sent. template size_t write_some(implementation_type& impl, const ConstBufferSequence& buffers, asio::error_code& ec) { return handle_service_.write_some(impl, buffers, ec); } // Start an asynchronous write. The data being written must be valid for the // lifetime of the asynchronous operation. template void async_write_some(implementation_type& impl, const ConstBufferSequence& buffers, Handler handler) { handle_service_.async_write_some(impl, buffers, handler); } // Read some data. Returns the number of bytes received. template size_t read_some(implementation_type& impl, const MutableBufferSequence& buffers, asio::error_code& ec) { return handle_service_.read_some(impl, buffers, ec); } // Start an asynchronous read. The buffer for the data being received must be // valid for the lifetime of the asynchronous operation. template void async_read_some(implementation_type& impl, const MutableBufferSequence& buffers, Handler handler) { handle_service_.async_read_some(impl, buffers, handler); } private: // Function pointer type for storing a serial port option. typedef asio::error_code (*store_function_type)( const void*, ::DCB&, asio::error_code&); // Helper function template to store a serial port option. template static asio::error_code store_option(const void* option, ::DCB& storage, asio::error_code& ec) { return static_cast(option)->store( storage, ec); } // Helper function to set a serial port option. ASIO_DECL asio::error_code do_set_option( implementation_type& impl, store_function_type store, const void* option, asio::error_code& ec); // Function pointer type for loading a serial port option. typedef asio::error_code (*load_function_type)( void*, const ::DCB&, asio::error_code&); // Helper function template to load a serial port option. template static asio::error_code load_option(void* option, const ::DCB& storage, asio::error_code& ec) { return static_cast(option)->load(storage, ec); } // Helper function to get a serial port option. ASIO_DECL asio::error_code do_get_option( const implementation_type& impl, load_function_type load, void* option, asio::error_code& ec) const; // The implementation used for initiating asynchronous operations. win_iocp_handle_service handle_service_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/win_iocp_serial_port_service.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // defined(ASIO_HAS_IOCP) && defined(ASIO_HAS_SERIAL_PORT) #endif // ASIO_DETAIL_WIN_IOCP_SERIAL_PORT_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/detail/win_iocp_socket_accept_op.hpp0000644000000000000000000001142012247075736027705 0ustar rootroot00000000000000// // detail/win_iocp_socket_accept_op.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_ACCEPT_OP_HPP #define ASIO_DETAIL_WIN_IOCP_SOCKET_ACCEPT_OP_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) #include #include "asio/detail/bind_handler.hpp" #include "asio/detail/buffer_sequence_adapter.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/operation.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/detail/win_iocp_socket_service_base.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class win_iocp_socket_accept_op : public operation { public: ASIO_DEFINE_HANDLER_PTR(win_iocp_socket_accept_op); win_iocp_socket_accept_op(win_iocp_socket_service_base& socket_service, socket_type socket, Socket& peer, const Protocol& protocol, typename Protocol::endpoint* peer_endpoint, bool enable_connection_aborted, Handler handler) : operation(&win_iocp_socket_accept_op::do_complete), socket_service_(socket_service), socket_(socket), peer_(peer), protocol_(protocol), peer_endpoint_(peer_endpoint), enable_connection_aborted_(enable_connection_aborted), handler_(handler) { } socket_holder& new_socket() { return new_socket_; } void* output_buffer() { return output_buffer_; } DWORD address_length() { return sizeof(sockaddr_storage_type) + 16; } static void do_complete(io_service_impl* owner, operation* base, asio::error_code ec, std::size_t /*bytes_transferred*/) { // Take ownership of the operation object. win_iocp_socket_accept_op* o(static_cast(base)); ptr p = { boost::addressof(o->handler_), o, o }; if (owner) { typename Protocol::endpoint peer_endpoint; std::size_t addr_len = peer_endpoint.capacity(); socket_ops::complete_iocp_accept(o->socket_, o->output_buffer(), o->address_length(), peer_endpoint.data(), &addr_len, o->new_socket_.get(), ec); // Restart the accept operation if we got the connection_aborted error // and the enable_connection_aborted socket option is not set. if (ec == asio::error::connection_aborted && !o->enable_connection_aborted_) { o->reset(); o->socket_service_.restart_accept_op(o->socket_, o->new_socket_, o->protocol_.family(), o->protocol_.type(), o->protocol_.protocol(), o->output_buffer(), o->address_length(), o); p.v = p.p = 0; return; } // If the socket was successfully accepted, transfer ownership of the // socket to the peer object. if (!ec) { o->peer_.assign(o->protocol_, typename Socket::native_type( o->new_socket_.get(), peer_endpoint), ec); if (!ec) o->new_socket_.release(); } // Pass endpoint back to caller. if (o->peer_endpoint_) *o->peer_endpoint_ = peer_endpoint; } // Make a copy of the handler so that the memory can be deallocated before // the upcall is made. Even if we're not about to make an upcall, a // sub-object of the handler may be the true owner of the memory associated // with the handler. Consequently, a local copy of the handler is required // to ensure that any owning sub-object remains valid until after we have // deallocated the memory here. detail::binder1 handler(o->handler_, ec); p.h = boost::addressof(handler.handler_); p.reset(); // Make the upcall if required. if (owner) { asio::detail::fenced_block b; asio_handler_invoke_helpers::invoke(handler, handler.handler_); } } private: win_iocp_socket_service_base& socket_service_; socket_type socket_; socket_holder new_socket_; Socket& peer_; Protocol protocol_; typename Protocol::endpoint* peer_endpoint_; unsigned char output_buffer_[(sizeof(sockaddr_storage_type) + 16) * 2]; bool enable_connection_aborted_; Handler handler_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_IOCP) #endif // ASIO_DETAIL_WIN_IOCP_SOCKET_ACCEPT_OP_HPP percona-xtradb-cluster-galera/asio/asio/detail/win_iocp_socket_recv_op.hpp0000644000000000000000000000655012247075736027415 0ustar rootroot00000000000000// // detail/win_iocp_socket_recv_op.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_RECV_OP_HPP #define ASIO_DETAIL_WIN_IOCP_SOCKET_RECV_OP_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) #include #include "asio/detail/bind_handler.hpp" #include "asio/detail/buffer_sequence_adapter.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/operation.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class win_iocp_socket_recv_op : public operation { public: ASIO_DEFINE_HANDLER_PTR(win_iocp_socket_recv_op); win_iocp_socket_recv_op(socket_ops::state_type state, socket_ops::weak_cancel_token_type cancel_token, const MutableBufferSequence& buffers, Handler handler) : operation(&win_iocp_socket_recv_op::do_complete), state_(state), cancel_token_(cancel_token), buffers_(buffers), handler_(handler) { } static void do_complete(io_service_impl* owner, operation* base, asio::error_code ec, std::size_t bytes_transferred) { // Take ownership of the operation object. win_iocp_socket_recv_op* o(static_cast(base)); ptr p = { boost::addressof(o->handler_), o, o }; #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) // Check whether buffers are still valid. if (owner) { buffer_sequence_adapter::validate(o->buffers_); } #endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) socket_ops::complete_iocp_recv(o->state_, o->cancel_token_, buffer_sequence_adapter::all_empty(o->buffers_), ec, bytes_transferred); // Make a copy of the handler so that the memory can be deallocated before // the upcall is made. Even if we're not about to make an upcall, a // sub-object of the handler may be the true owner of the memory associated // with the handler. Consequently, a local copy of the handler is required // to ensure that any owning sub-object remains valid until after we have // deallocated the memory here. detail::binder2 handler(o->handler_, ec, bytes_transferred); p.h = boost::addressof(handler.handler_); p.reset(); // Make the upcall if required. if (owner) { asio::detail::fenced_block b; asio_handler_invoke_helpers::invoke(handler, handler.handler_); } } private: socket_ops::state_type state_; socket_ops::weak_cancel_token_type cancel_token_; MutableBufferSequence buffers_; Handler handler_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_IOCP) #endif // ASIO_DETAIL_WIN_IOCP_SOCKET_RECV_OP_HPP percona-xtradb-cluster-galera/asio/asio/detail/win_iocp_socket_recvfrom_op.hpp0000644000000000000000000000701512247075736030276 0ustar rootroot00000000000000// // detail/win_iocp_socket_recvfrom_op.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_RECVFROM_OP_HPP #define ASIO_DETAIL_WIN_IOCP_SOCKET_RECVFROM_OP_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) #include #include "asio/detail/bind_handler.hpp" #include "asio/detail/buffer_sequence_adapter.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/operation.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class win_iocp_socket_recvfrom_op : public operation { public: ASIO_DEFINE_HANDLER_PTR(win_iocp_socket_recvfrom_op); win_iocp_socket_recvfrom_op(Endpoint& endpoint, socket_ops::weak_cancel_token_type cancel_token, const MutableBufferSequence& buffers, Handler handler) : operation(&win_iocp_socket_recvfrom_op::do_complete), endpoint_(endpoint), endpoint_size_(static_cast(endpoint.capacity())), cancel_token_(cancel_token), buffers_(buffers), handler_(handler) { } int& endpoint_size() { return endpoint_size_; } static void do_complete(io_service_impl* owner, operation* base, asio::error_code ec, std::size_t bytes_transferred) { // Take ownership of the operation object. win_iocp_socket_recvfrom_op* o( static_cast(base)); ptr p = { boost::addressof(o->handler_), o, o }; #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) // Check whether buffers are still valid. if (owner) { buffer_sequence_adapter::validate(o->buffers_); } #endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) socket_ops::complete_iocp_recvfrom(o->cancel_token_, ec); // Record the size of the endpoint returned by the operation. o->endpoint_.resize(o->endpoint_size_); // Make a copy of the handler so that the memory can be deallocated before // the upcall is made. Even if we're not about to make an upcall, a // sub-object of the handler may be the true owner of the memory associated // with the handler. Consequently, a local copy of the handler is required // to ensure that any owning sub-object remains valid until after we have // deallocated the memory here. detail::binder2 handler(o->handler_, ec, bytes_transferred); p.h = boost::addressof(handler.handler_); p.reset(); // Make the upcall if required. if (owner) { asio::detail::fenced_block b; asio_handler_invoke_helpers::invoke(handler, handler.handler_); } } private: Endpoint& endpoint_; int endpoint_size_; socket_ops::weak_cancel_token_type cancel_token_; MutableBufferSequence buffers_; Handler handler_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_IOCP) #endif // ASIO_DETAIL_WIN_IOCP_SOCKET_RECVFROM_OP_HPP percona-xtradb-cluster-galera/asio/asio/detail/win_iocp_socket_send_op.hpp0000644000000000000000000000615612247075736027411 0ustar rootroot00000000000000// // detail/win_iocp_socket_send_op.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_SEND_OP_HPP #define ASIO_DETAIL_WIN_IOCP_SOCKET_SEND_OP_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) #include #include "asio/detail/bind_handler.hpp" #include "asio/detail/buffer_sequence_adapter.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/operation.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class win_iocp_socket_send_op : public operation { public: ASIO_DEFINE_HANDLER_PTR(win_iocp_socket_send_op); win_iocp_socket_send_op(socket_ops::weak_cancel_token_type cancel_token, const ConstBufferSequence& buffers, Handler handler) : operation(&win_iocp_socket_send_op::do_complete), cancel_token_(cancel_token), buffers_(buffers), handler_(handler) { } static void do_complete(io_service_impl* owner, operation* base, asio::error_code ec, std::size_t bytes_transferred) { // Take ownership of the operation object. win_iocp_socket_send_op* o(static_cast(base)); ptr p = { boost::addressof(o->handler_), o, o }; #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) // Check whether buffers are still valid. if (owner) { buffer_sequence_adapter::validate(o->buffers_); } #endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) socket_ops::complete_iocp_send(o->cancel_token_, ec); // Make a copy of the handler so that the memory can be deallocated before // the upcall is made. Even if we're not about to make an upcall, a // sub-object of the handler may be the true owner of the memory associated // with the handler. Consequently, a local copy of the handler is required // to ensure that any owning sub-object remains valid until after we have // deallocated the memory here. detail::binder2 handler(o->handler_, ec, bytes_transferred); p.h = boost::addressof(handler.handler_); p.reset(); // Make the upcall if required. if (owner) { asio::detail::fenced_block b; asio_handler_invoke_helpers::invoke(handler, handler.handler_); } } private: socket_ops::weak_cancel_token_type cancel_token_; ConstBufferSequence buffers_; Handler handler_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_IOCP) #endif // ASIO_DETAIL_WIN_IOCP_SOCKET_SEND_OP_HPP percona-xtradb-cluster-galera/asio/asio/detail/win_iocp_socket_service.hpp0000644000000000000000000003505112247075736027416 0ustar rootroot00000000000000// // detail/win_iocp_socket_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP #define ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) #include #include #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/socket_base.hpp" #include "asio/detail/bind_handler.hpp" #include "asio/detail/buffer_sequence_adapter.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/mutex.hpp" #include "asio/detail/operation.hpp" #include "asio/detail/reactive_socket_connect_op.hpp" #include "asio/detail/reactor.hpp" #include "asio/detail/reactor_op.hpp" #include "asio/detail/socket_holder.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/win_iocp_io_service.hpp" #include "asio/detail/win_iocp_null_buffers_op.hpp" #include "asio/detail/win_iocp_socket_accept_op.hpp" #include "asio/detail/win_iocp_socket_recvfrom_op.hpp" #include "asio/detail/win_iocp_socket_send_op.hpp" #include "asio/detail/win_iocp_socket_service_base.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class win_iocp_socket_service : public win_iocp_socket_service_base { public: // The protocol type. typedef Protocol protocol_type; // The endpoint type. typedef typename Protocol::endpoint endpoint_type; // The native type of a socket. class native_type { public: native_type(socket_type s) : socket_(s), have_remote_endpoint_(false) { } native_type(socket_type s, const endpoint_type& ep) : socket_(s), have_remote_endpoint_(true), remote_endpoint_(ep) { } void operator=(socket_type s) { socket_ = s; have_remote_endpoint_ = false; remote_endpoint_ = endpoint_type(); } operator socket_type() const { return socket_; } bool have_remote_endpoint() const { return have_remote_endpoint_; } endpoint_type remote_endpoint() const { return remote_endpoint_; } private: socket_type socket_; bool have_remote_endpoint_; endpoint_type remote_endpoint_; }; // The implementation type of the socket. struct implementation_type : win_iocp_socket_service_base::base_implementation_type { // Default constructor. implementation_type() : protocol_(endpoint_type().protocol()), have_remote_endpoint_(false), remote_endpoint_() { } // The protocol associated with the socket. protocol_type protocol_; // Whether we have a cached remote endpoint. bool have_remote_endpoint_; // A cached remote endpoint. endpoint_type remote_endpoint_; }; // Constructor. win_iocp_socket_service(asio::io_service& io_service) : win_iocp_socket_service_base(io_service) { } // Open a new socket implementation. asio::error_code open(implementation_type& impl, const protocol_type& protocol, asio::error_code& ec) { if (!do_open(impl, protocol.family(), protocol.type(), protocol.protocol(), ec)) { impl.protocol_ = protocol; impl.have_remote_endpoint_ = false; impl.remote_endpoint_ = endpoint_type(); } return ec; } // Assign a native socket to a socket implementation. asio::error_code assign(implementation_type& impl, const protocol_type& protocol, const native_type& native_socket, asio::error_code& ec) { if (!do_assign(impl, protocol.type(), native_socket, ec)) { impl.protocol_ = protocol; impl.have_remote_endpoint_ = native_socket.have_remote_endpoint(); impl.remote_endpoint_ = native_socket.remote_endpoint(); } return ec; } // Get the native socket representation. native_type native(implementation_type& impl) { if (impl.have_remote_endpoint_) return native_type(impl.socket_, impl.remote_endpoint_); return native_type(impl.socket_); } // Bind the socket to the specified local endpoint. asio::error_code bind(implementation_type& impl, const endpoint_type& endpoint, asio::error_code& ec) { socket_ops::bind(impl.socket_, endpoint.data(), endpoint.size(), ec); return ec; } // Set a socket option. template asio::error_code set_option(implementation_type& impl, const Option& option, asio::error_code& ec) { socket_ops::setsockopt(impl.socket_, impl.state_, option.level(impl.protocol_), option.name(impl.protocol_), option.data(impl.protocol_), option.size(impl.protocol_), ec); return ec; } // Set a socket option. template asio::error_code get_option(const implementation_type& impl, Option& option, asio::error_code& ec) const { std::size_t size = option.size(impl.protocol_); socket_ops::getsockopt(impl.socket_, impl.state_, option.level(impl.protocol_), option.name(impl.protocol_), option.data(impl.protocol_), &size, ec); if (!ec) option.resize(impl.protocol_, size); return ec; } // Get the local endpoint. endpoint_type local_endpoint(const implementation_type& impl, asio::error_code& ec) const { endpoint_type endpoint; std::size_t addr_len = endpoint.capacity(); if (socket_ops::getsockname(impl.socket_, endpoint.data(), &addr_len, ec)) return endpoint_type(); endpoint.resize(addr_len); return endpoint; } // Get the remote endpoint. endpoint_type remote_endpoint(const implementation_type& impl, asio::error_code& ec) const { endpoint_type endpoint = impl.remote_endpoint_; std::size_t addr_len = endpoint.capacity(); if (socket_ops::getpeername(impl.socket_, endpoint.data(), &addr_len, impl.have_remote_endpoint_, ec)) return endpoint_type(); endpoint.resize(addr_len); return endpoint; } // Send a datagram to the specified endpoint. Returns the number of bytes // sent. template size_t send_to(implementation_type& impl, const ConstBufferSequence& buffers, const endpoint_type& destination, socket_base::message_flags flags, asio::error_code& ec) { buffer_sequence_adapter bufs(buffers); return socket_ops::sync_sendto(impl.socket_, impl.state_, bufs.buffers(), bufs.count(), flags, destination.data(), destination.size(), ec); } // Wait until data can be sent without blocking. size_t send_to(implementation_type& impl, const null_buffers&, const endpoint_type&, socket_base::message_flags, asio::error_code& ec) { // Wait for socket to become ready. socket_ops::poll_write(impl.socket_, ec); return 0; } // Start an asynchronous send. The data being sent must be valid for the // lifetime of the asynchronous operation. template void async_send_to(implementation_type& impl, const ConstBufferSequence& buffers, const endpoint_type& destination, socket_base::message_flags flags, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef win_iocp_socket_send_op op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(impl.cancel_token_, buffers, handler); buffer_sequence_adapter bufs(buffers); start_send_to_op(impl, bufs.buffers(), bufs.count(), destination.data(), static_cast(destination.size()), flags, p.p); p.v = p.p = 0; } // Start an asynchronous wait until data can be sent without blocking. template void async_send_to(implementation_type& impl, const null_buffers&, const endpoint_type&, socket_base::message_flags, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef win_iocp_null_buffers_op op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(impl.cancel_token_, handler); start_reactor_op(impl, reactor::write_op, p.p); p.v = p.p = 0; } // Receive a datagram with the endpoint of the sender. Returns the number of // bytes received. template size_t receive_from(implementation_type& impl, const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, socket_base::message_flags flags, asio::error_code& ec) { buffer_sequence_adapter bufs(buffers); std::size_t addr_len = sender_endpoint.capacity(); std::size_t bytes_recvd = socket_ops::sync_recvfrom( impl.socket_, impl.state_, bufs.buffers(), bufs.count(), flags, sender_endpoint.data(), &addr_len, ec); if (!ec) sender_endpoint.resize(addr_len); return bytes_recvd; } // Wait until data can be received without blocking. size_t receive_from(implementation_type& impl, const null_buffers&, endpoint_type& sender_endpoint, socket_base::message_flags, asio::error_code& ec) { // Wait for socket to become ready. socket_ops::poll_read(impl.socket_, ec); // Reset endpoint since it can be given no sensible value at this time. sender_endpoint = endpoint_type(); return 0; } // Start an asynchronous receive. The buffer for the data being received and // the sender_endpoint object must both be valid for the lifetime of the // asynchronous operation. template void async_receive_from(implementation_type& impl, const MutableBufferSequence& buffers, endpoint_type& sender_endp, socket_base::message_flags flags, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef win_iocp_socket_recvfrom_op< MutableBufferSequence, endpoint_type, Handler> op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(sender_endp, impl.cancel_token_, buffers, handler); buffer_sequence_adapter bufs(buffers); start_receive_from_op(impl, bufs.buffers(), bufs.count(), sender_endp.data(), flags, &p.p->endpoint_size(), p.p); p.v = p.p = 0; } // Wait until data can be received without blocking. template void async_receive_from(implementation_type& impl, const null_buffers&, endpoint_type& sender_endpoint, socket_base::message_flags flags, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef win_iocp_null_buffers_op op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(impl.cancel_token_, handler); // Reset endpoint since it can be given no sensible value at this time. sender_endpoint = endpoint_type(); start_null_buffers_receive_op(impl, flags, p.p); p.v = p.p = 0; } // Accept a new connection. template asio::error_code accept(implementation_type& impl, Socket& peer, endpoint_type* peer_endpoint, asio::error_code& ec) { // We cannot accept a socket that is already open. if (peer.is_open()) { ec = asio::error::already_open; return ec; } std::size_t addr_len = peer_endpoint ? peer_endpoint->capacity() : 0; socket_holder new_socket(socket_ops::sync_accept(impl.socket_, impl.state_, peer_endpoint ? peer_endpoint->data() : 0, peer_endpoint ? &addr_len : 0, ec)); // On success, assign new connection to peer socket object. if (new_socket.get() >= 0) { if (peer_endpoint) peer_endpoint->resize(addr_len); if (!peer.assign(impl.protocol_, new_socket.get(), ec)) new_socket.release(); } return ec; } // Start an asynchronous accept. The peer and peer_endpoint objects // must be valid until the accept's handler is invoked. template void async_accept(implementation_type& impl, Socket& peer, endpoint_type* peer_endpoint, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef win_iocp_socket_accept_op op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; bool enable_connection_aborted = (impl.state_ & socket_ops::enable_connection_aborted) != 0; p.p = new (p.v) op(*this, impl.socket_, peer, impl.protocol_, peer_endpoint, enable_connection_aborted, handler); start_accept_op(impl, peer.is_open(), p.p->new_socket(), impl.protocol_.family(), impl.protocol_.type(), impl.protocol_.protocol(), p.p->output_buffer(), p.p->address_length(), p.p); p.v = p.p = 0; } // Connect the socket to the specified endpoint. asio::error_code connect(implementation_type& impl, const endpoint_type& peer_endpoint, asio::error_code& ec) { socket_ops::sync_connect(impl.socket_, peer_endpoint.data(), peer_endpoint.size(), ec); return ec; } // Start an asynchronous connect. template void async_connect(implementation_type& impl, const endpoint_type& peer_endpoint, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef reactive_socket_connect_op op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(impl.socket_, handler); start_connect_op(impl, p.p, peer_endpoint.data(), static_cast(peer_endpoint.size())); p.v = p.p = 0; } }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_IOCP) #endif // ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/detail/win_iocp_socket_service_base.hpp0000644000000000000000000003403012247075736030404 0ustar rootroot00000000000000// // detail/win_iocp_socket_service_base.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_BASE_HPP #define ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_BASE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) #include #include #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/socket_base.hpp" #include "asio/detail/bind_handler.hpp" #include "asio/detail/buffer_sequence_adapter.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/mutex.hpp" #include "asio/detail/operation.hpp" #include "asio/detail/reactor.hpp" #include "asio/detail/reactor_op.hpp" #include "asio/detail/socket_holder.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/win_iocp_io_service.hpp" #include "asio/detail/win_iocp_null_buffers_op.hpp" #include "asio/detail/win_iocp_socket_send_op.hpp" #include "asio/detail/win_iocp_socket_recv_op.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class win_iocp_socket_service_base { public: // The implementation type of the socket. struct base_implementation_type { // The native socket representation. socket_type socket_; // The current state of the socket. socket_ops::state_type state_; // We use a shared pointer as a cancellation token here to work around the // broken Windows support for cancellation. MSDN says that when you call // closesocket any outstanding WSARecv or WSASend operations will complete // with the error ERROR_OPERATION_ABORTED. In practice they complete with // ERROR_NETNAME_DELETED, which means you can't tell the difference between // a local cancellation and the socket being hard-closed by the peer. socket_ops::shared_cancel_token_type cancel_token_; // Per-descriptor data used by the reactor. reactor::per_descriptor_data reactor_data_; #if defined(ASIO_ENABLE_CANCELIO) // The ID of the thread from which it is safe to cancel asynchronous // operations. 0 means no asynchronous operations have been started yet. // ~0 means asynchronous operations have been started from more than one // thread, and cancellation is not supported for the socket. DWORD safe_cancellation_thread_id_; #endif // defined(ASIO_ENABLE_CANCELIO) // Pointers to adjacent socket implementations in linked list. base_implementation_type* next_; base_implementation_type* prev_; }; // Constructor. ASIO_DECL win_iocp_socket_service_base( asio::io_service& io_service); // Destroy all user-defined handler objects owned by the service. ASIO_DECL void shutdown_service(); // Construct a new socket implementation. ASIO_DECL void construct(base_implementation_type& impl); // Destroy a socket implementation. ASIO_DECL void destroy(base_implementation_type& impl); // Determine whether the socket is open. bool is_open(const base_implementation_type& impl) const { return impl.socket_ != invalid_socket; } // Destroy a socket implementation. ASIO_DECL asio::error_code close( base_implementation_type& impl, asio::error_code& ec); // Cancel all operations associated with the socket. ASIO_DECL asio::error_code cancel( base_implementation_type& impl, asio::error_code& ec); // Determine whether the socket is at the out-of-band data mark. bool at_mark(const base_implementation_type& impl, asio::error_code& ec) const { return socket_ops::sockatmark(impl.socket_, ec); } // Determine the number of bytes available for reading. std::size_t available(const base_implementation_type& impl, asio::error_code& ec) const { return socket_ops::available(impl.socket_, ec); } // Place the socket into the state where it will listen for new connections. asio::error_code listen(base_implementation_type& impl, int backlog, asio::error_code& ec) { socket_ops::listen(impl.socket_, backlog, ec); return ec; } // Perform an IO control command on the socket. template asio::error_code io_control(base_implementation_type& impl, IO_Control_Command& command, asio::error_code& ec) { socket_ops::ioctl(impl.socket_, impl.state_, command.name(), static_cast(command.data()), ec); return ec; } /// Disable sends or receives on the socket. asio::error_code shutdown(base_implementation_type& impl, socket_base::shutdown_type what, asio::error_code& ec) { socket_ops::shutdown(impl.socket_, what, ec); return ec; } // Send the given data to the peer. Returns the number of bytes sent. template size_t send(base_implementation_type& impl, const ConstBufferSequence& buffers, socket_base::message_flags flags, asio::error_code& ec) { buffer_sequence_adapter bufs(buffers); return socket_ops::sync_send(impl.socket_, impl.state_, bufs.buffers(), bufs.count(), flags, bufs.all_empty(), ec); } // Wait until data can be sent without blocking. size_t send(base_implementation_type& impl, const null_buffers&, socket_base::message_flags, asio::error_code& ec) { // Wait for socket to become ready. socket_ops::poll_write(impl.socket_, ec); return 0; } // Start an asynchronous send. The data being sent must be valid for the // lifetime of the asynchronous operation. template void async_send(base_implementation_type& impl, const ConstBufferSequence& buffers, socket_base::message_flags flags, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef win_iocp_socket_send_op op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(impl.cancel_token_, buffers, handler); buffer_sequence_adapter bufs(buffers); start_send_op(impl, bufs.buffers(), bufs.count(), flags, (impl.state_ & socket_ops::stream_oriented) != 0 && bufs.all_empty(), p.p); p.v = p.p = 0; } // Start an asynchronous wait until data can be sent without blocking. template void async_send(base_implementation_type& impl, const null_buffers&, socket_base::message_flags, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef win_iocp_null_buffers_op op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(impl.cancel_token_, handler); start_reactor_op(impl, reactor::write_op, p.p); p.v = p.p = 0; } // Receive some data from the peer. Returns the number of bytes received. template size_t receive(base_implementation_type& impl, const MutableBufferSequence& buffers, socket_base::message_flags flags, asio::error_code& ec) { buffer_sequence_adapter bufs(buffers); return socket_ops::sync_recv(impl.socket_, impl.state_, bufs.buffers(), bufs.count(), flags, bufs.all_empty(), ec); } // Wait until data can be received without blocking. size_t receive(base_implementation_type& impl, const null_buffers&, socket_base::message_flags, asio::error_code& ec) { // Wait for socket to become ready. socket_ops::poll_read(impl.socket_, ec); return 0; } // Start an asynchronous receive. The buffer for the data being received // must be valid for the lifetime of the asynchronous operation. template void async_receive(base_implementation_type& impl, const MutableBufferSequence& buffers, socket_base::message_flags flags, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef win_iocp_socket_recv_op op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(impl.state_, impl.cancel_token_, buffers, handler); buffer_sequence_adapter bufs(buffers); start_receive_op(impl, bufs.buffers(), bufs.count(), flags, (impl.state_ & socket_ops::stream_oriented) != 0 && bufs.all_empty(), p.p); p.v = p.p = 0; } // Wait until data can be received without blocking. template void async_receive(base_implementation_type& impl, const null_buffers&, socket_base::message_flags flags, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef win_iocp_null_buffers_op op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(impl.cancel_token_, handler); start_null_buffers_receive_op(impl, flags, p.p); p.v = p.p = 0; } // Helper function to restart an asynchronous accept operation. ASIO_DECL void restart_accept_op(socket_type s, socket_holder& new_socket, int family, int type, int protocol, void* output_buffer, DWORD address_length, operation* op); protected: // Open a new socket implementation. ASIO_DECL asio::error_code do_open( base_implementation_type& impl, int family, int type, int protocol, asio::error_code& ec); // Assign a native socket to a socket implementation. ASIO_DECL asio::error_code do_assign( base_implementation_type& impl, int type, socket_type native_socket, asio::error_code& ec); // Helper function to start an asynchronous send operation. ASIO_DECL void start_send_op(base_implementation_type& impl, WSABUF* buffers, std::size_t buffer_count, socket_base::message_flags flags, bool noop, operation* op); // Helper function to start an asynchronous send_to operation. ASIO_DECL void start_send_to_op(base_implementation_type& impl, WSABUF* buffers, std::size_t buffer_count, const socket_addr_type* addr, int addrlen, socket_base::message_flags flags, operation* op); // Helper function to start an asynchronous receive operation. ASIO_DECL void start_receive_op(base_implementation_type& impl, WSABUF* buffers, std::size_t buffer_count, socket_base::message_flags flags, bool noop, operation* op); // Helper function to start an asynchronous null_buffers receive operation. ASIO_DECL void start_null_buffers_receive_op( base_implementation_type& impl, socket_base::message_flags flags, reactor_op* op); // Helper function to start an asynchronous receive_from operation. ASIO_DECL void start_receive_from_op(base_implementation_type& impl, WSABUF* buffers, std::size_t buffer_count, socket_addr_type* addr, socket_base::message_flags flags, int* addrlen, operation* op); // Helper function to start an asynchronous accept operation. ASIO_DECL void start_accept_op(base_implementation_type& impl, bool peer_is_open, socket_holder& new_socket, int family, int type, int protocol, void* output_buffer, DWORD address_length, operation* op); // Start an asynchronous read or write operation using the the reactor. ASIO_DECL void start_reactor_op(base_implementation_type& impl, int op_type, reactor_op* op); // Start the asynchronous connect operation using the reactor. ASIO_DECL void start_connect_op(base_implementation_type& impl, reactor_op* op, const socket_addr_type* addr, std::size_t addrlen); // Helper function to close a socket when the associated object is being // destroyed. ASIO_DECL void close_for_destruction(base_implementation_type& impl); // Update the ID of the thread from which cancellation is safe. ASIO_DECL void update_cancellation_thread_id( base_implementation_type& impl); // Helper function to get the reactor. If no reactor has been created yet, a // new one is obtained from the io_service and a pointer to it is cached in // this service. ASIO_DECL reactor& get_reactor(); // Helper function to emulate InterlockedCompareExchangePointer functionality // for: // - very old Platform SDKs; and // - platform SDKs where MSVC's /Wp64 option causes spurious warnings. ASIO_DECL void* interlocked_compare_exchange_pointer( void** dest, void* exch, void* cmp); // Helper function to emulate InterlockedExchangePointer functionality for: // - very old Platform SDKs; and // - platform SDKs where MSVC's /Wp64 option causes spurious warnings. ASIO_DECL void* interlocked_exchange_pointer(void** dest, void* val); // The io_service used to obtain the reactor, if required. asio::io_service& io_service_; // The IOCP service used for running asynchronous operations and dispatching // handlers. win_iocp_io_service& iocp_service_; // The reactor used for performing connect operations. This object is created // only if needed. reactor* reactor_; // Mutex to protect access to the linked list of implementations. asio::detail::mutex mutex_; // The head of a linked list of all implementations. base_implementation_type* impl_list_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/win_iocp_socket_service_base.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // defined(ASIO_HAS_IOCP) #endif // ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_BASE_HPP percona-xtradb-cluster-galera/asio/asio/detail/win_mutex.hpp0000644000000000000000000000323412247075736024534 0ustar rootroot00000000000000// // detail/win_mutex.hpp // ~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_WIN_MUTEX_HPP #define ASIO_DETAIL_WIN_MUTEX_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) #include "asio/detail/noncopyable.hpp" #include "asio/detail/scoped_lock.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class win_mutex : private noncopyable { public: typedef asio::detail::scoped_lock scoped_lock; // Constructor. ASIO_DECL win_mutex(); // Destructor. ~win_mutex() { ::DeleteCriticalSection(&crit_section_); } // Lock the mutex. void lock() { ::EnterCriticalSection(&crit_section_); } // Unlock the mutex. void unlock() { ::LeaveCriticalSection(&crit_section_); } private: // Initialisation must be performed in a separate function to the constructor // since the compiler does not support the use of structured exceptions and // C++ exceptions in the same function. ASIO_DECL int do_init(); ::CRITICAL_SECTION crit_section_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/win_mutex.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // defined(BOOST_WINDOWS) #endif // ASIO_DETAIL_WIN_MUTEX_HPP percona-xtradb-cluster-galera/asio/asio/detail/win_signal_blocker.hpp0000644000000000000000000000255112247075736026351 0ustar rootroot00000000000000// // win_signal_blocker.hpp // ~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_WIN_SIGNAL_BLOCKER_HPP #define ASIO_DETAIL_WIN_SIGNAL_BLOCKER_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/push_options.hpp" #include "asio/detail/push_options.hpp" #include #include "asio/detail/pop_options.hpp" #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) #include "asio/detail/noncopyable.hpp" namespace asio { namespace detail { class win_signal_blocker : private noncopyable { public: // Constructor blocks all signals for the calling thread. win_signal_blocker() { // No-op. } // Destructor restores the previous signal mask. ~win_signal_blocker() { // No-op. } // Block all signals for the calling thread. void block() { // No-op. } // Restore the previous signal mask. void unblock() { // No-op. } }; } // namespace detail } // namespace asio #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_WIN_SIGNAL_BLOCKER_HPP percona-xtradb-cluster-galera/asio/asio/detail/win_thread.hpp0000644000000000000000000000545212247075736024645 0ustar rootroot00000000000000// // detail/win_thread.hpp // ~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_WIN_THREAD_HPP #define ASIO_DETAIL_WIN_THREAD_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) && !defined(UNDER_CE) #include "asio/detail/noncopyable.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { ASIO_DECL unsigned int __stdcall win_thread_function(void* arg); #if defined(WINVER) && (WINVER < 0x0500) ASIO_DECL void __stdcall apc_function(ULONG data); #else ASIO_DECL void __stdcall apc_function(ULONG_PTR data); #endif template class win_thread_base { public: static bool terminate_threads() { return ::InterlockedExchangeAdd(&terminate_threads_, 0) != 0; } static void set_terminate_threads(bool b) { ::InterlockedExchange(&terminate_threads_, b ? 1 : 0); } private: static long terminate_threads_; }; template long win_thread_base::terminate_threads_ = 0; class win_thread : private noncopyable, public win_thread_base { public: // Constructor. template win_thread(Function f, unsigned int stack_size = 0) : thread_(0), exit_event_(0) { start_thread(new func(f), stack_size); } // Destructor. ASIO_DECL ~win_thread(); // Wait for the thread to exit. ASIO_DECL void join(); private: friend ASIO_DECL unsigned int __stdcall win_thread_function(void* arg); #if defined(WINVER) && (WINVER < 0x0500) friend ASIO_DECL void __stdcall apc_function(ULONG); #else friend ASIO_DECL void __stdcall apc_function(ULONG_PTR); #endif class func_base { public: virtual ~func_base() {} virtual void run() = 0; ::HANDLE entry_event_; ::HANDLE exit_event_; }; struct auto_func_base_ptr { func_base* ptr; ~auto_func_base_ptr() { delete ptr; } }; template class func : public func_base { public: func(Function f) : f_(f) { } virtual void run() { f_(); } private: Function f_; }; ASIO_DECL void start_thread(func_base* arg, unsigned int stack_size); ::HANDLE thread_; ::HANDLE exit_event_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/win_thread.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // defined(BOOST_WINDOWS) && !defined(UNDER_CE) #endif // ASIO_DETAIL_WIN_THREAD_HPP percona-xtradb-cluster-galera/asio/asio/detail/win_tss_ptr.hpp0000644000000000000000000000312712247075736025071 0ustar rootroot00000000000000// // detail/win_tss_ptr.hpp // ~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_WIN_TSS_PTR_HPP #define ASIO_DETAIL_WIN_TSS_PTR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) #include "asio/detail/noncopyable.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { // Helper function to create thread-specific storage. ASIO_DECL DWORD win_tss_ptr_create(); template class win_tss_ptr : private noncopyable { public: // Constructor. win_tss_ptr() : tss_key_(win_tss_ptr_create()) { } // Destructor. ~win_tss_ptr() { ::TlsFree(tss_key_); } // Get the value. operator T*() const { return static_cast(::TlsGetValue(tss_key_)); } // Set the value. void operator=(T* value) { ::TlsSetValue(tss_key_, value); } private: // Thread-specific storage to allow unlocked access to determine whether a // thread is a member of the pool. DWORD tss_key_; }; } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/win_tss_ptr.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // defined(BOOST_WINDOWS) #endif // ASIO_DETAIL_WIN_TSS_PTR_HPP percona-xtradb-cluster-galera/asio/asio/detail/wince_thread.hpp0000644000000000000000000000442612247075736025155 0ustar rootroot00000000000000// // detail/wince_thread.hpp // ~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_WINCE_THREAD_HPP #define ASIO_DETAIL_WINCE_THREAD_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) && defined(UNDER_CE) #include #include "asio/detail/noncopyable.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { DWORD WINAPI wince_thread_function(LPVOID arg); class wince_thread : private noncopyable { public: // Constructor. template wince_thread(Function f) { std::auto_ptr arg(new func(f)); DWORD thread_id = 0; thread_ = ::CreateThread(0, 0, wince_thread_function, arg.get(), 0, &thread_id); if (!thread_) { DWORD last_error = ::GetLastError(); asio::error_code ec(last_error, asio::error::get_system_category()); asio::detail::throw_error(ec, "thread"); } arg.release(); } // Destructor. ~wince_thread() { ::CloseHandle(thread_); } // Wait for the thread to exit. void join() { ::WaitForSingleObject(thread_, INFINITE); } private: friend DWORD WINAPI wince_thread_function(LPVOID arg); class func_base { public: virtual ~func_base() {} virtual void run() = 0; }; template class func : public func_base { public: func(Function f) : f_(f) { } virtual void run() { f_(); } private: Function f_; }; ::HANDLE thread_; }; inline DWORD WINAPI wince_thread_function(LPVOID arg) { std::auto_ptr func( static_cast(arg)); func->run(); return 0; } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(BOOST_WINDOWS) && defined(UNDER_CE) #endif // ASIO_DETAIL_WINCE_THREAD_HPP percona-xtradb-cluster-galera/asio/asio/detail/winsock_init.hpp0000644000000000000000000000407012247075736025214 0ustar rootroot00000000000000// // detail/winsock_init.hpp // ~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_WINSOCK_INIT_HPP #define ASIO_DETAIL_WINSOCK_INIT_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class winsock_init_base { protected: // Structure to track result of initialisation and number of uses. POD is used // to ensure that the values are zero-initialised prior to any code being run. struct data { long init_count_; long result_; }; ASIO_DECL static void startup(data& d, unsigned char major, unsigned char minor); ASIO_DECL static void cleanup(data& d); ASIO_DECL static void throw_on_error(data& d); }; template class winsock_init : private winsock_init_base { public: winsock_init(bool allow_throw = true) { startup(data_, Major, Minor); if (allow_throw) throw_on_error(data_); } winsock_init(const winsock_init&) { startup(data_, Major, Minor); throw_on_error(data_); } ~winsock_init() { cleanup(data_); } private: static data data_; }; template winsock_init_base::data winsock_init::data_; // Static variable to ensure that winsock is initialised before main, and // therefore before any other threads can get started. static const winsock_init<>& winsock_init_instance = winsock_init<>(false); } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/detail/impl/winsock_init.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) #endif // ASIO_DETAIL_WINSOCK_INIT_HPP percona-xtradb-cluster-galera/asio/asio/detail/wrapped_handler.hpp0000644000000000000000000001253712247075736025662 0ustar rootroot00000000000000// // detail/wrapped_handler.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_WRAPPED_HANDLER_HPP #define ASIO_DETAIL_WRAPPED_HANDLER_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/bind_handler.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template class wrapped_handler { public: typedef void result_type; wrapped_handler(Dispatcher dispatcher, Handler handler) : dispatcher_(dispatcher), handler_(handler) { } void operator()() { dispatcher_.dispatch(handler_); } void operator()() const { dispatcher_.dispatch(handler_); } template void operator()(const Arg1& arg1) { dispatcher_.dispatch(detail::bind_handler(handler_, arg1)); } template void operator()(const Arg1& arg1) const { dispatcher_.dispatch(detail::bind_handler(handler_, arg1)); } template void operator()(const Arg1& arg1, const Arg2& arg2) { dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2)); } template void operator()(const Arg1& arg1, const Arg2& arg2) const { dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2)); } template void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) { dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2, arg3)); } template void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) const { dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2, arg3)); } template void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4) { dispatcher_.dispatch( detail::bind_handler(handler_, arg1, arg2, arg3, arg4)); } template void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4) const { dispatcher_.dispatch( detail::bind_handler(handler_, arg1, arg2, arg3, arg4)); } template void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4, const Arg5& arg5) { dispatcher_.dispatch( detail::bind_handler(handler_, arg1, arg2, arg3, arg4, arg5)); } template void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4, const Arg5& arg5) const { dispatcher_.dispatch( detail::bind_handler(handler_, arg1, arg2, arg3, arg4, arg5)); } //private: Dispatcher dispatcher_; Handler handler_; }; template class rewrapped_handler { public: explicit rewrapped_handler(const Handler& handler, const Context& context) : handler_(handler), context_(context) { } void operator()() { handler_(); } void operator()() const { handler_(); } //private: Handler handler_; Context context_; }; template inline void* asio_handler_allocate(std::size_t size, wrapped_handler* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, wrapped_handler* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, wrapped_handler* this_handler) { this_handler->dispatcher_.dispatch( rewrapped_handler( function, this_handler->handler_)); } template inline void* asio_handler_allocate(std::size_t size, rewrapped_handler* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->context_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, rewrapped_handler* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->context_); } template inline void asio_handler_invoke(const Function& function, rewrapped_handler* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->context_); } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_WRAPPED_HANDLER_HPP percona-xtradb-cluster-galera/asio/asio/detail/impl/descriptor_ops.ipp0000644000000000000000000001772212247075736026525 0ustar rootroot00000000000000// // detail/impl/descriptor_ops.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_DESCRIPTOR_OPS_IPP #define ASIO_DETAIL_IMPL_DESCRIPTOR_OPS_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/detail/descriptor_ops.hpp" #include "asio/error.hpp" #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) #include "asio/detail/push_options.hpp" namespace asio { namespace detail { namespace descriptor_ops { int open(const char* path, int flags, asio::error_code& ec) { errno = 0; int result = error_wrapper(::open(path, flags), ec); if (result >= 0) ec = asio::error_code(); return result; } int close(int d, state_type& state, asio::error_code& ec) { int result = 0; if (d != -1) { if (state & internal_non_blocking) { #if defined(__SYMBIAN32__) int flags = ::fcntl(d, F_GETFL, 0); if (flags >= 0) ::fcntl(d, F_SETFL, flags & ~O_NONBLOCK); #else // defined(__SYMBIAN32__) ioctl_arg_type arg = 0; ::ioctl(d, FIONBIO, &arg); #endif // defined(__SYMBIAN32__) state &= ~internal_non_blocking; } errno = 0; result = error_wrapper(::close(d), ec); } if (result == 0) ec = asio::error_code(); return result; } bool set_internal_non_blocking(int d, state_type& state, asio::error_code& ec) { if (d == -1) { ec = asio::error::bad_descriptor; return false; } errno = 0; #if defined(__SYMBIAN32__) int result = error_wrapper(::fcntl(d, F_GETFL, 0), ec); if (result >= 0) { errno = 0; result = error_wrapper(::fcntl(d, F_SETFL, result | O_NONBLOCK), ec); } #else // defined(__SYMBIAN32__) ioctl_arg_type arg = 1; int result = error_wrapper(::ioctl(d, FIONBIO, &arg), ec); #endif // defined(__SYMBIAN32__) if (result >= 0) { ec = asio::error_code(); state |= internal_non_blocking; return true; } return false; } std::size_t sync_read(int d, state_type state, buf* bufs, std::size_t count, bool all_empty, asio::error_code& ec) { if (d == -1) { ec = asio::error::bad_descriptor; return 0; } // A request to read 0 bytes on a stream is a no-op. if (all_empty) { ec = asio::error_code(); return 0; } // Read some data. for (;;) { // Try to complete the operation without blocking. errno = 0; int bytes = error_wrapper(::readv(d, bufs, static_cast(count)), ec); // Check if operation succeeded. if (bytes > 0) return bytes; // Check for EOF. if (bytes == 0) { ec = asio::error::eof; return 0; } // Operation failed. if ((state & user_set_non_blocking) || (ec != asio::error::would_block && ec != asio::error::try_again)) return 0; // Wait for descriptor to become ready. if (descriptor_ops::poll_read(d, ec) < 0) return 0; } } bool non_blocking_read(int d, buf* bufs, std::size_t count, asio::error_code& ec, std::size_t& bytes_transferred) { for (;;) { // Read some data. errno = 0; int bytes = error_wrapper(::readv(d, bufs, static_cast(count)), ec); // Check for end of stream. if (bytes == 0) { ec = asio::error::eof; return true; } // Retry operation if interrupted by signal. if (ec == asio::error::interrupted) continue; // Check if we need to run the operation again. if (ec == asio::error::would_block || ec == asio::error::try_again) return false; // Operation is complete. if (bytes > 0) { ec = asio::error_code(); bytes_transferred = bytes; } else bytes_transferred = 0; return true; } } std::size_t sync_write(int d, state_type state, const buf* bufs, std::size_t count, bool all_empty, asio::error_code& ec) { if (d == -1) { ec = asio::error::bad_descriptor; return 0; } // A request to write 0 bytes on a stream is a no-op. if (all_empty) { ec = asio::error_code(); return 0; } // Write some data. for (;;) { // Try to complete the operation without blocking. errno = 0; int bytes = error_wrapper(::writev(d, bufs, static_cast(count)), ec); // Check if operation succeeded. if (bytes > 0) return bytes; // Operation failed. if ((state & user_set_non_blocking) || (ec != asio::error::would_block && ec != asio::error::try_again)) return 0; // Wait for descriptor to become ready. if (descriptor_ops::poll_write(d, ec) < 0) return 0; } } bool non_blocking_write(int d, const buf* bufs, std::size_t count, asio::error_code& ec, std::size_t& bytes_transferred) { for (;;) { // Write some data. errno = 0; int bytes = error_wrapper(::writev(d, bufs, static_cast(count)), ec); // Retry operation if interrupted by signal. if (ec == asio::error::interrupted) continue; // Check if we need to run the operation again. if (ec == asio::error::would_block || ec == asio::error::try_again) return false; // Operation is complete. if (bytes >= 0) { ec = asio::error_code(); bytes_transferred = bytes; } else bytes_transferred = 0; return true; } } int ioctl(int d, state_type& state, long cmd, ioctl_arg_type* arg, asio::error_code& ec) { if (d == -1) { ec = asio::error::bad_descriptor; return -1; } errno = 0; int result = error_wrapper(::ioctl(d, cmd, arg), ec); if (result >= 0) { ec = asio::error_code(); // When updating the non-blocking mode we always perform the ioctl syscall, // even if the flags would otherwise indicate that the descriptor is // already in the correct state. This ensures that the underlying // descriptor is put into the state that has been requested by the user. If // the ioctl syscall was successful then we need to update the flags to // match. if (cmd == static_cast(FIONBIO)) { if (*arg) { state |= user_set_non_blocking; } else { // Clearing the non-blocking mode always overrides any internally-set // non-blocking flag. Any subsequent asynchronous operations will need // to re-enable non-blocking I/O. state &= ~(user_set_non_blocking | internal_non_blocking); } } } return result; } int fcntl(int d, long cmd, asio::error_code& ec) { if (d == -1) { ec = asio::error::bad_descriptor; return -1; } errno = 0; int result = error_wrapper(::fcntl(d, cmd), ec); if (result != -1) ec = asio::error_code(); return result; } int fcntl(int d, long cmd, long arg, asio::error_code& ec) { if (d == -1) { ec = asio::error::bad_descriptor; return -1; } errno = 0; int result = error_wrapper(::fcntl(d, cmd, arg), ec); if (result != -1) ec = asio::error_code(); return result; } int poll_read(int d, asio::error_code& ec) { if (d == -1) { ec = asio::error::bad_descriptor; return -1; } pollfd fds; fds.fd = d; fds.events = POLLIN; fds.revents = 0; errno = 0; int result = error_wrapper(::poll(&fds, 1, -1), ec); if (result >= 0) ec = asio::error_code(); return result; } int poll_write(int d, asio::error_code& ec) { if (d == -1) { ec = asio::error::bad_descriptor; return -1; } pollfd fds; fds.fd = d; fds.events = POLLOUT; fds.revents = 0; errno = 0; int result = error_wrapper(::poll(&fds, 1, -1), ec); if (result >= 0) ec = asio::error_code(); return result; } } // namespace descriptor_ops } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) #endif // ASIO_DETAIL_IMPL_DESCRIPTOR_OPS_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/dev_poll_reactor.hpp0000644000000000000000000000370712247075736027006 0ustar rootroot00000000000000// // detail/impl/dev_poll_reactor.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_HPP #define ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_DEV_POLL) #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template void dev_poll_reactor::add_timer_queue(timer_queue& queue) { do_add_timer_queue(queue); } template void dev_poll_reactor::remove_timer_queue(timer_queue& queue) { do_remove_timer_queue(queue); } template void dev_poll_reactor::schedule_timer(timer_queue& queue, const typename Time_Traits::time_type& time, typename timer_queue::per_timer_data& timer, timer_op* op) { asio::detail::mutex::scoped_lock lock(mutex_); if (shutdown_) { io_service_.post_immediate_completion(op); return; } bool earliest = queue.enqueue_timer(time, timer, op); io_service_.work_started(); if (earliest) interrupter_.interrupt(); } template std::size_t dev_poll_reactor::cancel_timer(timer_queue& queue, typename timer_queue::per_timer_data& timer) { asio::detail::mutex::scoped_lock lock(mutex_); op_queue ops; std::size_t n = queue.cancel_timer(timer, ops); lock.unlock(); io_service_.post_deferred_completions(ops); return n; } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_DEV_POLL) #endif // ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_HPP percona-xtradb-cluster-galera/asio/asio/detail/impl/dev_poll_reactor.ipp0000644000000000000000000002274312247075736027010 0ustar rootroot00000000000000// // detail/impl/dev_poll_reactor.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_IPP #define ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_DEV_POLL) #include "asio/detail/dev_poll_reactor.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { dev_poll_reactor::dev_poll_reactor(asio::io_service& io_service) : asio::detail::service_base(io_service), io_service_(use_service(io_service)), mutex_(), dev_poll_fd_(do_dev_poll_create()), interrupter_(), shutdown_(false) { // Add the interrupter's descriptor to /dev/poll. ::pollfd ev = { 0, 0, 0 }; ev.fd = interrupter_.read_descriptor(); ev.events = POLLIN | POLLERR; ev.revents = 0; ::write(dev_poll_fd_, &ev, sizeof(ev)); } dev_poll_reactor::~dev_poll_reactor() { shutdown_service(); ::close(dev_poll_fd_); } void dev_poll_reactor::shutdown_service() { asio::detail::mutex::scoped_lock lock(mutex_); shutdown_ = true; lock.unlock(); op_queue ops; for (int i = 0; i < max_ops; ++i) op_queue_[i].get_all_operations(ops); timer_queues_.get_all_timers(ops); } void dev_poll_reactor::init_task() { io_service_.init_task(); } int dev_poll_reactor::register_descriptor(socket_type, per_descriptor_data&) { return 0; } void dev_poll_reactor::start_op(int op_type, socket_type descriptor, dev_poll_reactor::per_descriptor_data&, reactor_op* op, bool allow_speculative) { asio::detail::mutex::scoped_lock lock(mutex_); if (shutdown_) { post_immediate_completion(op); return; } if (allow_speculative) { if (op_type != read_op || !op_queue_[except_op].has_operation(descriptor)) { if (!op_queue_[op_type].has_operation(descriptor)) { if (op->perform()) { lock.unlock(); io_service_.post_immediate_completion(op); return; } } } } bool first = op_queue_[op_type].enqueue_operation(descriptor, op); io_service_.work_started(); if (first) { ::pollfd& ev = add_pending_event_change(descriptor); ev.events = POLLERR | POLLHUP; if (op_type == read_op || op_queue_[read_op].has_operation(descriptor)) ev.events |= POLLIN; if (op_type == write_op || op_queue_[write_op].has_operation(descriptor)) ev.events |= POLLOUT; if (op_type == except_op || op_queue_[except_op].has_operation(descriptor)) ev.events |= POLLPRI; interrupter_.interrupt(); } } void dev_poll_reactor::cancel_ops(socket_type descriptor, dev_poll_reactor::per_descriptor_data&) { asio::detail::mutex::scoped_lock lock(mutex_); cancel_ops_unlocked(descriptor, asio::error::operation_aborted); } void dev_poll_reactor::close_descriptor(socket_type descriptor, dev_poll_reactor::per_descriptor_data&) { asio::detail::mutex::scoped_lock lock(mutex_); // Remove the descriptor from /dev/poll. ::pollfd& ev = add_pending_event_change(descriptor); ev.events = POLLREMOVE; interrupter_.interrupt(); // Cancel any outstanding operations associated with the descriptor. cancel_ops_unlocked(descriptor, asio::error::operation_aborted); } void dev_poll_reactor::run(bool block, op_queue& ops) { asio::detail::mutex::scoped_lock lock(mutex_); // We can return immediately if there's no work to do and the reactor is // not supposed to block. if (!block && op_queue_[read_op].empty() && op_queue_[write_op].empty() && op_queue_[except_op].empty() && timer_queues_.all_empty()) return; // Write the pending event registration changes to the /dev/poll descriptor. std::size_t events_size = sizeof(::pollfd) * pending_event_changes_.size(); if (events_size > 0) { errno = 0; int result = ::write(dev_poll_fd_, &pending_event_changes_[0], events_size); if (result != static_cast(events_size)) { asio::error_code ec = asio::error_code( errno, asio::error::get_system_category()); for (std::size_t i = 0; i < pending_event_changes_.size(); ++i) { int descriptor = pending_event_changes_[i].fd; for (int j = 0; j < max_ops; ++j) op_queue_[j].cancel_operations(descriptor, ops, ec); } } pending_event_changes_.clear(); pending_event_change_index_.clear(); } int timeout = block ? get_timeout() : 0; lock.unlock(); // Block on the /dev/poll descriptor. ::pollfd events[128] = { { 0, 0, 0 } }; ::dvpoll dp = { 0, 0, 0 }; dp.dp_fds = events; dp.dp_nfds = 128; dp.dp_timeout = timeout; int num_events = ::ioctl(dev_poll_fd_, DP_POLL, &dp); lock.lock(); // Dispatch the waiting events. for (int i = 0; i < num_events; ++i) { int descriptor = events[i].fd; if (descriptor == interrupter_.read_descriptor()) { interrupter_.reset(); } else { bool more_reads = false; bool more_writes = false; bool more_except = false; // Exception operations must be processed first to ensure that any // out-of-band data is read before normal data. if (events[i].events & (POLLPRI | POLLERR | POLLHUP)) more_except = op_queue_[except_op].perform_operations(descriptor, ops); else more_except = op_queue_[except_op].has_operation(descriptor); if (events[i].events & (POLLIN | POLLERR | POLLHUP)) more_reads = op_queue_[read_op].perform_operations(descriptor, ops); else more_reads = op_queue_[read_op].has_operation(descriptor); if (events[i].events & (POLLOUT | POLLERR | POLLHUP)) more_writes = op_queue_[write_op].perform_operations(descriptor, ops); else more_writes = op_queue_[write_op].has_operation(descriptor); if ((events[i].events & (POLLERR | POLLHUP)) != 0 && !more_except && !more_reads && !more_writes) { // If we have an event and no operations associated with the // descriptor then we need to delete the descriptor from /dev/poll. // The poll operation can produce POLLHUP or POLLERR events when there // is no operation pending, so if we do not remove the descriptor we // can end up in a tight polling loop. ::pollfd ev = { 0, 0, 0 }; ev.fd = descriptor; ev.events = POLLREMOVE; ev.revents = 0; ::write(dev_poll_fd_, &ev, sizeof(ev)); } else { ::pollfd ev = { 0, 0, 0 }; ev.fd = descriptor; ev.events = POLLERR | POLLHUP; if (more_reads) ev.events |= POLLIN; if (more_writes) ev.events |= POLLOUT; if (more_except) ev.events |= POLLPRI; ev.revents = 0; int result = ::write(dev_poll_fd_, &ev, sizeof(ev)); if (result != sizeof(ev)) { asio::error_code ec(errno, asio::error::get_system_category()); for (int j = 0; j < max_ops; ++j) op_queue_[j].cancel_operations(descriptor, ops, ec); } } } } timer_queues_.get_ready_timers(ops); } void dev_poll_reactor::interrupt() { interrupter_.interrupt(); } int dev_poll_reactor::do_dev_poll_create() { int fd = ::open("/dev/poll", O_RDWR); if (fd == -1) { asio::error_code ec(errno, asio::error::get_system_category()); asio::detail::throw_error(ec, "/dev/poll"); } return fd; } void dev_poll_reactor::do_add_timer_queue(timer_queue_base& queue) { mutex::scoped_lock lock(mutex_); timer_queues_.insert(&queue); } void dev_poll_reactor::do_remove_timer_queue(timer_queue_base& queue) { mutex::scoped_lock lock(mutex_); timer_queues_.erase(&queue); } int dev_poll_reactor::get_timeout() { // By default we will wait no longer than 5 minutes. This will ensure that // any changes to the system clock are detected after no longer than this. return timer_queues_.wait_duration_msec(5 * 60 * 1000); } void dev_poll_reactor::cancel_ops_unlocked(socket_type descriptor, const asio::error_code& ec) { bool need_interrupt = false; op_queue ops; for (int i = 0; i < max_ops; ++i) need_interrupt = op_queue_[i].cancel_operations( descriptor, ops, ec) || need_interrupt; io_service_.post_deferred_completions(ops); if (need_interrupt) interrupter_.interrupt(); } ::pollfd& dev_poll_reactor::add_pending_event_change(int descriptor) { hash_map::iterator iter = pending_event_change_index_.find(descriptor); if (iter == pending_event_change_index_.end()) { std::size_t index = pending_event_changes_.size(); pending_event_changes_.reserve(pending_event_changes_.size() + 1); pending_event_change_index_.insert(std::make_pair(descriptor, index)); pending_event_changes_.push_back(::pollfd()); pending_event_changes_[index].fd = descriptor; pending_event_changes_[index].revents = 0; return pending_event_changes_[index]; } else { return pending_event_changes_[iter->second]; } } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_DEV_POLL) #endif // ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/epoll_reactor.hpp0000644000000000000000000000353712247075736026316 0ustar rootroot00000000000000// // detail/impl/epoll_reactor.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_EPOLL_REACTOR_HPP #define ASIO_DETAIL_IMPL_EPOLL_REACTOR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #if defined(ASIO_HAS_EPOLL) #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template void epoll_reactor::add_timer_queue(timer_queue& queue) { do_add_timer_queue(queue); } template void epoll_reactor::remove_timer_queue(timer_queue& queue) { do_remove_timer_queue(queue); } template void epoll_reactor::schedule_timer(timer_queue& queue, const typename Time_Traits::time_type& time, typename timer_queue::per_timer_data& timer, timer_op* op) { mutex::scoped_lock lock(mutex_); if (shutdown_) { io_service_.post_immediate_completion(op); return; } bool earliest = queue.enqueue_timer(time, timer, op); io_service_.work_started(); if (earliest) update_timeout(); } template std::size_t epoll_reactor::cancel_timer(timer_queue& queue, typename timer_queue::per_timer_data& timer) { mutex::scoped_lock lock(mutex_); op_queue ops; std::size_t n = queue.cancel_timer(timer, ops); lock.unlock(); io_service_.post_deferred_completions(ops); return n; } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_EPOLL) #endif // ASIO_DETAIL_IMPL_EPOLL_REACTOR_HPP percona-xtradb-cluster-galera/asio/asio/detail/impl/epoll_reactor.ipp0000644000000000000000000002327712247075736026322 0ustar rootroot00000000000000// // detail/impl/epoll_reactor.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_EPOLL_REACTOR_IPP #define ASIO_DETAIL_IMPL_EPOLL_REACTOR_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_EPOLL) #include #include #include "asio/detail/epoll_reactor.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #if defined(ASIO_HAS_TIMERFD) # include #endif // defined(ASIO_HAS_TIMERFD) #include "asio/detail/push_options.hpp" namespace asio { namespace detail { epoll_reactor::epoll_reactor(asio::io_service& io_service) : asio::detail::service_base(io_service), io_service_(use_service(io_service)), mutex_(), epoll_fd_(do_epoll_create()), #if defined(ASIO_HAS_TIMERFD) timer_fd_(timerfd_create(CLOCK_MONOTONIC, 0)), #else // defined(ASIO_HAS_TIMERFD) timer_fd_(-1), #endif // defined(ASIO_HAS_TIMERFD) interrupter_(), shutdown_(false) { // Add the interrupter's descriptor to epoll. epoll_event ev = { 0, { 0 } }; ev.events = EPOLLIN | EPOLLERR | EPOLLET; ev.data.ptr = &interrupter_; epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, interrupter_.read_descriptor(), &ev); interrupter_.interrupt(); // Add the timer descriptor to epoll. if (timer_fd_ != -1) { ev.events = EPOLLIN | EPOLLERR; ev.data.ptr = &timer_fd_; epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, timer_fd_, &ev); } } epoll_reactor::~epoll_reactor() { close(epoll_fd_); if (timer_fd_ != -1) close(timer_fd_); } void epoll_reactor::shutdown_service() { mutex::scoped_lock lock(mutex_); shutdown_ = true; lock.unlock(); op_queue ops; while (descriptor_state* state = registered_descriptors_.first()) { for (int i = 0; i < max_ops; ++i) ops.push(state->op_queue_[i]); state->shutdown_ = true; registered_descriptors_.free(state); } timer_queues_.get_all_timers(ops); } void epoll_reactor::init_task() { io_service_.init_task(); } int epoll_reactor::register_descriptor(socket_type descriptor, epoll_reactor::per_descriptor_data& descriptor_data) { mutex::scoped_lock lock(registered_descriptors_mutex_); descriptor_data = registered_descriptors_.alloc(); descriptor_data->shutdown_ = false; lock.unlock(); epoll_event ev = { 0, { 0 } }; ev.events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLOUT | EPOLLPRI | EPOLLET; ev.data.ptr = descriptor_data; int result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev); if (result != 0) return errno; return 0; } void epoll_reactor::start_op(int op_type, socket_type descriptor, epoll_reactor::per_descriptor_data& descriptor_data, reactor_op* op, bool allow_speculative) { if (!descriptor_data) { op->ec_ = asio::error::bad_descriptor; post_immediate_completion(op); return; } mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); if (descriptor_data->shutdown_) { post_immediate_completion(op); return; } if (descriptor_data->op_queue_[op_type].empty()) { if (allow_speculative && (op_type != read_op || descriptor_data->op_queue_[except_op].empty())) { if (op->perform()) { descriptor_lock.unlock(); io_service_.post_immediate_completion(op); return; } } else { epoll_event ev = { 0, { 0 } }; ev.events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLOUT | EPOLLPRI | EPOLLET; ev.data.ptr = descriptor_data; epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); } } descriptor_data->op_queue_[op_type].push(op); io_service_.work_started(); } void epoll_reactor::cancel_ops(socket_type, epoll_reactor::per_descriptor_data& descriptor_data) { if (!descriptor_data) return; mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); op_queue ops; for (int i = 0; i < max_ops; ++i) { while (reactor_op* op = descriptor_data->op_queue_[i].front()) { op->ec_ = asio::error::operation_aborted; descriptor_data->op_queue_[i].pop(); ops.push(op); } } descriptor_lock.unlock(); io_service_.post_deferred_completions(ops); } void epoll_reactor::close_descriptor(socket_type, epoll_reactor::per_descriptor_data& descriptor_data) { if (!descriptor_data) return; mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); if (!descriptor_data->shutdown_) { // Remove the descriptor from the set of known descriptors. The descriptor // will be automatically removed from the epoll set when it is closed. op_queue ops; for (int i = 0; i < max_ops; ++i) { while (reactor_op* op = descriptor_data->op_queue_[i].front()) { op->ec_ = asio::error::operation_aborted; descriptor_data->op_queue_[i].pop(); ops.push(op); } } descriptor_data->shutdown_ = true; descriptor_lock.unlock(); registered_descriptors_.free(descriptor_data); descriptor_data = 0; descriptors_lock.unlock(); io_service_.post_deferred_completions(ops); } } void epoll_reactor::run(bool block, op_queue& ops) { // Calculate a timeout only if timerfd is not used. int timeout; if (timer_fd_ != -1) timeout = block ? -1 : 0; else { mutex::scoped_lock lock(mutex_); timeout = block ? get_timeout() : 0; } // Block on the epoll descriptor. epoll_event events[128]; int num_events = epoll_wait(epoll_fd_, events, 128, timeout); #if defined(ASIO_HAS_TIMERFD) bool check_timers = (timer_fd_ == -1); #else // defined(ASIO_HAS_TIMERFD) bool check_timers = true; #endif // defined(ASIO_HAS_TIMERFD) // Dispatch the waiting events. for (int i = 0; i < num_events; ++i) { void* ptr = events[i].data.ptr; if (ptr == &interrupter_) { // No need to reset the interrupter since we're leaving the descriptor // in a ready-to-read state and relying on edge-triggered notifications // to make it so that we only get woken up when the descriptor's epoll // registration is updated. #if defined(ASIO_HAS_TIMERFD) if (timer_fd_ == -1) check_timers = true; #else // defined(ASIO_HAS_TIMERFD) check_timers = true; #endif // defined(ASIO_HAS_TIMERFD) } #if defined(ASIO_HAS_TIMERFD) else if (ptr == &timer_fd_) { check_timers = true; } #endif // defined(ASIO_HAS_TIMERFD) else { descriptor_state* descriptor_data = static_cast(ptr); mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); // Exception operations must be processed first to ensure that any // out-of-band data is read before normal data. static const int flag[max_ops] = { EPOLLIN, EPOLLOUT, EPOLLPRI }; for (int j = max_ops - 1; j >= 0; --j) { if (events[i].events & (flag[j] | EPOLLERR | EPOLLHUP)) { while (reactor_op* op = descriptor_data->op_queue_[j].front()) { if (op->perform()) { descriptor_data->op_queue_[j].pop(); ops.push(op); } else break; } } } } } if (check_timers) { mutex::scoped_lock common_lock(mutex_); timer_queues_.get_ready_timers(ops); #if defined(ASIO_HAS_TIMERFD) if (timer_fd_ != -1) { itimerspec new_timeout; itimerspec old_timeout; int flags = get_timeout(new_timeout); timerfd_settime(timer_fd_, flags, &new_timeout, &old_timeout); } #endif // defined(ASIO_HAS_TIMERFD) } } void epoll_reactor::interrupt() { epoll_event ev = { 0, { 0 } }; ev.events = EPOLLIN | EPOLLERR | EPOLLET; ev.data.ptr = &interrupter_; epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, interrupter_.read_descriptor(), &ev); } int epoll_reactor::do_epoll_create() { int fd = epoll_create(epoll_size); if (fd == -1) { asio::error_code ec(errno, asio::error::get_system_category()); asio::detail::throw_error(ec, "epoll"); } return fd; } void epoll_reactor::do_add_timer_queue(timer_queue_base& queue) { mutex::scoped_lock lock(mutex_); timer_queues_.insert(&queue); } void epoll_reactor::do_remove_timer_queue(timer_queue_base& queue) { mutex::scoped_lock lock(mutex_); timer_queues_.erase(&queue); } void epoll_reactor::update_timeout() { #if defined(ASIO_HAS_TIMERFD) if (timer_fd_ != -1) { itimerspec new_timeout; itimerspec old_timeout; int flags = get_timeout(new_timeout); timerfd_settime(timer_fd_, flags, &new_timeout, &old_timeout); return; } #endif // defined(ASIO_HAS_TIMERFD) interrupt(); } int epoll_reactor::get_timeout() { // By default we will wait no longer than 5 minutes. This will ensure that // any changes to the system clock are detected after no longer than this. return timer_queues_.wait_duration_msec(5 * 60 * 1000); } #if defined(ASIO_HAS_TIMERFD) int epoll_reactor::get_timeout(itimerspec& ts) { ts.it_interval.tv_sec = 0; ts.it_interval.tv_nsec = 0; long usec = timer_queues_.wait_duration_usec(5 * 60 * 1000 * 1000); ts.it_value.tv_sec = usec / 1000000; ts.it_value.tv_nsec = usec ? (usec % 1000000) * 1000 : 1; return usec ? 0 : TFD_TIMER_ABSTIME; } #endif // defined(ASIO_HAS_TIMERFD) } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_EPOLL) #endif // ASIO_DETAIL_IMPL_EPOLL_REACTOR_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/eventfd_select_interrupter.ipp0000644000000000000000000000654512247075736031124 0ustar rootroot00000000000000// // detail/impl/eventfd_select_interrupter.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2008 Roelof Naude (roelof.naude at gmail dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_EVENTFD_SELECT_INTERRUPTER_IPP #define ASIO_DETAIL_IMPL_EVENTFD_SELECT_INTERRUPTER_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_EVENTFD) #include #include #include #if __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 # include #else // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 # include #endif // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 #include "asio/detail/eventfd_select_interrupter.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { eventfd_select_interrupter::eventfd_select_interrupter() { #if __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 write_descriptor_ = read_descriptor_ = syscall(__NR_eventfd, 0); #else // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 write_descriptor_ = read_descriptor_ = ::eventfd(0, 0); #endif // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 if (read_descriptor_ != -1) { ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK); } else { int pipe_fds[2]; if (pipe(pipe_fds) == 0) { read_descriptor_ = pipe_fds[0]; ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK); write_descriptor_ = pipe_fds[1]; ::fcntl(write_descriptor_, F_SETFL, O_NONBLOCK); } else { asio::error_code ec(errno, asio::error::get_system_category()); asio::detail::throw_error(ec, "eventfd_select_interrupter"); } } } eventfd_select_interrupter::~eventfd_select_interrupter() { if (write_descriptor_ != -1 && write_descriptor_ != read_descriptor_) ::close(write_descriptor_); if (read_descriptor_ != -1) ::close(read_descriptor_); } void eventfd_select_interrupter::interrupt() { uint64_t counter(1UL); int result = ::write(write_descriptor_, &counter, sizeof(uint64_t)); (void)result; } bool eventfd_select_interrupter::reset() { if (write_descriptor_ == read_descriptor_) { for (;;) { // Only perform one read. The kernel maintains an atomic counter. uint64_t counter(0); errno = 0; int bytes_read = ::read(read_descriptor_, &counter, sizeof(uint64_t)); if (bytes_read < 0 && errno == EINTR) continue; bool was_interrupted = (bytes_read > 0); return was_interrupted; } } else { for (;;) { // Clear all data from the pipe. char data[1024]; int bytes_read = ::read(read_descriptor_, data, sizeof(data)); if (bytes_read < 0 && errno == EINTR) continue; bool was_interrupted = (bytes_read > 0); while (bytes_read == sizeof(data)) bytes_read = ::read(read_descriptor_, data, sizeof(data)); return was_interrupted; } } } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_EVENTFD) #endif // ASIO_DETAIL_IMPL_EVENTFD_SELECT_INTERRUPTER_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/kqueue_reactor.hpp0000644000000000000000000000401312247075736026470 0ustar rootroot00000000000000// // detail/impl/kqueue_reactor.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2005 Stefan Arentz (stefan at soze dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_KQUEUE_REACTOR_HPP #define ASIO_DETAIL_IMPL_KQUEUE_REACTOR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_KQUEUE) #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template void kqueue_reactor::add_timer_queue(timer_queue& queue) { do_add_timer_queue(queue); } // Remove a timer queue from the reactor. template void kqueue_reactor::remove_timer_queue(timer_queue& queue) { do_remove_timer_queue(queue); } template void kqueue_reactor::schedule_timer(timer_queue& queue, const typename Time_Traits::time_type& time, typename timer_queue::per_timer_data& timer, timer_op* op) { asio::detail::mutex::scoped_lock lock(mutex_); if (shutdown_) { io_service_.post_immediate_completion(op); return; } bool earliest = queue.enqueue_timer(time, timer, op); io_service_.work_started(); if (earliest) interrupt(); } template std::size_t kqueue_reactor::cancel_timer(timer_queue& queue, typename timer_queue::per_timer_data& timer) { asio::detail::mutex::scoped_lock lock(mutex_); op_queue ops; std::size_t n = queue.cancel_timer(timer, ops); lock.unlock(); io_service_.post_deferred_completions(ops); return n; } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_KQUEUE) #endif // ASIO_DETAIL_IMPL_KQUEUE_REACTOR_HPP percona-xtradb-cluster-galera/asio/asio/detail/impl/kqueue_reactor.ipp0000644000000000000000000002427212247075736026502 0ustar rootroot00000000000000// // detail/impl/kqueue_reactor.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2005 Stefan Arentz (stefan at soze dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_KQUEUE_REACTOR_IPP #define ASIO_DETAIL_IMPL_KQUEUE_REACTOR_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_KQUEUE) #include "asio/detail/kqueue_reactor.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" #if defined(__NetBSD__) # define ASIO_KQUEUE_EV_SET(ev, ident, filt, flags, fflags, data, udata) \ EV_SET(ev, ident, filt, flags, fflags, \ data, reinterpret_cast(udata)) #else # define ASIO_KQUEUE_EV_SET(ev, ident, filt, flags, fflags, data, udata) \ EV_SET(ev, ident, filt, flags, fflags, data, udata) #endif namespace asio { namespace detail { kqueue_reactor::kqueue_reactor(asio::io_service& io_service) : asio::detail::service_base(io_service), io_service_(use_service(io_service)), mutex_(), kqueue_fd_(do_kqueue_create()), interrupter_(), shutdown_(false) { // The interrupter is put into a permanently readable state. Whenever we // want to interrupt the blocked kevent call we register a one-shot read // operation against the descriptor. interrupter_.interrupt(); } kqueue_reactor::~kqueue_reactor() { close(kqueue_fd_); } void kqueue_reactor::shutdown_service() { mutex::scoped_lock lock(mutex_); shutdown_ = true; lock.unlock(); op_queue ops; while (descriptor_state* state = registered_descriptors_.first()) { for (int i = 0; i < max_ops; ++i) ops.push(state->op_queue_[i]); state->shutdown_ = true; registered_descriptors_.free(state); } timer_queues_.get_all_timers(ops); } void kqueue_reactor::init_task() { io_service_.init_task(); } int kqueue_reactor::register_descriptor(socket_type, kqueue_reactor::per_descriptor_data& descriptor_data) { mutex::scoped_lock lock(registered_descriptors_mutex_); descriptor_data = registered_descriptors_.alloc(); descriptor_data->shutdown_ = false; return 0; } void kqueue_reactor::start_op(int op_type, socket_type descriptor, kqueue_reactor::per_descriptor_data& descriptor_data, reactor_op* op, bool allow_speculative) { if (!descriptor_data) { op->ec_ = asio::error::bad_descriptor; post_immediate_completion(op); return; } mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); if (descriptor_data->shutdown_) { post_immediate_completion(op); return; } bool first = descriptor_data->op_queue_[op_type].empty(); if (first) { if (allow_speculative) { if (op_type != read_op || descriptor_data->op_queue_[except_op].empty()) { if (op->perform()) { descriptor_lock.unlock(); io_service_.post_immediate_completion(op); return; } } } } descriptor_data->op_queue_[op_type].push(op); io_service_.work_started(); if (first) { struct kevent event; switch (op_type) { case read_op: ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, descriptor_data); break; case write_op: ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, descriptor_data); break; case except_op: if (!descriptor_data->op_queue_[read_op].empty()) return; // Already registered for read events. ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_READ, EV_ADD | EV_ONESHOT, EV_OOBAND, 0, descriptor_data); break; } if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) { op->ec_ = asio::error_code(errno, asio::error::get_system_category()); descriptor_data->op_queue_[op_type].pop(); io_service_.post_deferred_completion(op); } } } void kqueue_reactor::cancel_ops(socket_type, kqueue_reactor::per_descriptor_data& descriptor_data) { if (!descriptor_data) return; mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); op_queue ops; for (int i = 0; i < max_ops; ++i) { while (reactor_op* op = descriptor_data->op_queue_[i].front()) { op->ec_ = asio::error::operation_aborted; descriptor_data->op_queue_[i].pop(); ops.push(op); } } descriptor_lock.unlock(); io_service_.post_deferred_completions(ops); } void kqueue_reactor::close_descriptor(socket_type, kqueue_reactor::per_descriptor_data& descriptor_data) { if (!descriptor_data) return; mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); if (!descriptor_data->shutdown_) { // Remove the descriptor from the set of known descriptors. The descriptor // will be automatically removed from the kqueue set when it is closed. op_queue ops; for (int i = 0; i < max_ops; ++i) { while (reactor_op* op = descriptor_data->op_queue_[i].front()) { op->ec_ = asio::error::operation_aborted; descriptor_data->op_queue_[i].pop(); ops.push(op); } } descriptor_data->shutdown_ = true; descriptor_lock.unlock(); registered_descriptors_.free(descriptor_data); descriptor_data = 0; descriptors_lock.unlock(); io_service_.post_deferred_completions(ops); } } void kqueue_reactor::run(bool block, op_queue& ops) { mutex::scoped_lock lock(mutex_); // Determine how long to block while waiting for events. timespec timeout_buf = { 0, 0 }; timespec* timeout = block ? get_timeout(timeout_buf) : &timeout_buf; lock.unlock(); // Block on the kqueue descriptor. struct kevent events[128]; int num_events = kevent(kqueue_fd_, 0, 0, events, 128, timeout); // Dispatch the waiting events. for (int i = 0; i < num_events; ++i) { int descriptor = events[i].ident; void* ptr = reinterpret_cast(events[i].udata); if (ptr == &interrupter_) { // No need to reset the interrupter since we're leaving the descriptor // in a ready-to-read state and relying on one-shot notifications. } else { descriptor_state* descriptor_data = static_cast(ptr); mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); // Exception operations must be processed first to ensure that any // out-of-band data is read before normal data. #if defined(__NetBSD__) static const unsigned int filter[max_ops] = #else static const int filter[max_ops] = #endif { EVFILT_READ, EVFILT_WRITE, EVFILT_READ }; for (int j = max_ops - 1; j >= 0; --j) { if (events[i].filter == filter[j]) { if (j != except_op || events[i].flags & EV_OOBAND) { while (reactor_op* op = descriptor_data->op_queue_[j].front()) { if (events[i].flags & EV_ERROR) { op->ec_ = asio::error_code(events[i].data, asio::error::get_system_category()); descriptor_data->op_queue_[j].pop(); ops.push(op); } if (op->perform()) { descriptor_data->op_queue_[j].pop(); ops.push(op); } else break; } } } } // Renew registration for event notifications. struct kevent event; switch (events[i].filter) { case EVFILT_READ: if (!descriptor_data->op_queue_[read_op].empty()) ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, descriptor_data); else if (!descriptor_data->op_queue_[except_op].empty()) ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_READ, EV_ADD | EV_ONESHOT, EV_OOBAND, 0, descriptor_data); else continue; case EVFILT_WRITE: if (!descriptor_data->op_queue_[write_op].empty()) ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, descriptor_data); else continue; default: break; } if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) { asio::error_code error(errno, asio::error::get_system_category()); for (int j = 0; j < max_ops; ++j) { while (reactor_op* op = descriptor_data->op_queue_[j].front()) { op->ec_ = error; descriptor_data->op_queue_[j].pop(); ops.push(op); } } } } } lock.lock(); timer_queues_.get_ready_timers(ops); } void kqueue_reactor::interrupt() { struct kevent event; ASIO_KQUEUE_EV_SET(&event, interrupter_.read_descriptor(), EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, &interrupter_); ::kevent(kqueue_fd_, &event, 1, 0, 0, 0); } int kqueue_reactor::do_kqueue_create() { int fd = ::kqueue(); if (fd == -1) { asio::error_code ec(errno, asio::error::get_system_category()); asio::detail::throw_error(ec, "kqueue"); } return fd; } void kqueue_reactor::do_add_timer_queue(timer_queue_base& queue) { mutex::scoped_lock lock(mutex_); timer_queues_.insert(&queue); } void kqueue_reactor::do_remove_timer_queue(timer_queue_base& queue) { mutex::scoped_lock lock(mutex_); timer_queues_.erase(&queue); } timespec* kqueue_reactor::get_timeout(timespec& ts) { // By default we will wait no longer than 5 minutes. This will ensure that // any changes to the system clock are detected after no longer than this. long usec = timer_queues_.wait_duration_usec(5 * 60 * 1000 * 1000); ts.tv_sec = usec / 1000000; ts.tv_nsec = (usec % 1000000) * 1000; return &ts; } } // namespace detail } // namespace asio #undef ASIO_KQUEUE_EV_SET #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_KQUEUE) #endif // ASIO_DETAIL_IMPL_KQUEUE_REACTOR_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/pipe_select_interrupter.ipp0000644000000000000000000000452512247075736030422 0ustar rootroot00000000000000// // detail/impl/pipe_select_interrupter.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_PIPE_SELECT_INTERRUPTER_IPP #define ASIO_DETAIL_IMPL_PIPE_SELECT_INTERRUPTER_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(BOOST_WINDOWS) #if !defined(__CYGWIN__) #if !defined(__SYMBIAN32__) #if !defined(ASIO_HAS_EVENTFD) #include #include #include #include #include "asio/detail/pipe_select_interrupter.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { pipe_select_interrupter::pipe_select_interrupter() { int pipe_fds[2]; if (pipe(pipe_fds) == 0) { read_descriptor_ = pipe_fds[0]; ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK); write_descriptor_ = pipe_fds[1]; ::fcntl(write_descriptor_, F_SETFL, O_NONBLOCK); } else { asio::error_code ec(errno, asio::error::get_system_category()); asio::detail::throw_error(ec, "pipe_select_interrupter"); } } pipe_select_interrupter::~pipe_select_interrupter() { if (read_descriptor_ != -1) ::close(read_descriptor_); if (write_descriptor_ != -1) ::close(write_descriptor_); } void pipe_select_interrupter::interrupt() { char byte = 0; int result = ::write(write_descriptor_, &byte, 1); (void)result; } bool pipe_select_interrupter::reset() { for (;;) { char data[1024]; int bytes_read = ::read(read_descriptor_, data, sizeof(data)); if (bytes_read < 0 && errno == EINTR) continue; bool was_interrupted = (bytes_read > 0); while (bytes_read == sizeof(data)) bytes_read = ::read(read_descriptor_, data, sizeof(data)); return was_interrupted; } } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // !defined(ASIO_HAS_EVENTFD) #endif // !defined(__SYMBIAN32__) #endif // !defined(__CYGWIN__) #endif // !defined(BOOST_WINDOWS) #endif // ASIO_DETAIL_IMPL_PIPE_SELECT_INTERRUPTER_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/posix_event.ipp0000644000000000000000000000222312247075736026017 0ustar rootroot00000000000000// // detail/impl/posix_event.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_POSIX_EVENT_IPP #define ASIO_DETAIL_IMPL_POSIX_EVENT_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) #include "asio/detail/posix_event.hpp" #include "asio/detail/throw_error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { posix_event::posix_event() : signalled_(false) { int error = ::pthread_cond_init(&cond_, 0); asio::error_code ec(error, asio::error::get_system_category()); asio::detail::throw_error(ec, "event"); } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) #endif // ASIO_DETAIL_IMPL_POSIX_EVENT_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/posix_mutex.ipp0000644000000000000000000000223112247075736026037 0ustar rootroot00000000000000// // detail/impl/posix_mutex.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_POSIX_MUTEX_IPP #define ASIO_DETAIL_IMPL_POSIX_MUTEX_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) #include "asio/detail/posix_mutex.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { posix_mutex::posix_mutex() { int error = ::pthread_mutex_init(&mutex_, 0); asio::error_code ec(error, asio::error::get_system_category()); asio::detail::throw_error(ec, "mutex"); } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) #endif // ASIO_DETAIL_IMPL_POSIX_MUTEX_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/posix_thread.ipp0000644000000000000000000000320612247075736026147 0ustar rootroot00000000000000// // detail/impl/posix_thread.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_POSIX_THREAD_IPP #define ASIO_DETAIL_IMPL_POSIX_THREAD_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) #include "asio/detail/posix_thread.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { posix_thread::~posix_thread() { if (!joined_) ::pthread_detach(thread_); } void posix_thread::join() { if (!joined_) { ::pthread_join(thread_, 0); joined_ = true; } } void posix_thread::start_thread(func_base* arg) { int error = ::pthread_create(&thread_, 0, asio_detail_posix_thread_function, arg); if (error != 0) { delete arg; asio::error_code ec(error, asio::error::get_system_category()); asio::detail::throw_error(ec, "thread"); } } void* asio_detail_posix_thread_function(void* arg) { posix_thread::auto_func_base_ptr func = { static_cast(arg) }; func.ptr->run(); return 0; } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) #endif // ASIO_DETAIL_IMPL_POSIX_THREAD_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/posix_tss_ptr.ipp0000644000000000000000000000226312247075736026400 0ustar rootroot00000000000000// // detail/impl/posix_tss_ptr.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_POSIX_TSS_PTR_IPP #define ASIO_DETAIL_IMPL_POSIX_TSS_PTR_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) #include "asio/detail/posix_tss_ptr.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { void posix_tss_ptr_create(pthread_key_t& key) { int error = ::pthread_key_create(&key, 0); asio::error_code ec(error, asio::error::get_system_category()); asio::detail::throw_error(ec, "tss"); } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) #endif // ASIO_DETAIL_IMPL_POSIX_TSS_PTR_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/reactive_descriptor_service.ipp0000644000000000000000000000656212247075736031246 0ustar rootroot00000000000000// // detail/impl/reactive_descriptor_service.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_REACTIVE_DESCRIPTOR_SERVICE_IPP #define ASIO_DETAIL_IMPL_REACTIVE_DESCRIPTOR_SERVICE_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) #include "asio/error.hpp" #include "asio/detail/reactive_descriptor_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { reactive_descriptor_service::reactive_descriptor_service( asio::io_service& io_service) : reactor_(asio::use_service(io_service)) { reactor_.init_task(); } void reactive_descriptor_service::shutdown_service() { } void reactive_descriptor_service::construct( reactive_descriptor_service::implementation_type& impl) { impl.descriptor_ = -1; impl.state_ = 0; } void reactive_descriptor_service::destroy( reactive_descriptor_service::implementation_type& impl) { if (is_open(impl)) reactor_.close_descriptor(impl.descriptor_, impl.reactor_data_); asio::error_code ignored_ec; descriptor_ops::close(impl.descriptor_, impl.state_, ignored_ec); } asio::error_code reactive_descriptor_service::assign( reactive_descriptor_service::implementation_type& impl, const native_type& native_descriptor, asio::error_code& ec) { if (is_open(impl)) { ec = asio::error::already_open; return ec; } if (int err = reactor_.register_descriptor( native_descriptor, impl.reactor_data_)) { ec = asio::error_code(err, asio::error::get_system_category()); return ec; } impl.descriptor_ = native_descriptor; impl.state_ = 0; ec = asio::error_code(); return ec; } asio::error_code reactive_descriptor_service::close( reactive_descriptor_service::implementation_type& impl, asio::error_code& ec) { if (is_open(impl)) reactor_.close_descriptor(impl.descriptor_, impl.reactor_data_); if (descriptor_ops::close(impl.descriptor_, impl.state_, ec) == 0) construct(impl); return ec; } asio::error_code reactive_descriptor_service::cancel( reactive_descriptor_service::implementation_type& impl, asio::error_code& ec) { if (!is_open(impl)) { ec = asio::error::bad_descriptor; return ec; } reactor_.cancel_ops(impl.descriptor_, impl.reactor_data_); ec = asio::error_code(); return ec; } void reactive_descriptor_service::start_op( reactive_descriptor_service::implementation_type& impl, int op_type, reactor_op* op, bool non_blocking, bool noop) { if (!noop) { if ((impl.state_ & descriptor_ops::non_blocking) || descriptor_ops::set_internal_non_blocking( impl.descriptor_, impl.state_, op->ec_)) { reactor_.start_op(op_type, impl.descriptor_, impl.reactor_data_, op, non_blocking); return; } } reactor_.post_immediate_completion(op); } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) #endif // ASIO_DETAIL_IMPL_REACTIVE_DESCRIPTOR_SERVICE_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/reactive_serial_port_service.ipp0000644000000000000000000000764012247075736031411 0ustar rootroot00000000000000// // detail/impl/reactive_serial_port_service.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_REACTIVE_SERIAL_PORT_SERVICE_IPP #define ASIO_DETAIL_IMPL_REACTIVE_SERIAL_PORT_SERVICE_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_SERIAL_PORT) #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) #include #include "asio/detail/reactive_serial_port_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { reactive_serial_port_service::reactive_serial_port_service( asio::io_service& io_service) : descriptor_service_(io_service) { } void reactive_serial_port_service::shutdown_service() { descriptor_service_.shutdown_service(); } asio::error_code reactive_serial_port_service::open( reactive_serial_port_service::implementation_type& impl, const std::string& device, asio::error_code& ec) { if (is_open(impl)) { ec = asio::error::already_open; return ec; } descriptor_ops::state_type state = 0; int fd = descriptor_ops::open(device.c_str(), O_RDWR | O_NONBLOCK | O_NOCTTY, ec); if (fd < 0) return ec; int s = descriptor_ops::fcntl(fd, F_GETFL, ec); if (s >= 0) s = descriptor_ops::fcntl(fd, F_SETFL, s | O_NONBLOCK, ec); if (s < 0) { asio::error_code ignored_ec; descriptor_ops::close(fd, state, ignored_ec); return ec; } // Set up default serial port options. termios ios; errno = 0; s = descriptor_ops::error_wrapper(::tcgetattr(fd, &ios), ec); if (s >= 0) { #if defined(_BSD_SOURCE) ::cfmakeraw(&ios); #else ios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); ios.c_oflag &= ~OPOST; ios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); ios.c_cflag &= ~(CSIZE | PARENB); ios.c_cflag |= CS8; #endif ios.c_iflag |= IGNPAR; ios.c_cflag |= CREAD | CLOCAL; errno = 0; s = descriptor_ops::error_wrapper(::tcsetattr(fd, TCSANOW, &ios), ec); } if (s < 0) { asio::error_code ignored_ec; descriptor_ops::close(fd, state, ignored_ec); return ec; } // We're done. Take ownership of the serial port descriptor. if (descriptor_service_.assign(impl, fd, ec)) { asio::error_code ignored_ec; descriptor_ops::close(fd, state, ignored_ec); } return ec; } asio::error_code reactive_serial_port_service::do_set_option( reactive_serial_port_service::implementation_type& impl, reactive_serial_port_service::store_function_type store, const void* option, asio::error_code& ec) { termios ios; errno = 0; descriptor_ops::error_wrapper(::tcgetattr( descriptor_service_.native(impl), &ios), ec); if (ec) return ec; if (store(option, ios, ec)) return ec; errno = 0; descriptor_ops::error_wrapper(::tcsetattr( descriptor_service_.native(impl), TCSANOW, &ios), ec); return ec; } asio::error_code reactive_serial_port_service::do_get_option( const reactive_serial_port_service::implementation_type& impl, reactive_serial_port_service::load_function_type load, void* option, asio::error_code& ec) const { termios ios; errno = 0; descriptor_ops::error_wrapper(::tcgetattr( descriptor_service_.native(impl), &ios), ec); if (ec) return ec; return load(option, ios, ec); } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) #endif // defined(ASIO_HAS_SERIAL_PORT) #endif // ASIO_DETAIL_IMPL_REACTIVE_SERIAL_PORT_SERVICE_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/reactive_socket_service_base.ipp0000644000000000000000000001263112247075736031344 0ustar rootroot00000000000000// // detail/reactive_socket_service_base.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_REACTIVE_SOCKET_SERVICE_BASE_IPP #define ASIO_DETAIL_IMPL_REACTIVE_SOCKET_SERVICE_BASE_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(ASIO_HAS_IOCP) #include "asio/detail/reactive_socket_service_base.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { reactive_socket_service_base::reactive_socket_service_base( asio::io_service& io_service) : reactor_(use_service(io_service)) { reactor_.init_task(); } void reactive_socket_service_base::shutdown_service() { } void reactive_socket_service_base::construct( reactive_socket_service_base::base_implementation_type& impl) { impl.socket_ = invalid_socket; impl.state_ = 0; } void reactive_socket_service_base::destroy( reactive_socket_service_base::base_implementation_type& impl) { if (impl.socket_ != invalid_socket) { reactor_.close_descriptor(impl.socket_, impl.reactor_data_); asio::error_code ignored_ec; socket_ops::close(impl.socket_, impl.state_, true, ignored_ec); } } asio::error_code reactive_socket_service_base::close( reactive_socket_service_base::base_implementation_type& impl, asio::error_code& ec) { if (is_open(impl)) reactor_.close_descriptor(impl.socket_, impl.reactor_data_); if (socket_ops::close(impl.socket_, impl.state_, true, ec) == 0) construct(impl); return ec; } asio::error_code reactive_socket_service_base::cancel( reactive_socket_service_base::base_implementation_type& impl, asio::error_code& ec) { if (!is_open(impl)) { ec = asio::error::bad_descriptor; return ec; } reactor_.cancel_ops(impl.socket_, impl.reactor_data_); ec = asio::error_code(); return ec; } asio::error_code reactive_socket_service_base::do_open( reactive_socket_service_base::base_implementation_type& impl, int af, int type, int protocol, asio::error_code& ec) { if (is_open(impl)) { ec = asio::error::already_open; return ec; } socket_holder sock(socket_ops::socket(af, type, protocol, ec)); if (sock.get() == invalid_socket) return ec; if (int err = reactor_.register_descriptor(sock.get(), impl.reactor_data_)) { ec = asio::error_code(err, asio::error::get_system_category()); return ec; } impl.socket_ = sock.release(); switch (type) { case SOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break; case SOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break; default: impl.state_ = 0; break; } ec = asio::error_code(); return ec; } asio::error_code reactive_socket_service_base::do_assign( reactive_socket_service_base::base_implementation_type& impl, int type, const reactive_socket_service_base::native_type& native_socket, asio::error_code& ec) { if (is_open(impl)) { ec = asio::error::already_open; return ec; } if (int err = reactor_.register_descriptor( native_socket, impl.reactor_data_)) { ec = asio::error_code(err, asio::error::get_system_category()); return ec; } impl.socket_ = native_socket; switch (type) { case SOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break; case SOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break; default: impl.state_ = 0; break; } ec = asio::error_code(); return ec; } void reactive_socket_service_base::start_op( reactive_socket_service_base::base_implementation_type& impl, int op_type, reactor_op* op, bool non_blocking, bool noop) { if (!noop) { if ((impl.state_ & socket_ops::non_blocking) || socket_ops::set_internal_non_blocking( impl.socket_, impl.state_, op->ec_)) { reactor_.start_op(op_type, impl.socket_, impl.reactor_data_, op, non_blocking); return; } } reactor_.post_immediate_completion(op); } void reactive_socket_service_base::start_accept_op( reactive_socket_service_base::base_implementation_type& impl, reactor_op* op, bool peer_is_open) { if (!peer_is_open) start_op(impl, reactor::read_op, op, true, false); else { op->ec_ = asio::error::already_open; reactor_.post_immediate_completion(op); } } void reactive_socket_service_base::start_connect_op( reactive_socket_service_base::base_implementation_type& impl, reactor_op* op, const socket_addr_type* addr, size_t addrlen) { if ((impl.state_ & socket_ops::non_blocking) || socket_ops::set_internal_non_blocking( impl.socket_, impl.state_, op->ec_)) { if (socket_ops::connect(impl.socket_, addr, addrlen, op->ec_) != 0) { if (op->ec_ == asio::error::in_progress || op->ec_ == asio::error::would_block) { op->ec_ = asio::error_code(); reactor_.start_op(reactor::connect_op, impl.socket_, impl.reactor_data_, op, false); return; } } } reactor_.post_immediate_completion(op); } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // !defined(ASIO_HAS_IOCP) #endif // ASIO_DETAIL_IMPL_REACTIVE_SOCKET_SERVICE_BASE_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/resolver_service_base.ipp0000644000000000000000000000504512247075736030034 0ustar rootroot00000000000000// // detail/impl/resolver_service_base.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_RESOLVER_SERVICE_BASE_IPP #define ASIO_DETAIL_IMPL_RESOLVER_SERVICE_BASE_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/detail/resolver_service_base.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class resolver_service_base::work_io_service_runner { public: work_io_service_runner(asio::io_service& io_service) : io_service_(io_service) {} void operator()() { io_service_.run(); } private: asio::io_service& io_service_; }; resolver_service_base::resolver_service_base( asio::io_service& io_service) : io_service_impl_(asio::use_service(io_service)), work_io_service_(new asio::io_service), work_io_service_impl_(asio::use_service< io_service_impl>(*work_io_service_)), work_(new asio::io_service::work(*work_io_service_)), work_thread_(0) { } resolver_service_base::~resolver_service_base() { shutdown_service(); } void resolver_service_base::shutdown_service() { work_.reset(); if (work_io_service_) { work_io_service_->stop(); if (work_thread_) { work_thread_->join(); work_thread_.reset(); } work_io_service_.reset(); } } void resolver_service_base::construct( resolver_service_base::implementation_type& impl) { impl.reset(static_cast(0), socket_ops::noop_deleter()); } void resolver_service_base::destroy( resolver_service_base::implementation_type&) { } void resolver_service_base::cancel( resolver_service_base::implementation_type& impl) { impl.reset(static_cast(0), socket_ops::noop_deleter()); } void resolver_service_base::start_resolve_op(operation* op) { start_work_thread(); io_service_impl_.work_started(); work_io_service_impl_.post_immediate_completion(op); } void resolver_service_base::start_work_thread() { asio::detail::mutex::scoped_lock lock(mutex_); if (!work_thread_) { work_thread_.reset(new asio::detail::thread( work_io_service_runner(*work_io_service_))); } } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_IMPL_RESOLVER_SERVICE_BASE_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/select_reactor.hpp0000644000000000000000000000431312247075736026453 0ustar rootroot00000000000000// // detail/impl/select_reactor.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_SELECT_REACTOR_HPP #define ASIO_DETAIL_IMPL_SELECT_REACTOR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) \ || (!defined(ASIO_HAS_DEV_POLL) \ && !defined(ASIO_HAS_EPOLL) \ && !defined(ASIO_HAS_KQUEUE)) #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template void select_reactor::add_timer_queue(timer_queue& queue) { do_add_timer_queue(queue); } // Remove a timer queue from the reactor. template void select_reactor::remove_timer_queue(timer_queue& queue) { do_remove_timer_queue(queue); } template void select_reactor::schedule_timer(timer_queue& queue, const typename Time_Traits::time_type& time, typename timer_queue::per_timer_data& timer, timer_op* op) { asio::detail::mutex::scoped_lock lock(mutex_); if (shutdown_) { io_service_.post_immediate_completion(op); return; } bool earliest = queue.enqueue_timer(time, timer, op); io_service_.work_started(); if (earliest) interrupter_.interrupt(); } template std::size_t select_reactor::cancel_timer(timer_queue& queue, typename timer_queue::per_timer_data& timer) { asio::detail::mutex::scoped_lock lock(mutex_); op_queue ops; std::size_t n = queue.cancel_timer(timer, ops); lock.unlock(); io_service_.post_deferred_completions(ops); return n; } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_IOCP) // || (!defined(ASIO_HAS_DEV_POLL) // && !defined(ASIO_HAS_EPOLL) // && !defined(ASIO_HAS_KQUEUE)) #endif // ASIO_DETAIL_IMPL_SELECT_REACTOR_HPP percona-xtradb-cluster-galera/asio/asio/detail/impl/select_reactor.ipp0000644000000000000000000001657212247075736026466 0ustar rootroot00000000000000// // detail/impl/select_reactor.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_SELECT_REACTOR_IPP #define ASIO_DETAIL_IMPL_SELECT_REACTOR_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) \ || (!defined(ASIO_HAS_DEV_POLL) \ && !defined(ASIO_HAS_EPOLL) \ && !defined(ASIO_HAS_KQUEUE)) #include "asio/detail/bind_handler.hpp" #include "asio/detail/fd_set_adapter.hpp" #include "asio/detail/select_reactor.hpp" #include "asio/detail/signal_blocker.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { select_reactor::select_reactor(asio::io_service& io_service) : asio::detail::service_base(io_service), io_service_(use_service(io_service)), mutex_(), interrupter_(), #if defined(ASIO_HAS_IOCP) stop_thread_(false), thread_(0), #endif // defined(ASIO_HAS_IOCP) shutdown_(false) { #if defined(ASIO_HAS_IOCP) asio::detail::signal_blocker sb; thread_ = new asio::detail::thread( bind_handler(&select_reactor::call_run_thread, this)); #endif // defined(ASIO_HAS_IOCP) } select_reactor::~select_reactor() { shutdown_service(); } void select_reactor::shutdown_service() { asio::detail::mutex::scoped_lock lock(mutex_); shutdown_ = true; #if defined(ASIO_HAS_IOCP) stop_thread_ = true; #endif // defined(ASIO_HAS_IOCP) lock.unlock(); #if defined(ASIO_HAS_IOCP) if (thread_) { interrupter_.interrupt(); thread_->join(); delete thread_; thread_ = 0; } #endif // defined(ASIO_HAS_IOCP) op_queue ops; for (int i = 0; i < max_ops; ++i) op_queue_[i].get_all_operations(ops); timer_queues_.get_all_timers(ops); } void select_reactor::init_task() { io_service_.init_task(); } int select_reactor::register_descriptor(socket_type, select_reactor::per_descriptor_data&) { return 0; } void select_reactor::start_op(int op_type, socket_type descriptor, select_reactor::per_descriptor_data&, reactor_op* op, bool) { asio::detail::mutex::scoped_lock lock(mutex_); if (shutdown_) { post_immediate_completion(op); return; } bool first = op_queue_[op_type].enqueue_operation(descriptor, op); io_service_.work_started(); if (first) interrupter_.interrupt(); } void select_reactor::cancel_ops(socket_type descriptor, select_reactor::per_descriptor_data&) { asio::detail::mutex::scoped_lock lock(mutex_); cancel_ops_unlocked(descriptor, asio::error::operation_aborted); } void select_reactor::close_descriptor(socket_type descriptor, select_reactor::per_descriptor_data&) { asio::detail::mutex::scoped_lock lock(mutex_); cancel_ops_unlocked(descriptor, asio::error::operation_aborted); } void select_reactor::run(bool block, op_queue& ops) { asio::detail::mutex::scoped_lock lock(mutex_); #if defined(ASIO_HAS_IOCP) // Check if the thread is supposed to stop. if (stop_thread_) return; #endif // defined(ASIO_HAS_IOCP) // Set up the descriptor sets. fd_set_adapter fds[max_select_ops]; fds[read_op].set(interrupter_.read_descriptor()); socket_type max_fd = 0; bool have_work_to_do = !timer_queues_.all_empty(); for (int i = 0; i < max_select_ops; ++i) { have_work_to_do = have_work_to_do || !op_queue_[i].empty(); op_queue_[i].get_descriptors(fds[i], ops); if (fds[i].max_descriptor() > max_fd) max_fd = fds[i].max_descriptor(); } #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) // Connection operations on Windows use both except and write fd_sets. have_work_to_do = have_work_to_do || !op_queue_[connect_op].empty(); op_queue_[connect_op].get_descriptors(fds[write_op], ops); if (fds[write_op].max_descriptor() > max_fd) max_fd = fds[write_op].max_descriptor(); op_queue_[connect_op].get_descriptors(fds[except_op], ops); if (fds[except_op].max_descriptor() > max_fd) max_fd = fds[except_op].max_descriptor(); #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) // We can return immediately if there's no work to do and the reactor is // not supposed to block. if (!block && !have_work_to_do) return; // Determine how long to block while waiting for events. timeval tv_buf = { 0, 0 }; timeval* tv = block ? get_timeout(tv_buf) : &tv_buf; lock.unlock(); // Block on the select call until descriptors become ready. asio::error_code ec; int retval = socket_ops::select(static_cast(max_fd + 1), fds[read_op], fds[write_op], fds[except_op], tv, ec); // Reset the interrupter. if (retval > 0 && fds[read_op].is_set(interrupter_.read_descriptor())) interrupter_.reset(); lock.lock(); // Dispatch all ready operations. if (retval > 0) { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) // Connection operations on Windows use both except and write fd_sets. op_queue_[connect_op].perform_operations_for_descriptors( fds[except_op], ops); op_queue_[connect_op].perform_operations_for_descriptors( fds[write_op], ops); #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) // Exception operations must be processed first to ensure that any // out-of-band data is read before normal data. for (int i = max_select_ops - 1; i >= 0; --i) op_queue_[i].perform_operations_for_descriptors(fds[i], ops); } timer_queues_.get_ready_timers(ops); } void select_reactor::interrupt() { interrupter_.interrupt(); } #if defined(ASIO_HAS_IOCP) void select_reactor::run_thread() { asio::detail::mutex::scoped_lock lock(mutex_); while (!stop_thread_) { lock.unlock(); op_queue ops; run(true, ops); io_service_.post_deferred_completions(ops); lock.lock(); } } void select_reactor::call_run_thread(select_reactor* reactor) { reactor->run_thread(); } #endif // defined(ASIO_HAS_IOCP) void select_reactor::do_add_timer_queue(timer_queue_base& queue) { mutex::scoped_lock lock(mutex_); timer_queues_.insert(&queue); } void select_reactor::do_remove_timer_queue(timer_queue_base& queue) { mutex::scoped_lock lock(mutex_); timer_queues_.erase(&queue); } timeval* select_reactor::get_timeout(timeval& tv) { // By default we will wait no longer than 5 minutes. This will ensure that // any changes to the system clock are detected after no longer than this. long usec = timer_queues_.wait_duration_usec(5 * 60 * 1000 * 1000); tv.tv_sec = usec / 1000000; tv.tv_usec = usec % 1000000; return &tv; } void select_reactor::cancel_ops_unlocked(socket_type descriptor, const asio::error_code& ec) { bool need_interrupt = false; op_queue ops; for (int i = 0; i < max_ops; ++i) need_interrupt = op_queue_[i].cancel_operations( descriptor, ops, ec) || need_interrupt; io_service_.post_deferred_completions(ops); if (need_interrupt) interrupter_.interrupt(); } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_IOCP) // || (!defined(ASIO_HAS_DEV_POLL) // && !defined(ASIO_HAS_EPOLL) // && !defined(ASIO_HAS_KQUEUE)) #endif // ASIO_DETAIL_IMPL_SELECT_REACTOR_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/service_registry.hpp0000644000000000000000000000341012247075736027042 0ustar rootroot00000000000000// // detail/impl/service_registry.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_SERVICE_REGISTRY_HPP #define ASIO_DETAIL_IMPL_SERVICE_REGISTRY_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template Service& service_registry::use_service() { asio::io_service::service::key key; init_key(key, Service::id); factory_type factory = &service_registry::create; return *static_cast(do_use_service(key, factory)); } template void service_registry::add_service(Service* new_service) { asio::io_service::service::key key; init_key(key, Service::id); return do_add_service(key, new_service); } template bool service_registry::has_service() const { asio::io_service::service::key key; init_key(key, Service::id); return do_has_service(key); } #if !defined(ASIO_NO_TYPEID) template void service_registry::init_key(asio::io_service::service::key& key, const asio::detail::service_id& /*id*/) { key.type_info_ = &typeid(typeid_wrapper); key.id_ = 0; } #endif // !defined(ASIO_NO_TYPEID) template asio::io_service::service* service_registry::create( asio::io_service& owner) { return new Service(owner); } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_IMPL_SERVICE_REGISTRY_HPP percona-xtradb-cluster-galera/asio/asio/detail/impl/service_registry.ipp0000644000000000000000000001035612247075736027052 0ustar rootroot00000000000000// // detail/impl/service_registry.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_SERVICE_REGISTRY_IPP #define ASIO_DETAIL_IMPL_SERVICE_REGISTRY_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/detail/service_registry.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { service_registry::service_registry(asio::io_service& o) : owner_(o), first_service_(0) { } service_registry::~service_registry() { // Shutdown all services. This must be done in a separate loop before the // services are destroyed since the destructors of user-defined handler // objects may try to access other service objects. asio::io_service::service* service = first_service_; while (service) { service->shutdown_service(); service = service->next_; } // Destroy all services. while (first_service_) { asio::io_service::service* next_service = first_service_->next_; destroy(first_service_); first_service_ = next_service; } } void service_registry::init_key(asio::io_service::service::key& key, const asio::io_service::id& id) { key.type_info_ = 0; key.id_ = &id; } bool service_registry::keys_match( const asio::io_service::service::key& key1, const asio::io_service::service::key& key2) { if (key1.id_ && key2.id_) if (key1.id_ == key2.id_) return true; if (key1.type_info_ && key2.type_info_) if (*key1.type_info_ == *key2.type_info_) return true; return false; } void service_registry::destroy(asio::io_service::service* service) { delete service; } asio::io_service::service* service_registry::do_use_service( const asio::io_service::service::key& key, factory_type factory) { asio::detail::mutex::scoped_lock lock(mutex_); // First see if there is an existing service object with the given key. asio::io_service::service* service = first_service_; while (service) { if (keys_match(service->key_, key)) return service; service = service->next_; } // Create a new service object. The service registry's mutex is not locked // at this time to allow for nested calls into this function from the new // service's constructor. lock.unlock(); auto_service_ptr new_service = { factory(owner_) }; new_service.ptr_->key_ = key; lock.lock(); // Check that nobody else created another service object of the same type // while the lock was released. service = first_service_; while (service) { if (keys_match(service->key_, key)) return service; service = service->next_; } // Service was successfully initialised, pass ownership to registry. new_service.ptr_->next_ = first_service_; first_service_ = new_service.ptr_; new_service.ptr_ = 0; return first_service_; } void service_registry::do_add_service( const asio::io_service::service::key& key, asio::io_service::service* new_service) { if (&owner_ != &new_service->io_service()) boost::throw_exception(invalid_service_owner()); asio::detail::mutex::scoped_lock lock(mutex_); // Check if there is an existing service object with the given key. asio::io_service::service* service = first_service_; while (service) { if (keys_match(service->key_, key)) boost::throw_exception(service_already_exists()); service = service->next_; } // Take ownership of the service object. new_service->key_ = key; new_service->next_ = first_service_; first_service_ = new_service; } bool service_registry::do_has_service( const asio::io_service::service::key& key) const { asio::detail::mutex::scoped_lock lock(mutex_); asio::io_service::service* service = first_service_; while (service) { if (keys_match(service->key_, key)) return true; service = service->next_; } return false; } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_IMPL_SERVICE_REGISTRY_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/socket_ops.ipp0000644000000000000000000023051112247075736025630 0ustar rootroot00000000000000// // detail/impl/socket_ops.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_SOCKET_OPS_IPP #define ASIO_DETAIL_SOCKET_OPS_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include #include #include #include #include #include "asio/detail/socket_ops.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { namespace socket_ops { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) struct msghdr { int msg_namelen; }; #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) #if defined(__hpux) // HP-UX doesn't declare these functions extern "C", so they are declared again // here to avoid linker errors about undefined symbols. extern "C" char* if_indextoname(unsigned int, char*); extern "C" unsigned int if_nametoindex(const char*); #endif // defined(__hpux) inline void clear_last_error() { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) WSASetLastError(0); #else errno = 0; #endif } template inline ReturnType error_wrapper(ReturnType return_value, asio::error_code& ec) { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) ec = asio::error_code(WSAGetLastError(), asio::error::get_system_category()); #else ec = asio::error_code(errno, asio::error::get_system_category()); #endif return return_value; } template inline socket_type call_accept(SockLenType msghdr::*, socket_type s, socket_addr_type* addr, std::size_t* addrlen) { SockLenType tmp_addrlen = addrlen ? (SockLenType)*addrlen : 0; socket_type result = ::accept(s, addr, addrlen ? &tmp_addrlen : 0); if (addrlen) *addrlen = (std::size_t)tmp_addrlen; return result; } socket_type accept(socket_type s, socket_addr_type* addr, std::size_t* addrlen, asio::error_code& ec) { if (s == invalid_socket) { ec = asio::error::bad_descriptor; return invalid_socket; } clear_last_error(); socket_type new_s = error_wrapper(call_accept( &msghdr::msg_namelen, s, addr, addrlen), ec); if (new_s == invalid_socket) return new_s; #if defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__) int optval = 1; int result = error_wrapper(::setsockopt(new_s, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)), ec); if (result != 0) { ::close(new_s); return invalid_socket; } #endif ec = asio::error_code(); return new_s; } socket_type sync_accept(socket_type s, state_type state, socket_addr_type* addr, std::size_t* addrlen, asio::error_code& ec) { // Accept a socket. for (;;) { // Try to complete the operation without blocking. socket_type new_socket = socket_ops::accept(s, addr, addrlen, ec); // Check if operation succeeded. if (new_socket != invalid_socket) return new_socket; // Operation failed. if (ec == asio::error::would_block || ec == asio::error::try_again) { if (state & user_set_non_blocking) return invalid_socket; // Fall through to retry operation. } else if (ec == asio::error::connection_aborted) { if (state & enable_connection_aborted) return invalid_socket; // Fall through to retry operation. } #if defined(EPROTO) else if (ec.value() == EPROTO) { if (state & enable_connection_aborted) return invalid_socket; // Fall through to retry operation. } #endif // defined(EPROTO) else return invalid_socket; // Wait for socket to become ready. if (socket_ops::poll_read(s, ec) < 0) return invalid_socket; } } #if defined(ASIO_HAS_IOCP) void complete_iocp_accept(socket_type s, void* output_buffer, DWORD address_length, socket_addr_type* addr, std::size_t* addrlen, socket_type new_socket, asio::error_code& ec) { // Map non-portable errors to their portable counterparts. if (ec.value() == ERROR_NETNAME_DELETED) ec = asio::error::connection_aborted; if (!ec) { // Get the address of the peer. if (addr && addrlen) { LPSOCKADDR local_addr = 0; int local_addr_length = 0; LPSOCKADDR remote_addr = 0; int remote_addr_length = 0; GetAcceptExSockaddrs(output_buffer, 0, address_length, address_length, &local_addr, &local_addr_length, &remote_addr, &remote_addr_length); if (static_cast(remote_addr_length) > *addrlen) { ec = asio::error::invalid_argument; } else { using namespace std; // For memcpy. memcpy(addr, remote_addr, remote_addr_length); *addrlen = static_cast(remote_addr_length); } } // Need to set the SO_UPDATE_ACCEPT_CONTEXT option so that getsockname // and getpeername will work on the accepted socket. SOCKET update_ctx_param = s; socket_ops::state_type state = 0; socket_ops::setsockopt(new_socket, state, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, &update_ctx_param, sizeof(SOCKET), ec); } } #else // defined(ASIO_HAS_IOCP) bool non_blocking_accept(socket_type s, state_type state, socket_addr_type* addr, std::size_t* addrlen, asio::error_code& ec, socket_type& new_socket) { for (;;) { // Accept the waiting connection. new_socket = socket_ops::accept(s, addr, addrlen, ec); // Check if operation succeeded. if (new_socket != invalid_socket) return true; // Retry operation if interrupted by signal. if (ec == asio::error::interrupted) continue; // Operation failed. if (ec == asio::error::would_block || ec == asio::error::try_again) { if (state & user_set_non_blocking) return true; // Fall through to retry operation. } else if (ec == asio::error::connection_aborted) { if (state & enable_connection_aborted) return true; // Fall through to retry operation. } #if defined(EPROTO) else if (ec.value() == EPROTO) { if (state & enable_connection_aborted) return true; // Fall through to retry operation. } #endif // defined(EPROTO) else return true; return false; } } #endif // defined(ASIO_HAS_IOCP) template inline int call_bind(SockLenType msghdr::*, socket_type s, const socket_addr_type* addr, std::size_t addrlen) { return ::bind(s, addr, (SockLenType)addrlen); } int bind(socket_type s, const socket_addr_type* addr, std::size_t addrlen, asio::error_code& ec) { if (s == invalid_socket) { ec = asio::error::bad_descriptor; return socket_error_retval; } clear_last_error(); int result = error_wrapper(call_bind( &msghdr::msg_namelen, s, addr, addrlen), ec); if (result == 0) ec = asio::error_code(); return result; } int close(socket_type s, state_type& state, bool destruction, asio::error_code& ec) { int result = 0; if (s != invalid_socket) { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) if ((state & non_blocking) && (state & user_set_linger)) { ioctl_arg_type arg = 0; ::ioctlsocket(s, FIONBIO, &arg); state &= ~non_blocking; } #else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) if (state & non_blocking) { #if defined(__SYMBIAN32__) int flags = ::fcntl(s, F_GETFL, 0); if (flags >= 0) ::fcntl(s, F_SETFL, flags & ~O_NONBLOCK); #else // defined(__SYMBIAN32__) ioctl_arg_type arg = 0; ::ioctl(s, FIONBIO, &arg); #endif // defined(__SYMBIAN32__) state &= ~non_blocking; } #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) if (destruction && (state & user_set_linger)) { ::linger opt; opt.l_onoff = 0; opt.l_linger = 0; asio::error_code ignored_ec; socket_ops::setsockopt(s, state, SOL_SOCKET, SO_LINGER, &opt, sizeof(opt), ignored_ec); } clear_last_error(); #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) result = error_wrapper(::closesocket(s), ec); #else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) result = error_wrapper(::close(s), ec); #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) } if (result == 0) ec = asio::error_code(); return result; } bool set_internal_non_blocking(socket_type s, state_type& state, asio::error_code& ec) { if (s == invalid_socket) { ec = asio::error::bad_descriptor; return false; } clear_last_error(); #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) ioctl_arg_type arg = 1; int result = error_wrapper(::ioctlsocket(s, FIONBIO, &arg), ec); #elif defined(__SYMBIAN32__) int result = error_wrapper(::fcntl(s, F_GETFL, 0), ec); if (result >= 0) { clear_last_error(); result = error_wrapper(::fcntl(s, F_SETFL, result | O_NONBLOCK), ec); } #else ioctl_arg_type arg = 1; int result = error_wrapper(::ioctl(s, FIONBIO, &arg), ec); #endif if (result >= 0) { ec = asio::error_code(); state |= internal_non_blocking; return true; } return false; } int shutdown(socket_type s, int what, asio::error_code& ec) { if (s == invalid_socket) { ec = asio::error::bad_descriptor; return socket_error_retval; } clear_last_error(); int result = error_wrapper(::shutdown(s, what), ec); if (result == 0) ec = asio::error_code(); return result; } template inline int call_connect(SockLenType msghdr::*, socket_type s, const socket_addr_type* addr, std::size_t addrlen) { return ::connect(s, addr, (SockLenType)addrlen); } int connect(socket_type s, const socket_addr_type* addr, std::size_t addrlen, asio::error_code& ec) { if (s == invalid_socket) { ec = asio::error::bad_descriptor; return socket_error_retval; } clear_last_error(); int result = error_wrapper(call_connect( &msghdr::msg_namelen, s, addr, addrlen), ec); if (result == 0) ec = asio::error_code(); return result; } void sync_connect(socket_type s, const socket_addr_type* addr, std::size_t addrlen, asio::error_code& ec) { // Perform the connect operation. socket_ops::connect(s, addr, addrlen, ec); if (ec != asio::error::in_progress && ec != asio::error::would_block) { // The connect operation finished immediately. return; } // Wait for socket to become ready. if (socket_ops::poll_connect(s, ec) < 0) return; // Get the error code from the connect operation. int connect_error = 0; size_t connect_error_len = sizeof(connect_error); if (socket_ops::getsockopt(s, 0, SOL_SOCKET, SO_ERROR, &connect_error, &connect_error_len, ec) == socket_error_retval) return; // Return the result of the connect operation. ec = asio::error_code(connect_error, asio::error::get_system_category()); } bool non_blocking_connect(socket_type s, asio::error_code& ec) { // Get the error code from the connect operation. int connect_error = 0; size_t connect_error_len = sizeof(connect_error); if (socket_ops::getsockopt(s, 0, SOL_SOCKET, SO_ERROR, &connect_error, &connect_error_len, ec) == 0) { if (connect_error) { ec = asio::error_code(connect_error, asio::error::get_system_category()); } else ec = asio::error_code(); } return true; } int socketpair(int af, int type, int protocol, socket_type sv[2], asio::error_code& ec) { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) (void)(af); (void)(type); (void)(protocol); (void)(sv); ec = asio::error::operation_not_supported; return socket_error_retval; #else clear_last_error(); int result = error_wrapper(::socketpair(af, type, protocol, sv), ec); if (result == 0) ec = asio::error_code(); return result; #endif } bool sockatmark(socket_type s, asio::error_code& ec) { if (s == invalid_socket) { ec = asio::error::bad_descriptor; return false; } #if defined(SIOCATMARK) ioctl_arg_type value = 0; # if defined(BOOST_WINDOWS) || defined(__CYGWIN__) int result = error_wrapper(::ioctlsocket(s, SIOCATMARK, &value), ec); # else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) int result = error_wrapper(::ioctl(s, SIOCATMARK, &value), ec); # endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) if (result == 0) ec = asio::error_code(); # if defined(ENOTTY) if (ec.value() == ENOTTY) ec = asio::error::not_socket; # endif // defined(ENOTTY) #else // defined(SIOCATMARK) int value = error_wrapper(::sockatmark(s), ec); if (value != -1) ec = asio::error_code(); #endif // defined(SIOCATMARK) return ec ? false : value != 0; } size_t available(socket_type s, asio::error_code& ec) { if (s == invalid_socket) { ec = asio::error::bad_descriptor; return 0; } ioctl_arg_type value = 0; #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) int result = error_wrapper(::ioctlsocket(s, FIONREAD, &value), ec); #else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) int result = error_wrapper(::ioctl(s, FIONREAD, &value), ec); #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) if (result == 0) ec = asio::error_code(); #if defined(ENOTTY) if (ec.value() == ENOTTY) ec = asio::error::not_socket; #endif // defined(ENOTTY) return ec ? static_cast(0) : static_cast(value); } int listen(socket_type s, int backlog, asio::error_code& ec) { if (s == invalid_socket) { ec = asio::error::bad_descriptor; return socket_error_retval; } clear_last_error(); int result = error_wrapper(::listen(s, backlog), ec); if (result == 0) ec = asio::error_code(); return result; } inline void init_buf_iov_base(void*& base, void* addr) { base = addr; } template inline void init_buf_iov_base(T& base, void* addr) { base = static_cast(addr); } #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) typedef WSABUF buf; #else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) typedef iovec buf; #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) void init_buf(buf& b, void* data, size_t size) { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) b.buf = static_cast(data); b.len = static_cast(size); #else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) init_buf_iov_base(b.iov_base, data); b.iov_len = size; #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) } void init_buf(buf& b, const void* data, size_t size) { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) b.buf = static_cast(const_cast(data)); b.len = static_cast(size); #else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) init_buf_iov_base(b.iov_base, const_cast(data)); b.iov_len = size; #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) } inline void init_msghdr_msg_name(void*& name, socket_addr_type* addr) { name = addr; } inline void init_msghdr_msg_name(void*& name, const socket_addr_type* addr) { name = const_cast(addr); } template inline void init_msghdr_msg_name(T& name, socket_addr_type* addr) { name = reinterpret_cast(addr); } template inline void init_msghdr_msg_name(T& name, const socket_addr_type* addr) { name = reinterpret_cast(const_cast(addr)); } int recv(socket_type s, buf* bufs, size_t count, int flags, asio::error_code& ec) { clear_last_error(); #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) // Receive some data. DWORD recv_buf_count = static_cast(count); DWORD bytes_transferred = 0; DWORD recv_flags = flags; int result = error_wrapper(::WSARecv(s, bufs, recv_buf_count, &bytes_transferred, &recv_flags, 0, 0), ec); if (ec.value() == ERROR_NETNAME_DELETED) ec = asio::error::connection_reset; else if (ec.value() == ERROR_PORT_UNREACHABLE) ec = asio::error::connection_refused; if (result != 0) return socket_error_retval; ec = asio::error_code(); return bytes_transferred; #else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) msghdr msg = msghdr(); msg.msg_iov = bufs; msg.msg_iovlen = count; int result = error_wrapper(::recvmsg(s, &msg, flags), ec); if (result >= 0) ec = asio::error_code(); return result; #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) } size_t sync_recv(socket_type s, state_type state, buf* bufs, size_t count, int flags, bool all_empty, asio::error_code& ec) { if (s == invalid_socket) { ec = asio::error::bad_descriptor; return 0; } // A request to read 0 bytes on a stream is a no-op. if (all_empty && (state & stream_oriented)) { ec = asio::error_code(); return 0; } // Read some data. for (;;) { // Try to complete the operation without blocking. int bytes = socket_ops::recv(s, bufs, count, flags, ec); // Check if operation succeeded. if (bytes > 0) return bytes; // Check for EOF. if ((state & stream_oriented) && bytes == 0) { ec = asio::error::eof; return 0; } // Operation failed. if ((state & user_set_non_blocking) || (ec != asio::error::would_block && ec != asio::error::try_again)) return 0; // Wait for socket to become ready. if (socket_ops::poll_read(s, ec) < 0) return 0; } } #if defined(ASIO_HAS_IOCP) void complete_iocp_recv(state_type state, const weak_cancel_token_type& cancel_token, bool all_empty, asio::error_code& ec, size_t bytes_transferred) { // Map non-portable errors to their portable counterparts. if (ec.value() == ERROR_NETNAME_DELETED) { if (cancel_token.expired()) ec = asio::error::operation_aborted; else ec = asio::error::connection_reset; } else if (ec.value() == ERROR_PORT_UNREACHABLE) { ec = asio::error::connection_refused; } // Check for connection closed. else if (!ec && bytes_transferred == 0 && (state & stream_oriented) != 0 && !all_empty) { ec = asio::error::eof; } } #else // defined(ASIO_HAS_IOCP) bool non_blocking_recv(socket_type s, buf* bufs, size_t count, int flags, bool is_stream, asio::error_code& ec, size_t& bytes_transferred) { for (;;) { // Read some data. int bytes = socket_ops::recv(s, bufs, count, flags, ec); // Check for end of stream. if (is_stream && bytes == 0) { ec = asio::error::eof; return true; } // Retry operation if interrupted by signal. if (ec == asio::error::interrupted) continue; // Check if we need to run the operation again. if (ec == asio::error::would_block || ec == asio::error::try_again) return false; // Operation is complete. if (bytes >= 0) { ec = asio::error_code(); bytes_transferred = bytes; } else bytes_transferred = 0; return true; } } #endif // defined(ASIO_HAS_IOCP) int recvfrom(socket_type s, buf* bufs, size_t count, int flags, socket_addr_type* addr, std::size_t* addrlen, asio::error_code& ec) { clear_last_error(); #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) // Receive some data. DWORD recv_buf_count = static_cast(count); DWORD bytes_transferred = 0; DWORD recv_flags = flags; int tmp_addrlen = (int)*addrlen; int result = error_wrapper(::WSARecvFrom(s, bufs, recv_buf_count, &bytes_transferred, &recv_flags, addr, &tmp_addrlen, 0, 0), ec); *addrlen = (std::size_t)tmp_addrlen; if (ec.value() == ERROR_NETNAME_DELETED) ec = asio::error::connection_reset; else if (ec.value() == ERROR_PORT_UNREACHABLE) ec = asio::error::connection_refused; if (result != 0) return socket_error_retval; ec = asio::error_code(); return bytes_transferred; #else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) msghdr msg = msghdr(); init_msghdr_msg_name(msg.msg_name, addr); msg.msg_namelen = *addrlen; msg.msg_iov = bufs; msg.msg_iovlen = count; int result = error_wrapper(::recvmsg(s, &msg, flags), ec); *addrlen = msg.msg_namelen; if (result >= 0) ec = asio::error_code(); return result; #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) } size_t sync_recvfrom(socket_type s, state_type state, buf* bufs, size_t count, int flags, socket_addr_type* addr, std::size_t* addrlen, asio::error_code& ec) { if (s == invalid_socket) { ec = asio::error::bad_descriptor; return 0; } // Read some data. for (;;) { // Try to complete the operation without blocking. int bytes = socket_ops::recvfrom(s, bufs, count, flags, addr, addrlen, ec); // Check if operation succeeded. if (bytes >= 0) return bytes; // Operation failed. if ((state & user_set_non_blocking) || (ec != asio::error::would_block && ec != asio::error::try_again)) return 0; // Wait for socket to become ready. if (socket_ops::poll_read(s, ec) < 0) return 0; } } #if defined(ASIO_HAS_IOCP) void complete_iocp_recvfrom( const weak_cancel_token_type& cancel_token, asio::error_code& ec) { // Map non-portable errors to their portable counterparts. if (ec.value() == ERROR_NETNAME_DELETED) { if (cancel_token.expired()) ec = asio::error::operation_aborted; else ec = asio::error::connection_reset; } else if (ec.value() == ERROR_PORT_UNREACHABLE) { ec = asio::error::connection_refused; } } #else // defined(ASIO_HAS_IOCP) bool non_blocking_recvfrom(socket_type s, buf* bufs, size_t count, int flags, socket_addr_type* addr, std::size_t* addrlen, asio::error_code& ec, size_t& bytes_transferred) { for (;;) { // Read some data. int bytes = socket_ops::recvfrom(s, bufs, count, flags, addr, addrlen, ec); // Retry operation if interrupted by signal. if (ec == asio::error::interrupted) continue; // Check if we need to run the operation again. if (ec == asio::error::would_block || ec == asio::error::try_again) return false; // Operation is complete. if (bytes >= 0) { ec = asio::error_code(); bytes_transferred = bytes; } else bytes_transferred = 0; return true; } } #endif // defined(ASIO_HAS_IOCP) int send(socket_type s, const buf* bufs, size_t count, int flags, asio::error_code& ec) { clear_last_error(); #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) // Send the data. DWORD send_buf_count = static_cast(count); DWORD bytes_transferred = 0; DWORD send_flags = flags; int result = error_wrapper(::WSASend(s, const_cast(bufs), send_buf_count, &bytes_transferred, send_flags, 0, 0), ec); if (ec.value() == ERROR_NETNAME_DELETED) ec = asio::error::connection_reset; else if (ec.value() == ERROR_PORT_UNREACHABLE) ec = asio::error::connection_refused; if (result != 0) return socket_error_retval; ec = asio::error_code(); return bytes_transferred; #else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) msghdr msg = msghdr(); msg.msg_iov = const_cast(bufs); msg.msg_iovlen = count; #if defined(__linux__) flags |= MSG_NOSIGNAL; #endif // defined(__linux__) int result = error_wrapper(::sendmsg(s, &msg, flags), ec); if (result >= 0) ec = asio::error_code(); return result; #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) } size_t sync_send(socket_type s, state_type state, const buf* bufs, size_t count, int flags, bool all_empty, asio::error_code& ec) { if (s == invalid_socket) { ec = asio::error::bad_descriptor; return 0; } // A request to write 0 bytes to a stream is a no-op. if (all_empty && (state & stream_oriented)) { ec = asio::error_code(); return 0; } // Read some data. for (;;) { // Try to complete the operation without blocking. int bytes = socket_ops::send(s, bufs, count, flags, ec); // Check if operation succeeded. if (bytes >= 0) return bytes; // Operation failed. if ((state & user_set_non_blocking) || (ec != asio::error::would_block && ec != asio::error::try_again)) return 0; // Wait for socket to become ready. if (socket_ops::poll_write(s, ec) < 0) return 0; } } #if defined(ASIO_HAS_IOCP) void complete_iocp_send( const weak_cancel_token_type& cancel_token, asio::error_code& ec) { // Map non-portable errors to their portable counterparts. if (ec.value() == ERROR_NETNAME_DELETED) { if (cancel_token.expired()) ec = asio::error::operation_aborted; else ec = asio::error::connection_reset; } else if (ec.value() == ERROR_PORT_UNREACHABLE) { ec = asio::error::connection_refused; } } #else // defined(ASIO_HAS_IOCP) bool non_blocking_send(socket_type s, const buf* bufs, size_t count, int flags, asio::error_code& ec, size_t& bytes_transferred) { for (;;) { // Write some data. int bytes = socket_ops::send(s, bufs, count, flags, ec); // Retry operation if interrupted by signal. if (ec == asio::error::interrupted) continue; // Check if we need to run the operation again. if (ec == asio::error::would_block || ec == asio::error::try_again) return false; // Operation is complete. if (bytes >= 0) { ec = asio::error_code(); bytes_transferred = bytes; } else bytes_transferred = 0; return true; } } #endif // defined(ASIO_HAS_IOCP) int sendto(socket_type s, const buf* bufs, size_t count, int flags, const socket_addr_type* addr, std::size_t addrlen, asio::error_code& ec) { clear_last_error(); #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) // Send the data. DWORD send_buf_count = static_cast(count); DWORD bytes_transferred = 0; int result = error_wrapper(::WSASendTo(s, const_cast(bufs), send_buf_count, &bytes_transferred, flags, addr, static_cast(addrlen), 0, 0), ec); if (ec.value() == ERROR_NETNAME_DELETED) ec = asio::error::connection_reset; else if (ec.value() == ERROR_PORT_UNREACHABLE) ec = asio::error::connection_refused; if (result != 0) return socket_error_retval; ec = asio::error_code(); return bytes_transferred; #else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) msghdr msg = msghdr(); init_msghdr_msg_name(msg.msg_name, addr); msg.msg_namelen = addrlen; msg.msg_iov = const_cast(bufs); msg.msg_iovlen = count; #if defined(__linux__) flags |= MSG_NOSIGNAL; #endif // defined(__linux__) int result = error_wrapper(::sendmsg(s, &msg, flags), ec); if (result >= 0) ec = asio::error_code(); return result; #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) } size_t sync_sendto(socket_type s, state_type state, const buf* bufs, size_t count, int flags, const socket_addr_type* addr, std::size_t addrlen, asio::error_code& ec) { if (s == invalid_socket) { ec = asio::error::bad_descriptor; return 0; } // Write some data. for (;;) { // Try to complete the operation without blocking. int bytes = socket_ops::sendto(s, bufs, count, flags, addr, addrlen, ec); // Check if operation succeeded. if (bytes >= 0) return bytes; // Operation failed. if ((state & user_set_non_blocking) || (ec != asio::error::would_block && ec != asio::error::try_again)) return 0; // Wait for socket to become ready. if (socket_ops::poll_write(s, ec) < 0) return 0; } } #if !defined(ASIO_HAS_IOCP) bool non_blocking_sendto(socket_type s, const buf* bufs, size_t count, int flags, const socket_addr_type* addr, std::size_t addrlen, asio::error_code& ec, size_t& bytes_transferred) { for (;;) { // Write some data. int bytes = socket_ops::sendto(s, bufs, count, flags, addr, addrlen, ec); // Retry operation if interrupted by signal. if (ec == asio::error::interrupted) continue; // Check if we need to run the operation again. if (ec == asio::error::would_block || ec == asio::error::try_again) return false; // Operation is complete. if (bytes >= 0) { ec = asio::error_code(); bytes_transferred = bytes; } else bytes_transferred = 0; return true; } } #endif // !defined(ASIO_HAS_IOCP) socket_type socket(int af, int type, int protocol, asio::error_code& ec) { clear_last_error(); #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) socket_type s = error_wrapper(::WSASocket(af, type, protocol, 0, 0, WSA_FLAG_OVERLAPPED), ec); if (s == invalid_socket) return s; if (af == AF_INET6) { // Try to enable the POSIX default behaviour of having IPV6_V6ONLY set to // false. This will only succeed on Windows Vista and later versions of // Windows, where a dual-stack IPv4/v6 implementation is available. DWORD optval = 0; ::setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&optval), sizeof(optval)); } ec = asio::error_code(); return s; #elif defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__) socket_type s = error_wrapper(::socket(af, type, protocol), ec); if (s == invalid_socket) return s; int optval = 1; int result = error_wrapper(::setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)), ec); if (result != 0) { ::close(s); return invalid_socket; } return s; #else int s = error_wrapper(::socket(af, type, protocol), ec); if (s >= 0) ec = asio::error_code(); return s; #endif } template inline int call_setsockopt(SockLenType msghdr::*, socket_type s, int level, int optname, const void* optval, std::size_t optlen) { return ::setsockopt(s, level, optname, (const char*)optval, (SockLenType)optlen); } int setsockopt(socket_type s, state_type& state, int level, int optname, const void* optval, std::size_t optlen, asio::error_code& ec) { if (s == invalid_socket) { ec = asio::error::bad_descriptor; return socket_error_retval; } if (level == custom_socket_option_level && optname == always_fail_option) { ec = asio::error::invalid_argument; return socket_error_retval; } if (level == custom_socket_option_level && optname == enable_connection_aborted_option) { if (optlen != sizeof(int)) { ec = asio::error::invalid_argument; return socket_error_retval; } if (*static_cast(optval)) state |= enable_connection_aborted; else state &= ~enable_connection_aborted; ec = asio::error_code(); return 0; } if (level == SOL_SOCKET && optname == SO_LINGER) state |= user_set_linger; #if defined(__BORLANDC__) // Mysteriously, using the getsockopt and setsockopt functions directly with // Borland C++ results in incorrect values being set and read. The bug can be // worked around by using function addresses resolved with GetProcAddress. if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) { typedef int (WSAAPI *sso_t)(SOCKET, int, int, const char*, int); if (sso_t sso = (sso_t)::GetProcAddress(winsock_module, "setsockopt")) { clear_last_error(); return error_wrapper(sso(s, level, optname, reinterpret_cast(optval), static_cast(optlen)), ec); } } ec = asio::error::fault; return socket_error_retval; #else // defined(__BORLANDC__) clear_last_error(); int result = error_wrapper(call_setsockopt(&msghdr::msg_namelen, s, level, optname, optval, optlen), ec); if (result == 0) { ec = asio::error_code(); #if defined(__MACH__) && defined(__APPLE__) \ || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) // To implement portable behaviour for SO_REUSEADDR with UDP sockets we // need to also set SO_REUSEPORT on BSD-based platforms. if ((state & datagram_oriented) && level == SOL_SOCKET && optname == SO_REUSEADDR) { call_setsockopt(&msghdr::msg_namelen, s, SOL_SOCKET, SO_REUSEPORT, optval, optlen); } #endif } return result; #endif // defined(__BORLANDC__) } template inline int call_getsockopt(SockLenType msghdr::*, socket_type s, int level, int optname, void* optval, std::size_t* optlen) { SockLenType tmp_optlen = (SockLenType)*optlen; int result = ::getsockopt(s, level, optname, (char*)optval, &tmp_optlen); *optlen = (std::size_t)tmp_optlen; return result; } int getsockopt(socket_type s, state_type state, int level, int optname, void* optval, size_t* optlen, asio::error_code& ec) { if (s == invalid_socket) { ec = asio::error::bad_descriptor; return socket_error_retval; } if (level == custom_socket_option_level && optname == always_fail_option) { ec = asio::error::invalid_argument; return socket_error_retval; } if (level == custom_socket_option_level && optname == enable_connection_aborted_option) { if (*optlen != sizeof(int)) { ec = asio::error::invalid_argument; return socket_error_retval; } *static_cast(optval) = (state & enable_connection_aborted) ? 1 : 0; ec = asio::error_code(); return 0; } #if defined(__BORLANDC__) // Mysteriously, using the getsockopt and setsockopt functions directly with // Borland C++ results in incorrect values being set and read. The bug can be // worked around by using function addresses resolved with GetProcAddress. if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) { typedef int (WSAAPI *gso_t)(SOCKET, int, int, char*, int*); if (gso_t gso = (gso_t)::GetProcAddress(winsock_module, "getsockopt")) { clear_last_error(); int tmp_optlen = static_cast(*optlen); int result = error_wrapper(gso(s, level, optname, reinterpret_cast(optval), &tmp_optlen), ec); *optlen = static_cast(tmp_optlen); if (result != 0 && level == IPPROTO_IPV6 && optname == IPV6_V6ONLY && ec.value() == WSAENOPROTOOPT && *optlen == sizeof(DWORD)) { // Dual-stack IPv4/v6 sockets, and the IPV6_V6ONLY socket option, are // only supported on Windows Vista and later. To simplify program logic // we will fake success of getting this option and specify that the // value is non-zero (i.e. true). This corresponds to the behavior of // IPv6 sockets on Windows platforms pre-Vista. *static_cast(optval) = 1; ec = asio::error_code(); } return result; } } ec = asio::error::fault; return socket_error_retval; #elif defined(BOOST_WINDOWS) || defined(__CYGWIN__) clear_last_error(); int result = error_wrapper(call_getsockopt(&msghdr::msg_namelen, s, level, optname, optval, optlen), ec); if (result != 0 && level == IPPROTO_IPV6 && optname == IPV6_V6ONLY && ec.value() == WSAENOPROTOOPT && *optlen == sizeof(DWORD)) { // Dual-stack IPv4/v6 sockets, and the IPV6_V6ONLY socket option, are only // supported on Windows Vista and later. To simplify program logic we will // fake success of getting this option and specify that the value is // non-zero (i.e. true). This corresponds to the behavior of IPv6 sockets // on Windows platforms pre-Vista. *static_cast(optval) = 1; ec = asio::error_code(); } if (result == 0) ec = asio::error_code(); return result; #else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) clear_last_error(); int result = error_wrapper(call_getsockopt(&msghdr::msg_namelen, s, level, optname, optval, optlen), ec); #if defined(__linux__) if (result == 0 && level == SOL_SOCKET && *optlen == sizeof(int) && (optname == SO_SNDBUF || optname == SO_RCVBUF)) { // On Linux, setting SO_SNDBUF or SO_RCVBUF to N actually causes the kernel // to set the buffer size to N*2. Linux puts additional stuff into the // buffers so that only about half is actually available to the application. // The retrieved value is divided by 2 here to make it appear as though the // correct value has been set. *static_cast(optval) /= 2; } #endif // defined(__linux__) if (result == 0) ec = asio::error_code(); return result; #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) } template inline int call_getpeername(SockLenType msghdr::*, socket_type s, socket_addr_type* addr, std::size_t* addrlen) { SockLenType tmp_addrlen = (SockLenType)*addrlen; int result = ::getpeername(s, addr, &tmp_addrlen); *addrlen = (std::size_t)tmp_addrlen; return result; } int getpeername(socket_type s, socket_addr_type* addr, std::size_t* addrlen, bool cached, asio::error_code& ec) { if (s == invalid_socket) { ec = asio::error::bad_descriptor; return socket_error_retval; } #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) if (cached) { // Check if socket is still connected. DWORD connect_time = 0; size_t connect_time_len = sizeof(connect_time); if (socket_ops::getsockopt(s, 0, SOL_SOCKET, SO_CONNECT_TIME, &connect_time, &connect_time_len, ec) == socket_error_retval) { return socket_error_retval; } if (connect_time == 0xFFFFFFFF) { ec = asio::error::not_connected; return socket_error_retval; } // The cached value is still valid. ec = asio::error_code(); return 0; } #else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) (void)cached; #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) clear_last_error(); int result = error_wrapper(call_getpeername( &msghdr::msg_namelen, s, addr, addrlen), ec); if (result == 0) ec = asio::error_code(); return result; } template inline int call_getsockname(SockLenType msghdr::*, socket_type s, socket_addr_type* addr, std::size_t* addrlen) { SockLenType tmp_addrlen = (SockLenType)*addrlen; int result = ::getsockname(s, addr, &tmp_addrlen); *addrlen = (std::size_t)tmp_addrlen; return result; } int getsockname(socket_type s, socket_addr_type* addr, std::size_t* addrlen, asio::error_code& ec) { if (s == invalid_socket) { ec = asio::error::bad_descriptor; return socket_error_retval; } clear_last_error(); int result = error_wrapper(call_getsockname( &msghdr::msg_namelen, s, addr, addrlen), ec); if (result == 0) ec = asio::error_code(); return result; } int ioctl(socket_type s, state_type& state, int cmd, ioctl_arg_type* arg, asio::error_code& ec) { if (s == invalid_socket) { ec = asio::error::bad_descriptor; return socket_error_retval; } clear_last_error(); #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) int result = error_wrapper(::ioctlsocket(s, cmd, arg), ec); #elif defined(__MACH__) && defined(__APPLE__) \ || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) int result = error_wrapper(::ioctl(s, static_cast(cmd), arg), ec); #else int result = error_wrapper(::ioctl(s, cmd, arg), ec); #endif if (result >= 0) { ec = asio::error_code(); // When updating the non-blocking mode we always perform the ioctl syscall, // even if the flags would otherwise indicate that the socket is already in // the correct state. This ensures that the underlying socket is put into // the state that has been requested by the user. If the ioctl syscall was // successful then we need to update the flags to match. if (cmd == static_cast(FIONBIO)) { if (*arg) { state |= user_set_non_blocking; } else { // Clearing the non-blocking mode always overrides any internally-set // non-blocking flag. Any subsequent asynchronous operations will need // to re-enable non-blocking I/O. state &= ~(user_set_non_blocking | internal_non_blocking); } } } return result; } int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, timeval* timeout, asio::error_code& ec) { clear_last_error(); #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) if (!readfds && !writefds && !exceptfds && timeout) { DWORD milliseconds = timeout->tv_sec * 1000 + timeout->tv_usec / 1000; if (milliseconds == 0) milliseconds = 1; // Force context switch. ::Sleep(milliseconds); ec = asio::error_code(); return 0; } // The select() call allows timeout values measured in microseconds, but the // system clock (as wrapped by boost::posix_time::microsec_clock) typically // has a resolution of 10 milliseconds. This can lead to a spinning select // reactor, meaning increased CPU usage, when waiting for the earliest // scheduled timeout if it's less than 10 milliseconds away. To avoid a tight // spin we'll use a minimum timeout of 1 millisecond. if (timeout && timeout->tv_sec == 0 && timeout->tv_usec > 0 && timeout->tv_usec < 1000) timeout->tv_usec = 1000; #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) #if defined(__hpux) && defined(__SELECT) timespec ts; ts.tv_sec = timeout ? timeout->tv_sec : 0; ts.tv_nsec = timeout ? timeout->tv_usec * 1000 : 0; return error_wrapper(::pselect(nfds, readfds, writefds, exceptfds, timeout ? &ts : 0, 0), ec); #else int result = error_wrapper(::select(nfds, readfds, writefds, exceptfds, timeout), ec); if (result >= 0) ec = asio::error_code(); return result; #endif } int poll_read(socket_type s, asio::error_code& ec) { if (s == invalid_socket) { ec = asio::error::bad_descriptor; return socket_error_retval; } #if defined(BOOST_WINDOWS) \ || defined(__CYGWIN__) \ || defined(__SYMBIAN32__) fd_set fds; FD_ZERO(&fds); FD_SET(s, &fds); clear_last_error(); int result = error_wrapper(::select(s, &fds, 0, 0, 0), ec); if (result >= 0) ec = asio::error_code(); return result; #else // defined(BOOST_WINDOWS) // || defined(__CYGWIN__) // || defined(__SYMBIAN32__) pollfd fds; fds.fd = s; fds.events = POLLIN; fds.revents = 0; clear_last_error(); int result = error_wrapper(::poll(&fds, 1, -1), ec); if (result >= 0) ec = asio::error_code(); return result; #endif // defined(BOOST_WINDOWS) // || defined(__CYGWIN__) // || defined(__SYMBIAN32__) } int poll_write(socket_type s, asio::error_code& ec) { if (s == invalid_socket) { ec = asio::error::bad_descriptor; return socket_error_retval; } #if defined(BOOST_WINDOWS) \ || defined(__CYGWIN__) \ || defined(__SYMBIAN32__) fd_set fds; FD_ZERO(&fds); FD_SET(s, &fds); clear_last_error(); int result = error_wrapper(::select(s, 0, &fds, 0, 0), ec); if (result >= 0) ec = asio::error_code(); return result; #else // defined(BOOST_WINDOWS) // || defined(__CYGWIN__) // || defined(__SYMBIAN32__) pollfd fds; fds.fd = s; fds.events = POLLOUT; fds.revents = 0; clear_last_error(); int result = error_wrapper(::poll(&fds, 1, -1), ec); if (result >= 0) ec = asio::error_code(); return result; #endif // defined(BOOST_WINDOWS) // || defined(__CYGWIN__) // || defined(__SYMBIAN32__) } int poll_connect(socket_type s, asio::error_code& ec) { if (s == invalid_socket) { ec = asio::error::bad_descriptor; return socket_error_retval; } #if defined(BOOST_WINDOWS) \ || defined(__CYGWIN__) \ || defined(__SYMBIAN32__) fd_set write_fds; FD_ZERO(&write_fds); FD_SET(s, &write_fds); fd_set except_fds; FD_ZERO(&except_fds); FD_SET(s, &except_fds); clear_last_error(); int result = error_wrapper(::select(s, 0, &write_fds, &except_fds, 0), ec); if (result >= 0) ec = asio::error_code(); return result; #else // defined(BOOST_WINDOWS) // || defined(__CYGWIN__) // || defined(__SYMBIAN32__) pollfd fds; fds.fd = s; fds.events = POLLOUT; fds.revents = 0; clear_last_error(); int result = error_wrapper(::poll(&fds, 1, -1), ec); if (result >= 0) ec = asio::error_code(); return result; #endif // defined(BOOST_WINDOWS) // || defined(__CYGWIN__) // || defined(__SYMBIAN32__) } const char* inet_ntop(int af, const void* src, char* dest, size_t length, unsigned long scope_id, asio::error_code& ec) { clear_last_error(); #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) using namespace std; // For memcpy. if (af != AF_INET && af != AF_INET6) { ec = asio::error::address_family_not_supported; return 0; } union { socket_addr_type base; sockaddr_storage_type storage; sockaddr_in4_type v4; sockaddr_in6_type v6; } address; DWORD address_length; if (af == AF_INET) { address_length = sizeof(sockaddr_in4_type); address.v4.sin_family = AF_INET; address.v4.sin_port = 0; memcpy(&address.v4.sin_addr, src, sizeof(in4_addr_type)); } else // AF_INET6 { address_length = sizeof(sockaddr_in6_type); address.v6.sin6_family = AF_INET6; address.v6.sin6_port = 0; address.v6.sin6_flowinfo = 0; address.v6.sin6_scope_id = scope_id; memcpy(&address.v6.sin6_addr, src, sizeof(in6_addr_type)); } DWORD string_length = static_cast(length); #if defined(BOOST_NO_ANSI_APIS) LPWSTR string_buffer = (LPWSTR)_alloca(length * sizeof(WCHAR)); int result = error_wrapper(::WSAAddressToStringW(&address.base, address_length, 0, string_buffer, &string_length), ec); ::WideCharToMultiByte(CP_ACP, 0, string_buffer, -1, dest, length, 0, 0); #else int result = error_wrapper(::WSAAddressToStringA( &address.base, address_length, 0, dest, &string_length), ec); #endif // Windows may set error code on success. if (result != socket_error_retval) ec = asio::error_code(); // Windows may not set an error code on failure. else if (result == socket_error_retval && !ec) ec = asio::error::invalid_argument; return result == socket_error_retval ? 0 : dest; #else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) const char* result = error_wrapper(::inet_ntop(af, src, dest, length), ec); if (result == 0 && !ec) ec = asio::error::invalid_argument; if (result != 0 && af == AF_INET6 && scope_id != 0) { using namespace std; // For strcat and sprintf. char if_name[IF_NAMESIZE + 1] = "%"; const in6_addr_type* ipv6_address = static_cast(src); bool is_link_local = IN6_IS_ADDR_LINKLOCAL(ipv6_address); if (!is_link_local || if_indextoname(scope_id, if_name + 1) == 0) sprintf(if_name + 1, "%lu", scope_id); strcat(dest, if_name); } return result; #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) } int inet_pton(int af, const char* src, void* dest, unsigned long* scope_id, asio::error_code& ec) { clear_last_error(); #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) using namespace std; // For memcpy and strcmp. if (af != AF_INET && af != AF_INET6) { ec = asio::error::address_family_not_supported; return -1; } union { socket_addr_type base; sockaddr_storage_type storage; sockaddr_in4_type v4; sockaddr_in6_type v6; } address; int address_length = sizeof(sockaddr_storage_type); #if defined(BOOST_NO_ANSI_APIS) int num_wide_chars = strlen(src) + 1; LPWSTR wide_buffer = (LPWSTR)_alloca(num_wide_chars * sizeof(WCHAR)); ::MultiByteToWideChar(CP_ACP, 0, src, -1, wide_buffer, num_wide_chars); int result = error_wrapper(::WSAStringToAddressW( wide_buffer, af, 0, &address.base, &address_length), ec); #else int result = error_wrapper(::WSAStringToAddressA( const_cast(src), af, 0, &address.base, &address_length), ec); #endif if (af == AF_INET) { if (result != socket_error_retval) { memcpy(dest, &address.v4.sin_addr, sizeof(in4_addr_type)); ec = asio::error_code(); } else if (strcmp(src, "255.255.255.255") == 0) { static_cast(dest)->s_addr = INADDR_NONE; ec = asio::error_code(); } } else // AF_INET6 { if (result != socket_error_retval) { memcpy(dest, &address.v6.sin6_addr, sizeof(in6_addr_type)); if (scope_id) *scope_id = address.v6.sin6_scope_id; ec = asio::error_code(); } } // Windows may not set an error code on failure. if (result == socket_error_retval && !ec) ec = asio::error::invalid_argument; if (result != socket_error_retval) ec = asio::error_code(); return result == socket_error_retval ? -1 : 1; #else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) int result = error_wrapper(::inet_pton(af, src, dest), ec); if (result <= 0 && !ec) ec = asio::error::invalid_argument; if (result > 0 && af == AF_INET6 && scope_id) { using namespace std; // For strchr and atoi. *scope_id = 0; if (const char* if_name = strchr(src, '%')) { in6_addr_type* ipv6_address = static_cast(dest); bool is_link_local = IN6_IS_ADDR_LINKLOCAL(ipv6_address); if (is_link_local) *scope_id = if_nametoindex(if_name + 1); if (*scope_id == 0) *scope_id = atoi(if_name + 1); } } return result; #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) } int gethostname(char* name, int namelen, asio::error_code& ec) { clear_last_error(); int result = error_wrapper(::gethostname(name, namelen), ec); #if defined(BOOST_WINDOWS) if (result == 0) ec = asio::error_code(); #endif return result; } #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) \ || defined(__MACH__) && defined(__APPLE__) // The following functions are only needed for emulation of getaddrinfo and // getnameinfo. inline asio::error_code translate_netdb_error(int error) { switch (error) { case 0: return asio::error_code(); case HOST_NOT_FOUND: return asio::error::host_not_found; case TRY_AGAIN: return asio::error::host_not_found_try_again; case NO_RECOVERY: return asio::error::no_recovery; case NO_DATA: return asio::error::no_data; default: BOOST_ASSERT(false); return asio::error::invalid_argument; } } inline hostent* gethostbyaddr(const char* addr, int length, int af, hostent* result, char* buffer, int buflength, asio::error_code& ec) { clear_last_error(); #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) (void)(buffer); (void)(buflength); hostent* retval = error_wrapper(::gethostbyaddr(addr, length, af), ec); if (!retval) return 0; ec = asio::error_code(); *result = *retval; return retval; #elif defined(__sun) || defined(__QNX__) int error = 0; hostent* retval = error_wrapper(::gethostbyaddr_r(addr, length, af, result, buffer, buflength, &error), ec); if (error) ec = translate_netdb_error(error); return retval; #elif defined(__MACH__) && defined(__APPLE__) (void)(buffer); (void)(buflength); int error = 0; hostent* retval = error_wrapper(::getipnodebyaddr( addr, length, af, &error), ec); if (error) ec = translate_netdb_error(error); if (!retval) return 0; *result = *retval; return retval; #else hostent* retval = 0; int error = 0; error_wrapper(::gethostbyaddr_r(addr, length, af, result, buffer, buflength, &retval, &error), ec); if (error) ec = translate_netdb_error(error); return retval; #endif } inline hostent* gethostbyname(const char* name, int af, struct hostent* result, char* buffer, int buflength, int ai_flags, asio::error_code& ec) { clear_last_error(); #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) (void)(buffer); (void)(buflength); (void)(ai_flags); if (af != AF_INET) { ec = asio::error::address_family_not_supported; return 0; } hostent* retval = error_wrapper(::gethostbyname(name), ec); if (!retval) return 0; ec = asio::error_code(); *result = *retval; return result; #elif defined(__sun) || defined(__QNX__) (void)(ai_flags); if (af != AF_INET) { ec = asio::error::address_family_not_supported; return 0; } int error = 0; hostent* retval = error_wrapper(::gethostbyname_r(name, result, buffer, buflength, &error), ec); if (error) ec = translate_netdb_error(error); return retval; #elif defined(__MACH__) && defined(__APPLE__) (void)(buffer); (void)(buflength); int error = 0; hostent* retval = error_wrapper(::getipnodebyname( name, af, ai_flags, &error), ec); if (error) ec = translate_netdb_error(error); if (!retval) return 0; *result = *retval; return retval; #else (void)(ai_flags); if (af != AF_INET) { ec = asio::error::address_family_not_supported; return 0; } hostent* retval = 0; int error = 0; error_wrapper(::gethostbyname_r(name, result, buffer, buflength, &retval, &error), ec); if (error) ec = translate_netdb_error(error); return retval; #endif } inline void freehostent(hostent* h) { #if defined(__MACH__) && defined(__APPLE__) if (h) ::freehostent(h); #else (void)(h); #endif } // Emulation of getaddrinfo based on implementation in: // Stevens, W. R., UNIX Network Programming Vol. 1, 2nd Ed., Prentice-Hall 1998. struct gai_search { const char* host; int family; }; inline int gai_nsearch(const char* host, const addrinfo_type* hints, gai_search (&search)[2]) { int search_count = 0; if (host == 0 || host[0] == '\0') { if (hints->ai_flags & AI_PASSIVE) { // No host and AI_PASSIVE implies wildcard bind. switch (hints->ai_family) { case AF_INET: search[search_count].host = "0.0.0.0"; search[search_count].family = AF_INET; ++search_count; break; case AF_INET6: search[search_count].host = "0::0"; search[search_count].family = AF_INET6; ++search_count; break; case AF_UNSPEC: search[search_count].host = "0::0"; search[search_count].family = AF_INET6; ++search_count; search[search_count].host = "0.0.0.0"; search[search_count].family = AF_INET; ++search_count; break; default: break; } } else { // No host and not AI_PASSIVE means connect to local host. switch (hints->ai_family) { case AF_INET: search[search_count].host = "localhost"; search[search_count].family = AF_INET; ++search_count; break; case AF_INET6: search[search_count].host = "localhost"; search[search_count].family = AF_INET6; ++search_count; break; case AF_UNSPEC: search[search_count].host = "localhost"; search[search_count].family = AF_INET6; ++search_count; search[search_count].host = "localhost"; search[search_count].family = AF_INET; ++search_count; break; default: break; } } } else { // Host is specified. switch (hints->ai_family) { case AF_INET: search[search_count].host = host; search[search_count].family = AF_INET; ++search_count; break; case AF_INET6: search[search_count].host = host; search[search_count].family = AF_INET6; ++search_count; break; case AF_UNSPEC: search[search_count].host = host; search[search_count].family = AF_INET6; ++search_count; search[search_count].host = host; search[search_count].family = AF_INET; ++search_count; break; default: break; } } return search_count; } template inline T* gai_alloc(std::size_t size = sizeof(T)) { using namespace std; T* p = static_cast(::operator new(size, std::nothrow)); if (p) memset(p, 0, size); return p; } inline void gai_free(void* p) { ::operator delete(p); } inline void gai_strcpy(char* target, const char* source, std::size_t max_size) { using namespace std; #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) && !defined(UNDER_CE) strcpy_s(target, max_size, source); #else *target = 0; strncat(target, source, max_size); #endif } enum { gai_clone_flag = 1 << 30 }; inline int gai_aistruct(addrinfo_type*** next, const addrinfo_type* hints, const void* addr, int family) { using namespace std; addrinfo_type* ai = gai_alloc(); if (ai == 0) return EAI_MEMORY; ai->ai_next = 0; **next = ai; *next = &ai->ai_next; ai->ai_canonname = 0; ai->ai_socktype = hints->ai_socktype; if (ai->ai_socktype == 0) ai->ai_flags |= gai_clone_flag; ai->ai_protocol = hints->ai_protocol; ai->ai_family = family; switch (ai->ai_family) { case AF_INET: { sockaddr_in4_type* sinptr = gai_alloc(); if (sinptr == 0) return EAI_MEMORY; sinptr->sin_family = AF_INET; memcpy(&sinptr->sin_addr, addr, sizeof(in4_addr_type)); ai->ai_addr = reinterpret_cast(sinptr); ai->ai_addrlen = sizeof(sockaddr_in4_type); break; } case AF_INET6: { sockaddr_in6_type* sin6ptr = gai_alloc(); if (sin6ptr == 0) return EAI_MEMORY; sin6ptr->sin6_family = AF_INET6; memcpy(&sin6ptr->sin6_addr, addr, sizeof(in6_addr_type)); ai->ai_addr = reinterpret_cast(sin6ptr); ai->ai_addrlen = sizeof(sockaddr_in6_type); break; } default: break; } return 0; } inline addrinfo_type* gai_clone(addrinfo_type* ai) { using namespace std; addrinfo_type* new_ai = gai_alloc(); if (new_ai == 0) return new_ai; new_ai->ai_next = ai->ai_next; ai->ai_next = new_ai; new_ai->ai_flags = 0; new_ai->ai_family = ai->ai_family; new_ai->ai_socktype = ai->ai_socktype; new_ai->ai_protocol = ai->ai_protocol; new_ai->ai_canonname = 0; new_ai->ai_addrlen = ai->ai_addrlen; new_ai->ai_addr = gai_alloc(ai->ai_addrlen); memcpy(new_ai->ai_addr, ai->ai_addr, ai->ai_addrlen); return new_ai; } inline int gai_port(addrinfo_type* aihead, int port, int socktype) { int num_found = 0; for (addrinfo_type* ai = aihead; ai; ai = ai->ai_next) { if (ai->ai_flags & gai_clone_flag) { if (ai->ai_socktype != 0) { ai = gai_clone(ai); if (ai == 0) return -1; // ai now points to newly cloned entry. } } else if (ai->ai_socktype != socktype) { // Ignore if mismatch on socket type. continue; } ai->ai_socktype = socktype; switch (ai->ai_family) { case AF_INET: { sockaddr_in4_type* sinptr = reinterpret_cast(ai->ai_addr); sinptr->sin_port = port; ++num_found; break; } case AF_INET6: { sockaddr_in6_type* sin6ptr = reinterpret_cast(ai->ai_addr); sin6ptr->sin6_port = port; ++num_found; break; } default: break; } } return num_found; } inline int gai_serv(addrinfo_type* aihead, const addrinfo_type* hints, const char* serv) { using namespace std; int num_found = 0; if ( #if defined(AI_NUMERICSERV) (hints->ai_flags & AI_NUMERICSERV) || #endif isdigit(static_cast(serv[0]))) { int port = htons(atoi(serv)); if (hints->ai_socktype) { // Caller specifies socket type. int rc = gai_port(aihead, port, hints->ai_socktype); if (rc < 0) return EAI_MEMORY; num_found += rc; } else { // Caller does not specify socket type. int rc = gai_port(aihead, port, SOCK_STREAM); if (rc < 0) return EAI_MEMORY; num_found += rc; rc = gai_port(aihead, port, SOCK_DGRAM); if (rc < 0) return EAI_MEMORY; num_found += rc; } } else { // Try service name with TCP first, then UDP. if (hints->ai_socktype == 0 || hints->ai_socktype == SOCK_STREAM) { servent* sptr = getservbyname(serv, "tcp"); if (sptr != 0) { int rc = gai_port(aihead, sptr->s_port, SOCK_STREAM); if (rc < 0) return EAI_MEMORY; num_found += rc; } } if (hints->ai_socktype == 0 || hints->ai_socktype == SOCK_DGRAM) { servent* sptr = getservbyname(serv, "udp"); if (sptr != 0) { int rc = gai_port(aihead, sptr->s_port, SOCK_DGRAM); if (rc < 0) return EAI_MEMORY; num_found += rc; } } } if (num_found == 0) { if (hints->ai_socktype == 0) { // All calls to getservbyname() failed. return EAI_NONAME; } else { // Service not supported for socket type. return EAI_SERVICE; } } return 0; } inline int gai_echeck(const char* host, const char* service, int flags, int family, int socktype, int protocol) { (void)(flags); (void)(protocol); // Host or service must be specified. if (host == 0 || host[0] == '\0') if (service == 0 || service[0] == '\0') return EAI_NONAME; // Check combination of family and socket type. switch (family) { case AF_UNSPEC: break; case AF_INET: case AF_INET6: if (service != 0 && service[0] != '\0') if (socktype != 0 && socktype != SOCK_STREAM && socktype != SOCK_DGRAM) return EAI_SOCKTYPE; break; default: return EAI_FAMILY; } return 0; } inline void freeaddrinfo_emulation(addrinfo_type* aihead) { addrinfo_type* ai = aihead; while (ai) { gai_free(ai->ai_addr); gai_free(ai->ai_canonname); addrinfo_type* ainext = ai->ai_next; gai_free(ai); ai = ainext; } } inline int getaddrinfo_emulation(const char* host, const char* service, const addrinfo_type* hintsp, addrinfo_type** result) { // Set up linked list of addrinfo structures. addrinfo_type* aihead = 0; addrinfo_type** ainext = &aihead; char* canon = 0; // Supply default hints if not specified by caller. addrinfo_type hints = addrinfo_type(); hints.ai_family = AF_UNSPEC; if (hintsp) hints = *hintsp; // If the resolution is not specifically for AF_INET6, remove the AI_V4MAPPED // and AI_ALL flags. #if defined(AI_V4MAPPED) if (hints.ai_family != AF_INET6) hints.ai_flags &= ~AI_V4MAPPED; #endif #if defined(AI_ALL) if (hints.ai_family != AF_INET6) hints.ai_flags &= ~AI_ALL; #endif // Basic error checking. int rc = gai_echeck(host, service, hints.ai_flags, hints.ai_family, hints.ai_socktype, hints.ai_protocol); if (rc != 0) { freeaddrinfo_emulation(aihead); return rc; } gai_search search[2]; int search_count = gai_nsearch(host, &hints, search); for (gai_search* sptr = search; sptr < search + search_count; ++sptr) { // Check for IPv4 dotted decimal string. in4_addr_type inaddr; asio::error_code ec; if (socket_ops::inet_pton(AF_INET, sptr->host, &inaddr, 0, ec) == 1) { if (hints.ai_family != AF_UNSPEC && hints.ai_family != AF_INET) { freeaddrinfo_emulation(aihead); gai_free(canon); return EAI_FAMILY; } if (sptr->family == AF_INET) { rc = gai_aistruct(&ainext, &hints, &inaddr, AF_INET); if (rc != 0) { freeaddrinfo_emulation(aihead); gai_free(canon); return rc; } } continue; } // Check for IPv6 hex string. in6_addr_type in6addr; if (socket_ops::inet_pton(AF_INET6, sptr->host, &in6addr, 0, ec) == 1) { if (hints.ai_family != AF_UNSPEC && hints.ai_family != AF_INET6) { freeaddrinfo_emulation(aihead); gai_free(canon); return EAI_FAMILY; } if (sptr->family == AF_INET6) { rc = gai_aistruct(&ainext, &hints, &in6addr, AF_INET6); if (rc != 0) { freeaddrinfo_emulation(aihead); gai_free(canon); return rc; } } continue; } // Look up hostname. hostent hent; char hbuf[8192] = ""; hostent* hptr = socket_ops::gethostbyname(sptr->host, sptr->family, &hent, hbuf, sizeof(hbuf), hints.ai_flags, ec); if (hptr == 0) { if (search_count == 2) { // Failure is OK if there are multiple searches. continue; } freeaddrinfo_emulation(aihead); gai_free(canon); if (ec == asio::error::host_not_found) return EAI_NONAME; if (ec == asio::error::host_not_found_try_again) return EAI_AGAIN; if (ec == asio::error::no_recovery) return EAI_FAIL; if (ec == asio::error::no_data) return EAI_NONAME; return EAI_NONAME; } // Check for address family mismatch if one was specified. if (hints.ai_family != AF_UNSPEC && hints.ai_family != hptr->h_addrtype) { freeaddrinfo_emulation(aihead); gai_free(canon); socket_ops::freehostent(hptr); return EAI_FAMILY; } // Save canonical name first time. if (host != 0 && host[0] != '\0' && hptr->h_name && hptr->h_name[0] && (hints.ai_flags & AI_CANONNAME) && canon == 0) { std::size_t canon_len = strlen(hptr->h_name) + 1; canon = gai_alloc(canon_len); if (canon == 0) { freeaddrinfo_emulation(aihead); socket_ops::freehostent(hptr); return EAI_MEMORY; } gai_strcpy(canon, hptr->h_name, canon_len); } // Create an addrinfo structure for each returned address. for (char** ap = hptr->h_addr_list; *ap; ++ap) { rc = gai_aistruct(&ainext, &hints, *ap, hptr->h_addrtype); if (rc != 0) { freeaddrinfo_emulation(aihead); gai_free(canon); socket_ops::freehostent(hptr); return EAI_FAMILY; } } socket_ops::freehostent(hptr); } // Check if we found anything. if (aihead == 0) { gai_free(canon); return EAI_NONAME; } // Return canonical name in first entry. if (host != 0 && host[0] != '\0' && (hints.ai_flags & AI_CANONNAME)) { if (canon) { aihead->ai_canonname = canon; canon = 0; } else { std::size_t canonname_len = strlen(search[0].host) + 1; aihead->ai_canonname = gai_alloc(canonname_len); if (aihead->ai_canonname == 0) { freeaddrinfo_emulation(aihead); return EAI_MEMORY; } gai_strcpy(aihead->ai_canonname, search[0].host, canonname_len); } } gai_free(canon); // Process the service name. if (service != 0 && service[0] != '\0') { rc = gai_serv(aihead, &hints, service); if (rc != 0) { freeaddrinfo_emulation(aihead); return rc; } } // Return result to caller. *result = aihead; return 0; } inline asio::error_code getnameinfo_emulation( const socket_addr_type* sa, std::size_t salen, char* host, std::size_t hostlen, char* serv, std::size_t servlen, int flags, asio::error_code& ec) { using namespace std; const char* addr; size_t addr_len; unsigned short port; switch (sa->sa_family) { case AF_INET: if (salen != sizeof(sockaddr_in4_type)) { return ec = asio::error::invalid_argument; } addr = reinterpret_cast( &reinterpret_cast(sa)->sin_addr); addr_len = sizeof(in4_addr_type); port = reinterpret_cast(sa)->sin_port; break; case AF_INET6: if (salen != sizeof(sockaddr_in6_type)) { return ec = asio::error::invalid_argument; } addr = reinterpret_cast( &reinterpret_cast(sa)->sin6_addr); addr_len = sizeof(in6_addr_type); port = reinterpret_cast(sa)->sin6_port; break; default: return ec = asio::error::address_family_not_supported; } if (host && hostlen > 0) { if (flags & NI_NUMERICHOST) { if (socket_ops::inet_ntop(sa->sa_family, addr, host, hostlen, 0, ec) == 0) { return ec; } } else { hostent hent; char hbuf[8192] = ""; hostent* hptr = socket_ops::gethostbyaddr(addr, static_cast(addr_len), sa->sa_family, &hent, hbuf, sizeof(hbuf), ec); if (hptr && hptr->h_name && hptr->h_name[0] != '\0') { if (flags & NI_NOFQDN) { char* dot = strchr(hptr->h_name, '.'); if (dot) { *dot = 0; } } gai_strcpy(host, hptr->h_name, hostlen); socket_ops::freehostent(hptr); } else { socket_ops::freehostent(hptr); if (flags & NI_NAMEREQD) { return ec = asio::error::host_not_found; } if (socket_ops::inet_ntop(sa->sa_family, addr, host, hostlen, 0, ec) == 0) { return ec; } } } } if (serv && servlen > 0) { if (flags & NI_NUMERICSERV) { if (servlen < 6) { return ec = asio::error::no_buffer_space; } #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) && !defined(UNDER_CE) sprintf_s(serv, servlen, "%u", ntohs(port)); #else sprintf(serv, "%u", ntohs(port)); #endif } else { #if defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS) \ && !defined(ASIO_DISABLE_THREADS) static ::pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; ::pthread_mutex_lock(&mutex); #endif // defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS) // && !defined(ASIO_DISABLE_THREADS) servent* sptr = ::getservbyport(port, (flags & NI_DGRAM) ? "udp" : 0); if (sptr && sptr->s_name && sptr->s_name[0] != '\0') { gai_strcpy(serv, sptr->s_name, servlen); } else { if (servlen < 6) { return ec = asio::error::no_buffer_space; } #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) && !defined(UNDER_CE) sprintf_s(serv, servlen, "%u", ntohs(port)); #else sprintf(serv, "%u", ntohs(port)); #endif } #if defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS) \ && !defined(ASIO_DISABLE_THREADS) ::pthread_mutex_unlock(&mutex); #endif // defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS) // && !defined(ASIO_DISABLE_THREADS) } } ec = asio::error_code(); return ec; } #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) // || defined(__MACH__) && defined(__APPLE__) inline asio::error_code translate_addrinfo_error(int error) { switch (error) { case 0: return asio::error_code(); case EAI_AGAIN: return asio::error::host_not_found_try_again; case EAI_BADFLAGS: return asio::error::invalid_argument; case EAI_FAIL: return asio::error::no_recovery; case EAI_FAMILY: return asio::error::address_family_not_supported; case EAI_MEMORY: return asio::error::no_memory; case EAI_NONAME: #if defined(EAI_ADDRFAMILY) case EAI_ADDRFAMILY: #endif #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) case EAI_NODATA: #endif return asio::error::host_not_found; case EAI_SERVICE: return asio::error::service_not_found; case EAI_SOCKTYPE: return asio::error::socket_type_not_supported; default: // Possibly the non-portable EAI_SYSTEM. #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) return asio::error_code( WSAGetLastError(), asio::error::get_system_category()); #else return asio::error_code( errno, asio::error::get_system_category()); #endif } } asio::error_code getaddrinfo(const char* host, const char* service, const addrinfo_type& hints, addrinfo_type** result, asio::error_code& ec) { host = (host && *host) ? host : 0; service = (service && *service) ? service : 0; clear_last_error(); #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) # if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) || defined(UNDER_CE) // Building for Windows XP, Windows Server 2003, or later. int error = ::getaddrinfo(host, service, &hints, result); return ec = translate_addrinfo_error(error); # else // Building for Windows 2000 or earlier. typedef int (WSAAPI *gai_t)(const char*, const char*, const addrinfo_type*, addrinfo_type**); if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) { if (gai_t gai = (gai_t)::GetProcAddress(winsock_module, "getaddrinfo")) { int error = gai(host, service, &hints, result); return ec = translate_addrinfo_error(error); } } int error = getaddrinfo_emulation(host, service, &hints, result); return ec = translate_addrinfo_error(error); # endif #elif defined(__MACH__) && defined(__APPLE__) int error = getaddrinfo_emulation(host, service, &hints, result); return ec = translate_addrinfo_error(error); #else int error = ::getaddrinfo(host, service, &hints, result); return ec = translate_addrinfo_error(error); #endif } asio::error_code background_getaddrinfo( const weak_cancel_token_type& cancel_token, const char* host, const char* service, const addrinfo_type& hints, addrinfo_type** result, asio::error_code& ec) { if (cancel_token.expired()) ec = asio::error::operation_aborted; else socket_ops::getaddrinfo(host, service, hints, result, ec); return ec; } void freeaddrinfo(addrinfo_type* ai) { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) # if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) || defined(UNDER_CE) // Building for Windows XP, Windows Server 2003, or later. ::freeaddrinfo(ai); # else // Building for Windows 2000 or earlier. typedef int (WSAAPI *fai_t)(addrinfo_type*); if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) { if (fai_t fai = (fai_t)::GetProcAddress(winsock_module, "freeaddrinfo")) { fai(ai); return; } } freeaddrinfo_emulation(ai); # endif #elif defined(__MACH__) && defined(__APPLE__) freeaddrinfo_emulation(ai); #else ::freeaddrinfo(ai); #endif } asio::error_code getnameinfo(const socket_addr_type* addr, std::size_t addrlen, char* host, std::size_t hostlen, char* serv, std::size_t servlen, int flags, asio::error_code& ec) { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) # if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) || defined(UNDER_CE) // Building for Windows XP, Windows Server 2003, or later. clear_last_error(); int error = ::getnameinfo(addr, static_cast(addrlen), host, static_cast(hostlen), serv, static_cast(servlen), flags); return ec = translate_addrinfo_error(error); # else // Building for Windows 2000 or earlier. typedef int (WSAAPI *gni_t)(const socket_addr_type*, int, char*, DWORD, char*, DWORD, int); if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) { if (gni_t gni = (gni_t)::GetProcAddress(winsock_module, "getnameinfo")) { clear_last_error(); int error = gni(addr, static_cast(addrlen), host, static_cast(hostlen), serv, static_cast(servlen), flags); return ec = translate_addrinfo_error(error); } } clear_last_error(); return getnameinfo_emulation(addr, addrlen, host, hostlen, serv, servlen, flags, ec); # endif #elif defined(__MACH__) && defined(__APPLE__) using namespace std; // For memcpy. sockaddr_storage_type tmp_addr; memcpy(&tmp_addr, addr, addrlen); tmp_addr.ss_len = addrlen; addr = reinterpret_cast(&tmp_addr); clear_last_error(); return getnameinfo_emulation(addr, addrlen, host, hostlen, serv, servlen, flags, ec); #else clear_last_error(); int error = ::getnameinfo(addr, addrlen, host, hostlen, serv, servlen, flags); return ec = translate_addrinfo_error(error); #endif } asio::error_code sync_getnameinfo( const socket_addr_type* addr, std::size_t addrlen, char* host, std::size_t hostlen, char* serv, std::size_t servlen, int sock_type, asio::error_code& ec) { // First try resolving with the service name. If that fails try resolving // but allow the service to be returned as a number. int flags = (sock_type == SOCK_DGRAM) ? NI_DGRAM : 0; socket_ops::getnameinfo(addr, addrlen, host, hostlen, serv, servlen, flags, ec); if (ec) { socket_ops::getnameinfo(addr, addrlen, host, hostlen, serv, servlen, flags | NI_NUMERICSERV, ec); } return ec; } asio::error_code background_getnameinfo( const weak_cancel_token_type& cancel_token, const socket_addr_type* addr, std::size_t addrlen, char* host, std::size_t hostlen, char* serv, std::size_t servlen, int sock_type, asio::error_code& ec) { if (cancel_token.expired()) { ec = asio::error::operation_aborted; } else { // First try resolving with the service name. If that fails try resolving // but allow the service to be returned as a number. int flags = (sock_type == SOCK_DGRAM) ? NI_DGRAM : 0; socket_ops::getnameinfo(addr, addrlen, host, hostlen, serv, servlen, flags, ec); if (ec) { socket_ops::getnameinfo(addr, addrlen, host, hostlen, serv, servlen, flags | NI_NUMERICSERV, ec); } } return ec; } u_long_type network_to_host_long(u_long_type value) { return ntohl(value); } u_long_type host_to_network_long(u_long_type value) { return htonl(value); } u_short_type network_to_host_short(u_short_type value) { return ntohs(value); } u_short_type host_to_network_short(u_short_type value) { return htons(value); } } // namespace socket_ops } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_SOCKET_OPS_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/socket_select_interrupter.ipp0000644000000000000000000001155712247075736030760 0ustar rootroot00000000000000// // detail/impl/socket_select_interrupter.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_SOCKET_SELECT_INTERRUPTER_IPP #define ASIO_DETAIL_IMPL_SOCKET_SELECT_INTERRUPTER_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) \ || defined(__CYGWIN__) \ || defined(__SYMBIAN32__) #include #include "asio/detail/socket_holder.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/detail/socket_select_interrupter.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { socket_select_interrupter::socket_select_interrupter() { asio::error_code ec; socket_holder acceptor(socket_ops::socket( AF_INET, SOCK_STREAM, IPPROTO_TCP, ec)); if (acceptor.get() == invalid_socket) asio::detail::throw_error(ec, "socket_select_interrupter"); int opt = 1; socket_ops::state_type acceptor_state = 0; socket_ops::setsockopt(acceptor.get(), acceptor_state, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt), ec); using namespace std; // For memset. sockaddr_in4_type addr; std::size_t addr_len = sizeof(addr); memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr("127.0.0.1"); addr.sin_port = 0; if (socket_ops::bind(acceptor.get(), (const socket_addr_type*)&addr, addr_len, ec) == socket_error_retval) asio::detail::throw_error(ec, "socket_select_interrupter"); if (socket_ops::getsockname(acceptor.get(), (socket_addr_type*)&addr, &addr_len, ec) == socket_error_retval) asio::detail::throw_error(ec, "socket_select_interrupter"); // Some broken firewalls on Windows will intermittently cause getsockname to // return 0.0.0.0 when the socket is actually bound to 127.0.0.1. We // explicitly specify the target address here to work around this problem. addr.sin_addr.s_addr = inet_addr("127.0.0.1"); if (socket_ops::listen(acceptor.get(), SOMAXCONN, ec) == socket_error_retval) asio::detail::throw_error(ec, "socket_select_interrupter"); socket_holder client(socket_ops::socket( AF_INET, SOCK_STREAM, IPPROTO_TCP, ec)); if (client.get() == invalid_socket) asio::detail::throw_error(ec, "socket_select_interrupter"); if (socket_ops::connect(client.get(), (const socket_addr_type*)&addr, addr_len, ec) == socket_error_retval) asio::detail::throw_error(ec, "socket_select_interrupter"); socket_holder server(socket_ops::accept(acceptor.get(), 0, 0, ec)); if (server.get() == invalid_socket) asio::detail::throw_error(ec, "socket_select_interrupter"); ioctl_arg_type non_blocking = 1; socket_ops::state_type client_state = 0; if (socket_ops::ioctl(client.get(), client_state, FIONBIO, &non_blocking, ec)) asio::detail::throw_error(ec, "socket_select_interrupter"); opt = 1; socket_ops::setsockopt(client.get(), client_state, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt), ec); non_blocking = 1; socket_ops::state_type server_state = 0; if (socket_ops::ioctl(server.get(), server_state, FIONBIO, &non_blocking, ec)) asio::detail::throw_error(ec, "socket_select_interrupter"); opt = 1; socket_ops::setsockopt(server.get(), server_state, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt), ec); read_descriptor_ = server.release(); write_descriptor_ = client.release(); } socket_select_interrupter::~socket_select_interrupter() { asio::error_code ec; socket_ops::state_type state = socket_ops::internal_non_blocking; if (read_descriptor_ != invalid_socket) socket_ops::close(read_descriptor_, state, true, ec); if (write_descriptor_ != invalid_socket) socket_ops::close(write_descriptor_, state, true, ec); } void socket_select_interrupter::interrupt() { char byte = 0; socket_ops::buf b; socket_ops::init_buf(b, &byte, 1); asio::error_code ec; socket_ops::send(write_descriptor_, &b, 1, 0, ec); } bool socket_select_interrupter::reset() { char data[1024]; socket_ops::buf b; socket_ops::init_buf(b, data, sizeof(data)); asio::error_code ec; int bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0, ec); bool was_interrupted = (bytes_read > 0); while (bytes_read == sizeof(data)) bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0, ec); return was_interrupted; } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(BOOST_WINDOWS) // || defined(__CYGWIN__) // || defined(__SYMBIAN32__) #endif // ASIO_DETAIL_IMPL_SOCKET_SELECT_INTERRUPTER_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/strand_service.hpp0000644000000000000000000000756112247075736026500 0ustar rootroot00000000000000// // detail/impl/strand_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_STRAND_SERVICE_HPP #define ASIO_DETAIL_IMPL_STRAND_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/call_stack.hpp" #include "asio/detail/completion_handler.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { inline strand_service::strand_impl::strand_impl() : operation(&strand_service::do_complete), count_(0) { } struct strand_service::on_dispatch_exit { io_service_impl* io_service_; strand_impl* impl_; ~on_dispatch_exit() { impl_->mutex_.lock(); bool more_handlers = (--impl_->count_ > 0); impl_->mutex_.unlock(); if (more_handlers) io_service_->post_immediate_completion(impl_); } }; inline void strand_service::destroy(strand_service::implementation_type& impl) { impl = 0; } template void strand_service::dispatch(strand_service::implementation_type& impl, Handler handler) { // If we are already in the strand then the handler can run immediately. if (call_stack::contains(impl)) { asio::detail::fenced_block b; asio_handler_invoke_helpers::invoke(handler, handler); return; } // Allocate and construct an operation to wrap the handler. typedef completion_handler op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(handler); // If we are running inside the io_service, and no other handler is queued // or running, then the handler can run immediately. bool can_dispatch = call_stack::contains(&io_service_); impl->mutex_.lock(); bool first = (++impl->count_ == 1); if (can_dispatch && first) { // Immediate invocation is allowed. impl->mutex_.unlock(); // Memory must be releaesed before any upcall is made. p.reset(); // Indicate that this strand is executing on the current thread. call_stack::context ctx(impl); // Ensure the next handler, if any, is scheduled on block exit. on_dispatch_exit on_exit = { &io_service_, impl }; (void)on_exit; asio::detail::fenced_block b; asio_handler_invoke_helpers::invoke(handler, handler); return; } // Immediate invocation is not allowed, so enqueue for later. impl->queue_.push(p.p); impl->mutex_.unlock(); p.v = p.p = 0; // The first handler to be enqueued is responsible for scheduling the // strand. if (first) io_service_.post_immediate_completion(impl); } // Request the io_service to invoke the given handler and return immediately. template void strand_service::post(strand_service::implementation_type& impl, Handler handler) { // Allocate and construct an operation to wrap the handler. typedef completion_handler op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(handler); // Add the handler to the queue. impl->mutex_.lock(); bool first = (++impl->count_ == 1); impl->queue_.push(p.p); impl->mutex_.unlock(); p.v = p.p = 0; // The first handler to be enqueue is responsible for scheduling the strand. if (first) io_service_.post_immediate_completion(impl); } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_IMPL_STRAND_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/detail/impl/strand_service.ipp0000644000000000000000000000534212247075736026474 0ustar rootroot00000000000000// // detail/impl/strand_service.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_STRAND_SERVICE_IPP #define ASIO_DETAIL_IMPL_STRAND_SERVICE_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/detail/call_stack.hpp" #include "asio/detail/strand_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { struct strand_service::on_do_complete_exit { io_service_impl* owner_; strand_impl* impl_; ~on_do_complete_exit() { impl_->mutex_.lock(); bool more_handlers = (--impl_->count_ > 0); impl_->mutex_.unlock(); if (more_handlers) owner_->post_immediate_completion(impl_); } }; strand_service::strand_service(asio::io_service& io_service) : asio::detail::service_base(io_service), io_service_(asio::use_service(io_service)), mutex_(), salt_(0) { } void strand_service::shutdown_service() { op_queue ops; asio::detail::mutex::scoped_lock lock(mutex_); for (std::size_t i = 0; i < num_implementations; ++i) if (strand_impl* impl = implementations_[i].get()) ops.push(impl->queue_); } void strand_service::construct(strand_service::implementation_type& impl) { std::size_t salt = salt_++; std::size_t index = reinterpret_cast(&impl); index += (reinterpret_cast(&impl) >> 3); index ^= salt + 0x9e3779b9 + (index << 6) + (index >> 2); index = index % num_implementations; asio::detail::mutex::scoped_lock lock(mutex_); if (!implementations_[index]) implementations_[index].reset(new strand_impl); impl = implementations_[index].get(); } void strand_service::do_complete(io_service_impl* owner, operation* base, asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) { if (owner) { strand_impl* impl = static_cast(base); // Get the next handler to be executed. impl->mutex_.lock(); operation* o = impl->queue_.front(); impl->queue_.pop(); impl->mutex_.unlock(); // Indicate that this strand is executing on the current thread. call_stack::context ctx(impl); // Ensure the next handler, if any, is scheduled on block exit. on_do_complete_exit on_exit = { owner, impl }; (void)on_exit; o->complete(*owner); } } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_IMPL_STRAND_SERVICE_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/task_io_service.hpp0000644000000000000000000000307512247075736026632 0ustar rootroot00000000000000// // detail/impl/task_io_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_TASK_IO_SERVICE_HPP #define ASIO_DETAIL_IMPL_TASK_IO_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/call_stack.hpp" #include "asio/detail/completion_handler.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template void task_io_service::dispatch(Handler handler) { if (call_stack::contains(this)) { asio::detail::fenced_block b; asio_handler_invoke_helpers::invoke(handler, handler); } else post(handler); } template void task_io_service::post(Handler handler) { // Allocate and construct an operation to wrap the handler. typedef completion_handler op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(handler); post_immediate_completion(p.p); p.v = p.p = 0; } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_IMPL_TASK_IO_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/detail/impl/task_io_service.ipp0000644000000000000000000001732212247075736026633 0ustar rootroot00000000000000// // detail/impl/task_io_service.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_TASK_IO_SERVICE_IPP #define ASIO_DETAIL_IMPL_TASK_IO_SERVICE_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(ASIO_HAS_IOCP) #include #include "asio/detail/call_stack.hpp" #include "asio/detail/event.hpp" #include "asio/detail/reactor.hpp" #include "asio/detail/task_io_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { struct task_io_service::task_cleanup { ~task_cleanup() { // Enqueue the completed operations and reinsert the task at the end of // the operation queue. lock_->lock(); task_io_service_->task_interrupted_ = true; task_io_service_->op_queue_.push(*ops_); task_io_service_->op_queue_.push(&task_io_service_->task_operation_); } task_io_service* task_io_service_; mutex::scoped_lock* lock_; op_queue* ops_; }; struct task_io_service::work_finished_on_block_exit { ~work_finished_on_block_exit() { task_io_service_->work_finished(); } task_io_service* task_io_service_; }; struct task_io_service::idle_thread_info { event wakeup_event; idle_thread_info* next; }; task_io_service::task_io_service(asio::io_service& io_service) : asio::detail::service_base(io_service), mutex_(), task_(0), task_interrupted_(true), outstanding_work_(0), stopped_(false), shutdown_(false), first_idle_thread_(0) { } void task_io_service::init(std::size_t /*concurrency_hint*/) { } void task_io_service::shutdown_service() { mutex::scoped_lock lock(mutex_); shutdown_ = true; lock.unlock(); // Destroy handler objects. while (!op_queue_.empty()) { operation* o = op_queue_.front(); op_queue_.pop(); if (o != &task_operation_) o->destroy(); } // Reset to initial state. task_ = 0; } void task_io_service::init_task() { mutex::scoped_lock lock(mutex_); if (!shutdown_ && !task_) { task_ = &use_service(this->get_io_service()); op_queue_.push(&task_operation_); wake_one_thread_and_unlock(lock); } } std::size_t task_io_service::run(asio::error_code& ec) { ec = asio::error_code(); if (outstanding_work_ == 0) { stop(); return 0; } call_stack::context ctx(this); idle_thread_info this_idle_thread; this_idle_thread.next = 0; mutex::scoped_lock lock(mutex_); std::size_t n = 0; for (; do_one(lock, &this_idle_thread); lock.lock()) if (n != (std::numeric_limits::max)()) ++n; return n; } std::size_t task_io_service::run_one(asio::error_code& ec) { ec = asio::error_code(); if (outstanding_work_ == 0) { stop(); return 0; } call_stack::context ctx(this); idle_thread_info this_idle_thread; this_idle_thread.next = 0; mutex::scoped_lock lock(mutex_); return do_one(lock, &this_idle_thread); } std::size_t task_io_service::poll(asio::error_code& ec) { if (outstanding_work_ == 0) { stop(); ec = asio::error_code(); return 0; } call_stack::context ctx(this); mutex::scoped_lock lock(mutex_); std::size_t n = 0; for (; do_one(lock, 0); lock.lock()) if (n != (std::numeric_limits::max)()) ++n; return n; } std::size_t task_io_service::poll_one(asio::error_code& ec) { ec = asio::error_code(); if (outstanding_work_ == 0) { stop(); return 0; } call_stack::context ctx(this); mutex::scoped_lock lock(mutex_); return do_one(lock, 0); } void task_io_service::stop() { mutex::scoped_lock lock(mutex_); stop_all_threads(lock); } void task_io_service::reset() { mutex::scoped_lock lock(mutex_); stopped_ = false; } void task_io_service::post_immediate_completion(task_io_service::operation* op) { work_started(); post_deferred_completion(op); } void task_io_service::post_deferred_completion(task_io_service::operation* op) { mutex::scoped_lock lock(mutex_); op_queue_.push(op); wake_one_thread_and_unlock(lock); } void task_io_service::post_deferred_completions( op_queue& ops) { if (!ops.empty()) { mutex::scoped_lock lock(mutex_); op_queue_.push(ops); wake_one_thread_and_unlock(lock); } } std::size_t task_io_service::do_one(mutex::scoped_lock& lock, task_io_service::idle_thread_info* this_idle_thread) { bool polling = !this_idle_thread; bool task_has_run = false; while (!stopped_) { if (!op_queue_.empty()) { // Prepare to execute first handler from queue. operation* o = op_queue_.front(); op_queue_.pop(); bool more_handlers = (!op_queue_.empty()); if (o == &task_operation_) { task_interrupted_ = more_handlers || polling; // If the task has already run and we're polling then we're done. if (task_has_run && polling) { task_interrupted_ = true; op_queue_.push(&task_operation_); return 0; } task_has_run = true; if (!more_handlers || !wake_one_idle_thread_and_unlock(lock)) lock.unlock(); op_queue completed_ops; task_cleanup c = { this, &lock, &completed_ops }; (void)c; // Run the task. May throw an exception. Only block if the operation // queue is empty and we're not polling, otherwise we want to return // as soon as possible. task_->run(!more_handlers && !polling, completed_ops); } else { if (more_handlers) wake_one_thread_and_unlock(lock); else lock.unlock(); // Ensure the count of outstanding work is decremented on block exit. work_finished_on_block_exit on_exit = { this }; (void)on_exit; // Complete the operation. May throw an exception. o->complete(*this); // deletes the operation object return 1; } } else if (this_idle_thread) { // Nothing to run right now, so just wait for work to do. this_idle_thread->next = first_idle_thread_; first_idle_thread_ = this_idle_thread; this_idle_thread->wakeup_event.clear(lock); this_idle_thread->wakeup_event.wait(lock); } else { return 0; } } return 0; } void task_io_service::stop_all_threads( mutex::scoped_lock& lock) { stopped_ = true; while (first_idle_thread_) { idle_thread_info* idle_thread = first_idle_thread_; first_idle_thread_ = idle_thread->next; idle_thread->next = 0; idle_thread->wakeup_event.signal(lock); } if (!task_interrupted_ && task_) { task_interrupted_ = true; task_->interrupt(); } } bool task_io_service::wake_one_idle_thread_and_unlock( mutex::scoped_lock& lock) { if (first_idle_thread_) { idle_thread_info* idle_thread = first_idle_thread_; first_idle_thread_ = idle_thread->next; idle_thread->next = 0; idle_thread->wakeup_event.signal_and_unlock(lock); return true; } return false; } void task_io_service::wake_one_thread_and_unlock( mutex::scoped_lock& lock) { if (!wake_one_idle_thread_and_unlock(lock)) { if (!task_interrupted_ && task_) { task_interrupted_ = true; task_->interrupt(); } lock.unlock(); } } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // !defined(ASIO_HAS_IOCP) #endif // ASIO_DETAIL_IMPL_TASK_IO_SERVICE_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/throw_error.ipp0000644000000000000000000000211712247075736026032 0ustar rootroot00000000000000// // detail/impl/throw_error.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_THROW_ERROR_IPP #define ASIO_DETAIL_IMPL_THROW_ERROR_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/detail/throw_error.hpp" #include "asio/system_error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { void do_throw_error(const asio::error_code& err) { asio::system_error e(err); boost::throw_exception(e); } void do_throw_error(const asio::error_code& err, const char* location) { asio::system_error e(err, location); boost::throw_exception(e); } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_IMPL_THROW_ERROR_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/timer_queue.ipp0000644000000000000000000000401712247075736026003 0ustar rootroot00000000000000// // detail/impl/timer_queue.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_TIMER_QUEUE_IPP #define ASIO_DETAIL_IMPL_TIMER_QUEUE_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if !defined(ASIO_HEADER_ONLY) #include "asio/detail/timer_queue.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { timer_queue >::timer_queue() { } timer_queue >::~timer_queue() { } bool timer_queue >::enqueue_timer( const time_type& time, per_timer_data& timer, timer_op* op) { return impl_.enqueue_timer(time, timer, op); } bool timer_queue >::empty() const { return impl_.empty(); } long timer_queue >::wait_duration_msec( long max_duration) const { return impl_.wait_duration_msec(max_duration); } long timer_queue >::wait_duration_usec( long max_duration) const { return impl_.wait_duration_usec(max_duration); } void timer_queue >::get_ready_timers( op_queue& ops) { impl_.get_ready_timers(ops); } void timer_queue >::get_all_timers( op_queue& ops) { impl_.get_all_timers(ops); } std::size_t timer_queue >::cancel_timer( per_timer_data& timer, op_queue& ops) { return impl_.cancel_timer(timer, ops); } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // !defined(ASIO_HEADER_ONLY) #endif // ASIO_DETAIL_IMPL_TIMER_QUEUE_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/timer_queue_set.ipp0000644000000000000000000000427412247075736026663 0ustar rootroot00000000000000// // detail/impl/timer_queue_set.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_TIMER_QUEUE_SET_IPP #define ASIO_DETAIL_IMPL_TIMER_QUEUE_SET_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/detail/timer_queue_set.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { timer_queue_set::timer_queue_set() : first_(0) { } void timer_queue_set::insert(timer_queue_base* q) { q->next_ = first_; first_ = q; } void timer_queue_set::erase(timer_queue_base* q) { if (first_) { if (q == first_) { first_ = q->next_; q->next_ = 0; return; } for (timer_queue_base* p = first_; p->next_; p = p->next_) { if (p->next_ == q) { p->next_ = q->next_; q->next_ = 0; return; } } } } bool timer_queue_set::all_empty() const { for (timer_queue_base* p = first_; p; p = p->next_) if (!p->empty()) return false; return true; } long timer_queue_set::wait_duration_msec(long max_duration) const { long min_duration = max_duration; for (timer_queue_base* p = first_; p; p = p->next_) min_duration = p->wait_duration_msec(min_duration); return min_duration; } long timer_queue_set::wait_duration_usec(long max_duration) const { long min_duration = max_duration; for (timer_queue_base* p = first_; p; p = p->next_) min_duration = p->wait_duration_usec(min_duration); return min_duration; } void timer_queue_set::get_ready_timers(op_queue& ops) { for (timer_queue_base* p = first_; p; p = p->next_) p->get_ready_timers(ops); } void timer_queue_set::get_all_timers(op_queue& ops) { for (timer_queue_base* p = first_; p; p = p->next_) p->get_all_timers(ops); } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_DETAIL_IMPL_TIMER_QUEUE_SET_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/win_event.ipp0000644000000000000000000000217112247075736025454 0ustar rootroot00000000000000// // detail/win_event.ipp // ~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_WIN_EVENT_IPP #define ASIO_DETAIL_IMPL_WIN_EVENT_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) #include "asio/detail/throw_error.hpp" #include "asio/detail/win_event.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { win_event::win_event() : event_(::CreateEvent(0, true, false, 0)) { if (!event_) { DWORD last_error = ::GetLastError(); asio::error_code ec(last_error, asio::error::get_system_category()); asio::detail::throw_error(ec, "event"); } } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(BOOST_WINDOWS) #endif // ASIO_DETAIL_IMPL_WIN_EVENT_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/win_iocp_handle_service.ipp0000644000000000000000000002652712247075736030333 0ustar rootroot00000000000000// // detail/impl/win_iocp_handle_service.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP #define ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) #include "asio/detail/win_iocp_handle_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class win_iocp_handle_service::overlapped_wrapper : public OVERLAPPED { public: explicit overlapped_wrapper(asio::error_code& ec) { Internal = 0; InternalHigh = 0; Offset = 0; OffsetHigh = 0; // Create a non-signalled manual-reset event, for GetOverlappedResult. hEvent = ::CreateEvent(0, TRUE, FALSE, 0); if (hEvent) { // As documented in GetQueuedCompletionStatus, setting the low order // bit of this event prevents our synchronous writes from being treated // as completion port events. *reinterpret_cast(&hEvent) |= 1; } else { DWORD last_error = ::GetLastError(); ec = asio::error_code(last_error, asio::error::get_system_category()); } } ~overlapped_wrapper() { if (hEvent) { ::CloseHandle(hEvent); } } }; win_iocp_handle_service::win_iocp_handle_service( asio::io_service& io_service) : iocp_service_(asio::use_service(io_service)), mutex_(), impl_list_(0) { } void win_iocp_handle_service::shutdown_service() { // Close all implementations, causing all operations to complete. asio::detail::mutex::scoped_lock lock(mutex_); implementation_type* impl = impl_list_; while (impl) { close_for_destruction(*impl); impl = impl->next_; } } void win_iocp_handle_service::construct( win_iocp_handle_service::implementation_type& impl) { impl.handle_ = INVALID_HANDLE_VALUE; impl.safe_cancellation_thread_id_ = 0; // Insert implementation into linked list of all implementations. asio::detail::mutex::scoped_lock lock(mutex_); impl.next_ = impl_list_; impl.prev_ = 0; if (impl_list_) impl_list_->prev_ = &impl; impl_list_ = &impl; } void win_iocp_handle_service::destroy( win_iocp_handle_service::implementation_type& impl) { close_for_destruction(impl); // Remove implementation from linked list of all implementations. asio::detail::mutex::scoped_lock lock(mutex_); if (impl_list_ == &impl) impl_list_ = impl.next_; if (impl.prev_) impl.prev_->next_ = impl.next_; if (impl.next_) impl.next_->prev_= impl.prev_; impl.next_ = 0; impl.prev_ = 0; } asio::error_code win_iocp_handle_service::assign( win_iocp_handle_service::implementation_type& impl, const native_type& native_handle, asio::error_code& ec) { if (is_open(impl)) { ec = asio::error::already_open; return ec; } if (iocp_service_.register_handle(native_handle, ec)) return ec; impl.handle_ = native_handle; ec = asio::error_code(); return ec; } asio::error_code win_iocp_handle_service::close( win_iocp_handle_service::implementation_type& impl, asio::error_code& ec) { if (is_open(impl)) { if (!::CloseHandle(impl.handle_)) { DWORD last_error = ::GetLastError(); ec = asio::error_code(last_error, asio::error::get_system_category()); return ec; } impl.handle_ = INVALID_HANDLE_VALUE; impl.safe_cancellation_thread_id_ = 0; } ec = asio::error_code(); return ec; } asio::error_code win_iocp_handle_service::cancel( win_iocp_handle_service::implementation_type& impl, asio::error_code& ec) { if (!is_open(impl)) { ec = asio::error::bad_descriptor; } else if (FARPROC cancel_io_ex_ptr = ::GetProcAddress( ::GetModuleHandleA("KERNEL32"), "CancelIoEx")) { // The version of Windows supports cancellation from any thread. typedef BOOL (WINAPI* cancel_io_ex_t)(HANDLE, LPOVERLAPPED); cancel_io_ex_t cancel_io_ex = (cancel_io_ex_t)cancel_io_ex_ptr; if (!cancel_io_ex(impl.handle_, 0)) { DWORD last_error = ::GetLastError(); if (last_error == ERROR_NOT_FOUND) { // ERROR_NOT_FOUND means that there were no operations to be // cancelled. We swallow this error to match the behaviour on other // platforms. ec = asio::error_code(); } else { ec = asio::error_code(last_error, asio::error::get_system_category()); } } else { ec = asio::error_code(); } } else if (impl.safe_cancellation_thread_id_ == 0) { // No operations have been started, so there's nothing to cancel. ec = asio::error_code(); } else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId()) { // Asynchronous operations have been started from the current thread only, // so it is safe to try to cancel them using CancelIo. if (!::CancelIo(impl.handle_)) { DWORD last_error = ::GetLastError(); ec = asio::error_code(last_error, asio::error::get_system_category()); } else { ec = asio::error_code(); } } else { // Asynchronous operations have been started from more than one thread, // so cancellation is not safe. ec = asio::error::operation_not_supported; } return ec; } size_t win_iocp_handle_service::do_write( win_iocp_handle_service::implementation_type& impl, boost::uint64_t offset, const asio::const_buffer& buffer, asio::error_code& ec) { if (!is_open(impl)) { ec = asio::error::bad_descriptor; return 0; } // A request to write 0 bytes on a handle is a no-op. if (asio::buffer_size(buffer) == 0) { ec = asio::error_code(); return 0; } overlapped_wrapper overlapped(ec); if (ec) { return 0; } // Write the data. overlapped.Offset = offset & 0xFFFFFFFF; overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF; BOOL ok = ::WriteFile(impl.handle_, asio::buffer_cast(buffer), static_cast(asio::buffer_size(buffer)), 0, &overlapped); if (!ok) { DWORD last_error = ::GetLastError(); if (last_error != ERROR_IO_PENDING) { ec = asio::error_code(last_error, asio::error::get_system_category()); return 0; } } // Wait for the operation to complete. DWORD bytes_transferred = 0; ok = ::GetOverlappedResult(impl.handle_, &overlapped, &bytes_transferred, TRUE); if (!ok) { DWORD last_error = ::GetLastError(); ec = asio::error_code(last_error, asio::error::get_system_category()); return 0; } ec = asio::error_code(); return bytes_transferred; } void win_iocp_handle_service::start_write_op( win_iocp_handle_service::implementation_type& impl, boost::uint64_t offset, const asio::const_buffer& buffer, operation* op) { update_cancellation_thread_id(impl); iocp_service_.work_started(); if (!is_open(impl)) { iocp_service_.on_completion(op, asio::error::bad_descriptor); } else if (asio::buffer_size(buffer) == 0) { // A request to write 0 bytes on a handle is a no-op. iocp_service_.on_completion(op); } else { DWORD bytes_transferred = 0; op->Offset = offset & 0xFFFFFFFF; op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF; BOOL ok = ::WriteFile(impl.handle_, asio::buffer_cast(buffer), static_cast(asio::buffer_size(buffer)), &bytes_transferred, op); DWORD last_error = ::GetLastError(); if (!ok && last_error != ERROR_IO_PENDING && last_error != ERROR_MORE_DATA) { iocp_service_.on_completion(op, last_error, bytes_transferred); } else { iocp_service_.on_pending(op); } } } size_t win_iocp_handle_service::do_read( win_iocp_handle_service::implementation_type& impl, boost::uint64_t offset, const asio::mutable_buffer& buffer, asio::error_code& ec) { if (!is_open(impl)) { ec = asio::error::bad_descriptor; return 0; } // A request to read 0 bytes on a stream handle is a no-op. if (asio::buffer_size(buffer) == 0) { ec = asio::error_code(); return 0; } overlapped_wrapper overlapped(ec); if (ec) { return 0; } // Read some data. overlapped.Offset = offset & 0xFFFFFFFF; overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF; BOOL ok = ::ReadFile(impl.handle_, asio::buffer_cast(buffer), static_cast(asio::buffer_size(buffer)), 0, &overlapped); if (!ok) { DWORD last_error = ::GetLastError(); if (last_error != ERROR_IO_PENDING && last_error != ERROR_MORE_DATA) { if (last_error == ERROR_HANDLE_EOF) { ec = asio::error::eof; } else { ec = asio::error_code(last_error, asio::error::get_system_category()); } return 0; } } // Wait for the operation to complete. DWORD bytes_transferred = 0; ok = ::GetOverlappedResult(impl.handle_, &overlapped, &bytes_transferred, TRUE); if (!ok) { DWORD last_error = ::GetLastError(); if (last_error == ERROR_HANDLE_EOF) { ec = asio::error::eof; } else { ec = asio::error_code(last_error, asio::error::get_system_category()); } return 0; } ec = asio::error_code(); return bytes_transferred; } void win_iocp_handle_service::start_read_op( win_iocp_handle_service::implementation_type& impl, boost::uint64_t offset, const asio::mutable_buffer& buffer, operation* op) { update_cancellation_thread_id(impl); iocp_service_.work_started(); if (!is_open(impl)) { iocp_service_.on_completion(op, asio::error::bad_descriptor); } else if (asio::buffer_size(buffer) == 0) { // A request to read 0 bytes on a handle is a no-op. iocp_service_.on_completion(op); } else { DWORD bytes_transferred = 0; op->Offset = offset & 0xFFFFFFFF; op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF; BOOL ok = ::ReadFile(impl.handle_, asio::buffer_cast(buffer), static_cast(asio::buffer_size(buffer)), &bytes_transferred, op); DWORD last_error = ::GetLastError(); if (!ok && last_error != ERROR_IO_PENDING && last_error != ERROR_MORE_DATA) { iocp_service_.on_completion(op, last_error, bytes_transferred); } else { iocp_service_.on_pending(op); } } } void win_iocp_handle_service::update_cancellation_thread_id( win_iocp_handle_service::implementation_type& impl) { if (impl.safe_cancellation_thread_id_ == 0) impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) impl.safe_cancellation_thread_id_ = ~DWORD(0); } void win_iocp_handle_service::close_for_destruction(implementation_type& impl) { if (is_open(impl)) { ::CloseHandle(impl.handle_); impl.handle_ = INVALID_HANDLE_VALUE; impl.safe_cancellation_thread_id_ = 0; } } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_IOCP) #endif // ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/win_iocp_io_service.hpp0000644000000000000000000000602412247075736027474 0ustar rootroot00000000000000// // detail/impl/win_iocp_io_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_WIN_IOCP_IO_SERVICE_HPP #define ASIO_DETAIL_IMPL_WIN_IOCP_IO_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) #include "asio/detail/call_stack.hpp" #include "asio/detail/completion_handler.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { template void win_iocp_io_service::dispatch(Handler handler) { if (call_stack::contains(this)) { asio::detail::fenced_block b; asio_handler_invoke_helpers::invoke(handler, handler); } else post(handler); } template void win_iocp_io_service::post(Handler handler) { // Allocate and construct an operation to wrap the handler. typedef completion_handler op; typename op::ptr p = { boost::addressof(handler), asio_handler_alloc_helpers::allocate( sizeof(op), handler), 0 }; p.p = new (p.v) op(handler); post_immediate_completion(p.p); p.v = p.p = 0; } template void win_iocp_io_service::add_timer_queue( timer_queue& queue) { do_add_timer_queue(queue); } template void win_iocp_io_service::remove_timer_queue( timer_queue& queue) { do_remove_timer_queue(queue); } template void win_iocp_io_service::schedule_timer(timer_queue& queue, const typename Time_Traits::time_type& time, typename timer_queue::per_timer_data& timer, timer_op* op) { // If the service has been shut down we silently discard the timer. if (::InterlockedExchangeAdd(&shutdown_, 0) != 0) { post_immediate_completion(op); return; } mutex::scoped_lock lock(dispatch_mutex_); bool earliest = queue.enqueue_timer(time, timer, op); work_started(); if (earliest) update_timeout(); } template std::size_t win_iocp_io_service::cancel_timer(timer_queue& queue, typename timer_queue::per_timer_data& timer) { // If the service has been shut down we silently ignore the cancellation. if (::InterlockedExchangeAdd(&shutdown_, 0) != 0) return 0; mutex::scoped_lock lock(dispatch_mutex_); op_queue ops; std::size_t n = queue.cancel_timer(timer, ops); post_deferred_completions(ops); return n; } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_IOCP) #endif // ASIO_DETAIL_IMPL_WIN_IOCP_IO_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/detail/impl/win_iocp_io_service.ipp0000644000000000000000000003222512247075736027477 0ustar rootroot00000000000000// // detail/impl/win_iocp_io_service.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_WIN_IOCP_IO_SERVICE_IPP #define ASIO_DETAIL_IMPL_WIN_IOCP_IO_SERVICE_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) #include #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/throw_error.hpp" #include "asio/detail/win_iocp_io_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { struct win_iocp_io_service::work_finished_on_block_exit { ~work_finished_on_block_exit() { io_service_->work_finished(); } win_iocp_io_service* io_service_; }; struct win_iocp_io_service::timer_thread_function { void operator()() { while (::InterlockedExchangeAdd(&io_service_->shutdown_, 0) == 0) { if (::WaitForSingleObject(io_service_->waitable_timer_.handle, INFINITE) == WAIT_OBJECT_0) { ::InterlockedExchange(&io_service_->dispatch_required_, 1); ::PostQueuedCompletionStatus(io_service_->iocp_.handle, 0, wake_for_dispatch, 0); } } } win_iocp_io_service* io_service_; }; win_iocp_io_service::win_iocp_io_service(asio::io_service& io_service) : asio::detail::service_base(io_service), iocp_(), outstanding_work_(0), stopped_(0), shutdown_(0), dispatch_required_(0) { } void win_iocp_io_service::init(size_t concurrency_hint) { iocp_.handle = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, static_cast((std::min)(concurrency_hint, DWORD(~0)))); if (!iocp_.handle) { DWORD last_error = ::GetLastError(); asio::error_code ec(last_error, asio::error::get_system_category()); asio::detail::throw_error(ec, "iocp"); } } void win_iocp_io_service::shutdown_service() { ::InterlockedExchange(&shutdown_, 1); if (timer_thread_) { LARGE_INTEGER timeout; timeout.QuadPart = 1; ::SetWaitableTimer(waitable_timer_.handle, &timeout, 1, 0, 0, FALSE); } while (::InterlockedExchangeAdd(&outstanding_work_, 0) > 0) { op_queue ops; timer_queues_.get_all_timers(ops); ops.push(completed_ops_); if (!ops.empty()) { while (win_iocp_operation* op = ops.front()) { ops.pop(); ::InterlockedDecrement(&outstanding_work_); op->destroy(); } } else { DWORD bytes_transferred = 0; dword_ptr_t completion_key = 0; LPOVERLAPPED overlapped = 0; ::GetQueuedCompletionStatus(iocp_.handle, &bytes_transferred, &completion_key, &overlapped, gqcs_timeout); if (overlapped) { ::InterlockedDecrement(&outstanding_work_); static_cast(overlapped)->destroy(); } } } if (timer_thread_) timer_thread_->join(); } asio::error_code win_iocp_io_service::register_handle( HANDLE handle, asio::error_code& ec) { if (::CreateIoCompletionPort(handle, iocp_.handle, 0, 0) == 0) { DWORD last_error = ::GetLastError(); ec = asio::error_code(last_error, asio::error::get_system_category()); } else { ec = asio::error_code(); } return ec; } size_t win_iocp_io_service::run(asio::error_code& ec) { if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) { stop(); ec = asio::error_code(); return 0; } call_stack::context ctx(this); size_t n = 0; while (do_one(true, ec)) if (n != (std::numeric_limits::max)()) ++n; return n; } size_t win_iocp_io_service::run_one(asio::error_code& ec) { if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) { stop(); ec = asio::error_code(); return 0; } call_stack::context ctx(this); return do_one(true, ec); } size_t win_iocp_io_service::poll(asio::error_code& ec) { if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) { stop(); ec = asio::error_code(); return 0; } call_stack::context ctx(this); size_t n = 0; while (do_one(false, ec)) if (n != (std::numeric_limits::max)()) ++n; return n; } size_t win_iocp_io_service::poll_one(asio::error_code& ec) { if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) { stop(); ec = asio::error_code(); return 0; } call_stack::context ctx(this); return do_one(false, ec); } void win_iocp_io_service::stop() { if (::InterlockedExchange(&stopped_, 1) == 0) { if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0)) { DWORD last_error = ::GetLastError(); asio::error_code ec(last_error, asio::error::get_system_category()); asio::detail::throw_error(ec, "pqcs"); } } } void win_iocp_io_service::post_deferred_completion(win_iocp_operation* op) { // Flag the operation as ready. op->ready_ = 1; // Enqueue the operation on the I/O completion port. if (!::PostQueuedCompletionStatus(iocp_.handle, 0, overlapped_contains_result, op)) { // Out of resources. Put on completed queue instead. mutex::scoped_lock lock(dispatch_mutex_); completed_ops_.push(op); ::InterlockedExchange(&dispatch_required_, 1); } } void win_iocp_io_service::post_deferred_completions( op_queue& ops) { while (win_iocp_operation* op = ops.front()) { ops.pop(); // Flag the operation as ready. op->ready_ = 1; // Enqueue the operation on the I/O completion port. if (!::PostQueuedCompletionStatus(iocp_.handle, 0, overlapped_contains_result, op)) { // Out of resources. Put on completed queue instead. mutex::scoped_lock lock(dispatch_mutex_); completed_ops_.push(op); completed_ops_.push(ops); ::InterlockedExchange(&dispatch_required_, 1); } } } void win_iocp_io_service::on_pending(win_iocp_operation* op) { if (::InterlockedCompareExchange(&op->ready_, 1, 0) == 1) { // Enqueue the operation on the I/O completion port. if (!::PostQueuedCompletionStatus(iocp_.handle, 0, overlapped_contains_result, op)) { // Out of resources. Put on completed queue instead. mutex::scoped_lock lock(dispatch_mutex_); completed_ops_.push(op); ::InterlockedExchange(&dispatch_required_, 1); } } } void win_iocp_io_service::on_completion(win_iocp_operation* op, DWORD last_error, DWORD bytes_transferred) { // Flag that the operation is ready for invocation. op->ready_ = 1; // Store results in the OVERLAPPED structure. op->Internal = asio::error::get_system_category(); op->Offset = last_error; op->OffsetHigh = bytes_transferred; // Enqueue the operation on the I/O completion port. if (!::PostQueuedCompletionStatus(iocp_.handle, 0, overlapped_contains_result, op)) { // Out of resources. Put on completed queue instead. mutex::scoped_lock lock(dispatch_mutex_); completed_ops_.push(op); ::InterlockedExchange(&dispatch_required_, 1); } } void win_iocp_io_service::on_completion(win_iocp_operation* op, const asio::error_code& ec, DWORD bytes_transferred) { // Flag that the operation is ready for invocation. op->ready_ = 1; // Store results in the OVERLAPPED structure. op->Internal = ec.category(); op->Offset = ec.value(); op->OffsetHigh = bytes_transferred; // Enqueue the operation on the I/O completion port. if (!::PostQueuedCompletionStatus(iocp_.handle, 0, overlapped_contains_result, op)) { // Out of resources. Put on completed queue instead. mutex::scoped_lock lock(dispatch_mutex_); completed_ops_.push(op); ::InterlockedExchange(&dispatch_required_, 1); } } size_t win_iocp_io_service::do_one(bool block, asio::error_code& ec) { for (;;) { // Try to acquire responsibility for dispatching timers and completed ops. if (::InterlockedCompareExchange(&dispatch_required_, 0, 1) == 1) { mutex::scoped_lock lock(dispatch_mutex_); // Dispatch pending timers and operations. op_queue ops; ops.push(completed_ops_); timer_queues_.get_ready_timers(ops); post_deferred_completions(ops); update_timeout(); } // Get the next operation from the queue. DWORD bytes_transferred = 0; dword_ptr_t completion_key = 0; LPOVERLAPPED overlapped = 0; ::SetLastError(0); BOOL ok = ::GetQueuedCompletionStatus(iocp_.handle, &bytes_transferred, &completion_key, &overlapped, block ? gqcs_timeout : 0); DWORD last_error = ::GetLastError(); if (overlapped) { win_iocp_operation* op = static_cast(overlapped); asio::error_code result_ec(last_error, asio::error::get_system_category()); // We may have been passed the last_error and bytes_transferred in the // OVERLAPPED structure itself. if (completion_key == overlapped_contains_result) { result_ec = asio::error_code(static_cast(op->Offset), static_cast(op->Internal)); bytes_transferred = op->OffsetHigh; } // Otherwise ensure any result has been saved into the OVERLAPPED // structure. else { op->Internal = result_ec.category(); op->Offset = result_ec.value(); op->OffsetHigh = bytes_transferred; } // Dispatch the operation only if ready. The operation may not be ready // if the initiating function (e.g. a call to WSARecv) has not yet // returned. This is because the initiating function still wants access // to the operation's OVERLAPPED structure. if (::InterlockedCompareExchange(&op->ready_, 1, 0) == 1) { // Ensure the count of outstanding work is decremented on block exit. work_finished_on_block_exit on_exit = { this }; (void)on_exit; op->complete(*this, result_ec, bytes_transferred); ec = asio::error_code(); return 1; } } else if (!ok) { if (last_error != WAIT_TIMEOUT) { ec = asio::error_code(last_error, asio::error::get_system_category()); return 0; } // If we're not polling we need to keep going until we get a real handler. if (block) continue; ec = asio::error_code(); return 0; } else if (completion_key == wake_for_dispatch) { // We have been woken up to try to acquire responsibility for dispatching // timers and completed operations. } else { // The stopped_ flag is always checked to ensure that any leftover // interrupts from a previous run invocation are ignored. if (::InterlockedExchangeAdd(&stopped_, 0) != 0) { // Wake up next thread that is blocked on GetQueuedCompletionStatus. if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0)) { last_error = ::GetLastError(); ec = asio::error_code(last_error, asio::error::get_system_category()); return 0; } ec = asio::error_code(); return 0; } } } } void win_iocp_io_service::do_add_timer_queue(timer_queue_base& queue) { mutex::scoped_lock lock(dispatch_mutex_); timer_queues_.insert(&queue); if (!waitable_timer_.handle) { waitable_timer_.handle = ::CreateWaitableTimer(0, FALSE, 0); if (waitable_timer_.handle == 0) { DWORD last_error = ::GetLastError(); asio::error_code ec(last_error, asio::error::get_system_category()); asio::detail::throw_error(ec, "timer"); } LARGE_INTEGER timeout; timeout.QuadPart = -max_timeout_usec; timeout.QuadPart *= 10; ::SetWaitableTimer(waitable_timer_.handle, &timeout, max_timeout_msec, 0, 0, FALSE); } if (!timer_thread_) { timer_thread_function thread_function = { this }; timer_thread_.reset(new thread(thread_function, 65536)); } } void win_iocp_io_service::do_remove_timer_queue(timer_queue_base& queue) { mutex::scoped_lock lock(dispatch_mutex_); timer_queues_.erase(&queue); } void win_iocp_io_service::update_timeout() { if (timer_thread_) { // There's no point updating the waitable timer if the new timeout period // exceeds the maximum timeout. In that case, we might as well wait for the // existing period of the timer to expire. long timeout_usec = timer_queues_.wait_duration_usec(max_timeout_usec); if (timeout_usec < max_timeout_usec) { LARGE_INTEGER timeout; timeout.QuadPart = -timeout_usec; timeout.QuadPart *= 10; ::SetWaitableTimer(waitable_timer_.handle, &timeout, max_timeout_msec, 0, 0, FALSE); } } } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_IOCP) #endif // ASIO_DETAIL_IMPL_WIN_IOCP_IO_SERVICE_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/win_iocp_serial_port_service.ipp0000644000000000000000000001220612247075736031410 0ustar rootroot00000000000000// // detail/impl/win_iocp_serial_port_service.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_WIN_IOCP_SERIAL_PORT_SERVICE_IPP #define ASIO_DETAIL_IMPL_WIN_IOCP_SERIAL_PORT_SERVICE_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) && defined(ASIO_HAS_SERIAL_PORT) #include #include "asio/detail/win_iocp_serial_port_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { win_iocp_serial_port_service::win_iocp_serial_port_service( asio::io_service& io_service) : handle_service_(io_service) { } void win_iocp_serial_port_service::shutdown_service() { } asio::error_code win_iocp_serial_port_service::open( win_iocp_serial_port_service::implementation_type& impl, const std::string& device, asio::error_code& ec) { if (is_open(impl)) { ec = asio::error::already_open; return ec; } // For convenience, add a leading \\.\ sequence if not already present. std::string name = (device[0] == '\\') ? device : "\\\\.\\" + device; // Open a handle to the serial port. ::HANDLE handle = ::CreateFileA(name.c_str(), GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); if (handle == INVALID_HANDLE_VALUE) { DWORD last_error = ::GetLastError(); ec = asio::error_code(last_error, asio::error::get_system_category()); return ec; } // Determine the initial serial port parameters. using namespace std; // For memset. ::DCB dcb; memset(&dcb, 0, sizeof(DCB)); dcb.DCBlength = sizeof(DCB); if (!::GetCommState(handle, &dcb)) { DWORD last_error = ::GetLastError(); ::CloseHandle(handle); ec = asio::error_code(last_error, asio::error::get_system_category()); return ec; } // Set some default serial port parameters. This implementation does not // support changing these, so they might as well be in a known state. dcb.fBinary = TRUE; // Win32 only supports binary mode. dcb.fDsrSensitivity = FALSE; dcb.fNull = FALSE; // Do not ignore NULL characters. dcb.fAbortOnError = FALSE; // Ignore serial framing errors. if (!::SetCommState(handle, &dcb)) { DWORD last_error = ::GetLastError(); ::CloseHandle(handle); ec = asio::error_code(last_error, asio::error::get_system_category()); return ec; } // Set up timeouts so that the serial port will behave similarly to a // network socket. Reads wait for at least one byte, then return with // whatever they have. Writes return once everything is out the door. ::COMMTIMEOUTS timeouts; timeouts.ReadIntervalTimeout = 1; timeouts.ReadTotalTimeoutMultiplier = 0; timeouts.ReadTotalTimeoutConstant = 0; timeouts.WriteTotalTimeoutMultiplier = 0; timeouts.WriteTotalTimeoutConstant = 0; if (!::SetCommTimeouts(handle, &timeouts)) { DWORD last_error = ::GetLastError(); ::CloseHandle(handle); ec = asio::error_code(last_error, asio::error::get_system_category()); return ec; } // We're done. Take ownership of the serial port handle. if (handle_service_.assign(impl, handle, ec)) ::CloseHandle(handle); return ec; } asio::error_code win_iocp_serial_port_service::do_set_option( win_iocp_serial_port_service::implementation_type& impl, win_iocp_serial_port_service::store_function_type store, const void* option, asio::error_code& ec) { using namespace std; // For memcpy. ::DCB dcb; memset(&dcb, 0, sizeof(DCB)); dcb.DCBlength = sizeof(DCB); if (!::GetCommState(handle_service_.native(impl), &dcb)) { DWORD last_error = ::GetLastError(); ec = asio::error_code(last_error, asio::error::get_system_category()); return ec; } if (store(option, dcb, ec)) return ec; if (!::SetCommState(handle_service_.native(impl), &dcb)) { DWORD last_error = ::GetLastError(); ec = asio::error_code(last_error, asio::error::get_system_category()); return ec; } ec = asio::error_code(); return ec; } asio::error_code win_iocp_serial_port_service::do_get_option( const win_iocp_serial_port_service::implementation_type& impl, win_iocp_serial_port_service::load_function_type load, void* option, asio::error_code& ec) const { using namespace std; // For memset. ::DCB dcb; memset(&dcb, 0, sizeof(DCB)); dcb.DCBlength = sizeof(DCB); if (!::GetCommState(handle_service_.native(impl), &dcb)) { DWORD last_error = ::GetLastError(); ec = asio::error_code(last_error, asio::error::get_system_category()); return ec; } return load(option, dcb, ec); } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_IOCP) && defined(ASIO_HAS_SERIAL_PORT) #endif // ASIO_DETAIL_IMPL_WIN_IOCP_SERIAL_PORT_SERVICE_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/win_iocp_socket_service_base.ipp0000644000000000000000000004233212247075736031352 0ustar rootroot00000000000000// // detail/impl/win_iocp_socket_service_base.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_WIN_IOCP_SOCKET_SERVICE_BASE_IPP #define ASIO_DETAIL_IMPL_WIN_IOCP_SOCKET_SERVICE_BASE_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) #include "asio/detail/win_iocp_socket_service_base.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { win_iocp_socket_service_base::win_iocp_socket_service_base( asio::io_service& io_service) : io_service_(io_service), iocp_service_(use_service(io_service)), reactor_(0), mutex_(), impl_list_(0) { } void win_iocp_socket_service_base::shutdown_service() { // Close all implementations, causing all operations to complete. asio::detail::mutex::scoped_lock lock(mutex_); base_implementation_type* impl = impl_list_; while (impl) { asio::error_code ignored_ec; close_for_destruction(*impl); impl = impl->next_; } } void win_iocp_socket_service_base::construct( win_iocp_socket_service_base::base_implementation_type& impl) { impl.socket_ = invalid_socket; impl.state_ = 0; impl.cancel_token_.reset(); #if defined(ASIO_ENABLE_CANCELIO) impl.safe_cancellation_thread_id_ = 0; #endif // defined(ASIO_ENABLE_CANCELIO) // Insert implementation into linked list of all implementations. asio::detail::mutex::scoped_lock lock(mutex_); impl.next_ = impl_list_; impl.prev_ = 0; if (impl_list_) impl_list_->prev_ = &impl; impl_list_ = &impl; } void win_iocp_socket_service_base::destroy( win_iocp_socket_service_base::base_implementation_type& impl) { close_for_destruction(impl); // Remove implementation from linked list of all implementations. asio::detail::mutex::scoped_lock lock(mutex_); if (impl_list_ == &impl) impl_list_ = impl.next_; if (impl.prev_) impl.prev_->next_ = impl.next_; if (impl.next_) impl.next_->prev_= impl.prev_; impl.next_ = 0; impl.prev_ = 0; } asio::error_code win_iocp_socket_service_base::close( win_iocp_socket_service_base::base_implementation_type& impl, asio::error_code& ec) { if (is_open(impl)) { // Check if the reactor was created, in which case we need to close the // socket on the reactor as well to cancel any operations that might be // running there. reactor* r = static_cast( interlocked_compare_exchange_pointer( reinterpret_cast(&reactor_), 0, 0)); if (r) r->close_descriptor(impl.socket_, impl.reactor_data_); } if (socket_ops::close(impl.socket_, impl.state_, false, ec) == 0) { impl.socket_ = invalid_socket; impl.state_ = 0; impl.cancel_token_.reset(); #if defined(ASIO_ENABLE_CANCELIO) impl.safe_cancellation_thread_id_ = 0; #endif // defined(ASIO_ENABLE_CANCELIO) } return ec; } asio::error_code win_iocp_socket_service_base::cancel( win_iocp_socket_service_base::base_implementation_type& impl, asio::error_code& ec) { if (!is_open(impl)) { ec = asio::error::bad_descriptor; return ec; } else if (FARPROC cancel_io_ex_ptr = ::GetProcAddress( ::GetModuleHandleA("KERNEL32"), "CancelIoEx")) { // The version of Windows supports cancellation from any thread. typedef BOOL (WINAPI* cancel_io_ex_t)(HANDLE, LPOVERLAPPED); cancel_io_ex_t cancel_io_ex = (cancel_io_ex_t)cancel_io_ex_ptr; socket_type sock = impl.socket_; HANDLE sock_as_handle = reinterpret_cast(sock); if (!cancel_io_ex(sock_as_handle, 0)) { DWORD last_error = ::GetLastError(); if (last_error == ERROR_NOT_FOUND) { // ERROR_NOT_FOUND means that there were no operations to be // cancelled. We swallow this error to match the behaviour on other // platforms. ec = asio::error_code(); } else { ec = asio::error_code(last_error, asio::error::get_system_category()); } } else { ec = asio::error_code(); } } #if defined(ASIO_ENABLE_CANCELIO) else if (impl.safe_cancellation_thread_id_ == 0) { // No operations have been started, so there's nothing to cancel. ec = asio::error_code(); } else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId()) { // Asynchronous operations have been started from the current thread only, // so it is safe to try to cancel them using CancelIo. socket_type sock = impl.socket_; HANDLE sock_as_handle = reinterpret_cast(sock); if (!::CancelIo(sock_as_handle)) { DWORD last_error = ::GetLastError(); ec = asio::error_code(last_error, asio::error::get_system_category()); } else { ec = asio::error_code(); } } else { // Asynchronous operations have been started from more than one thread, // so cancellation is not safe. ec = asio::error::operation_not_supported; } #else // defined(ASIO_ENABLE_CANCELIO) else { // Cancellation is not supported as CancelIo may not be used. ec = asio::error::operation_not_supported; } #endif // defined(ASIO_ENABLE_CANCELIO) // Cancel any operations started via the reactor. if (!ec) { reactor* r = static_cast( interlocked_compare_exchange_pointer( reinterpret_cast(&reactor_), 0, 0)); if (r) r->cancel_ops(impl.socket_, impl.reactor_data_); } return ec; } asio::error_code win_iocp_socket_service_base::do_open( win_iocp_socket_service_base::base_implementation_type& impl, int family, int type, int protocol, asio::error_code& ec) { if (is_open(impl)) { ec = asio::error::already_open; return ec; } socket_holder sock(socket_ops::socket(family, type, protocol, ec)); if (sock.get() == invalid_socket) return ec; HANDLE sock_as_handle = reinterpret_cast(sock.get()); if (iocp_service_.register_handle(sock_as_handle, ec)) return ec; impl.socket_ = sock.release(); switch (type) { case SOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break; case SOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break; default: impl.state_ = 0; break; } impl.cancel_token_.reset(static_cast(0), socket_ops::noop_deleter()); ec = asio::error_code(); return ec; } asio::error_code win_iocp_socket_service_base::do_assign( win_iocp_socket_service_base::base_implementation_type& impl, int type, socket_type native_socket, asio::error_code& ec) { if (is_open(impl)) { ec = asio::error::already_open; return ec; } HANDLE sock_as_handle = reinterpret_cast(native_socket); if (iocp_service_.register_handle(sock_as_handle, ec)) return ec; impl.socket_ = native_socket; switch (type) { case SOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break; case SOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break; default: impl.state_ = 0; break; } impl.cancel_token_.reset(static_cast(0), socket_ops::noop_deleter()); ec = asio::error_code(); return ec; } void win_iocp_socket_service_base::start_send_op( win_iocp_socket_service_base::base_implementation_type& impl, WSABUF* buffers, std::size_t buffer_count, socket_base::message_flags flags, bool noop, operation* op) { update_cancellation_thread_id(impl); iocp_service_.work_started(); if (noop) iocp_service_.on_completion(op); else if (!is_open(impl)) iocp_service_.on_completion(op, asio::error::bad_descriptor); else { DWORD bytes_transferred = 0; int result = ::WSASend(impl.socket_, buffers, static_cast(buffer_count), &bytes_transferred, flags, op, 0); DWORD last_error = ::WSAGetLastError(); if (last_error == ERROR_PORT_UNREACHABLE) last_error = WSAECONNREFUSED; if (result != 0 && last_error != WSA_IO_PENDING) iocp_service_.on_completion(op, last_error, bytes_transferred); else iocp_service_.on_pending(op); } } void win_iocp_socket_service_base::start_send_to_op( win_iocp_socket_service_base::base_implementation_type& impl, WSABUF* buffers, std::size_t buffer_count, const socket_addr_type* addr, int addrlen, socket_base::message_flags flags, operation* op) { update_cancellation_thread_id(impl); iocp_service_.work_started(); if (!is_open(impl)) iocp_service_.on_completion(op, asio::error::bad_descriptor); else { DWORD bytes_transferred = 0; int result = ::WSASendTo(impl.socket_, buffers, static_cast(buffer_count), &bytes_transferred, flags, addr, addrlen, op, 0); DWORD last_error = ::WSAGetLastError(); if (last_error == ERROR_PORT_UNREACHABLE) last_error = WSAECONNREFUSED; if (result != 0 && last_error != WSA_IO_PENDING) iocp_service_.on_completion(op, last_error, bytes_transferred); else iocp_service_.on_pending(op); } } void win_iocp_socket_service_base::start_receive_op( win_iocp_socket_service_base::base_implementation_type& impl, WSABUF* buffers, std::size_t buffer_count, socket_base::message_flags flags, bool noop, operation* op) { update_cancellation_thread_id(impl); iocp_service_.work_started(); if (noop) iocp_service_.on_completion(op); else if (!is_open(impl)) iocp_service_.on_completion(op, asio::error::bad_descriptor); else { DWORD bytes_transferred = 0; DWORD recv_flags = flags; int result = ::WSARecv(impl.socket_, buffers, static_cast(buffer_count), &bytes_transferred, &recv_flags, op, 0); DWORD last_error = ::WSAGetLastError(); if (last_error == ERROR_NETNAME_DELETED) last_error = WSAECONNRESET; else if (last_error == ERROR_PORT_UNREACHABLE) last_error = WSAECONNREFUSED; if (result != 0 && last_error != WSA_IO_PENDING) iocp_service_.on_completion(op, last_error, bytes_transferred); else iocp_service_.on_pending(op); } } void win_iocp_socket_service_base::start_null_buffers_receive_op( win_iocp_socket_service_base::base_implementation_type& impl, socket_base::message_flags flags, reactor_op* op) { if ((impl.state_ & socket_ops::stream_oriented) != 0) { // For stream sockets on Windows, we may issue a 0-byte overlapped // WSARecv to wait until there is data available on the socket. ::WSABUF buf = { 0, 0 }; start_receive_op(impl, &buf, 1, flags, false, op); } else { start_reactor_op(impl, (flags & socket_base::message_out_of_band) ? reactor::except_op : reactor::read_op, op); } } void win_iocp_socket_service_base::start_receive_from_op( win_iocp_socket_service_base::base_implementation_type& impl, WSABUF* buffers, std::size_t buffer_count, socket_addr_type* addr, socket_base::message_flags flags, int* addrlen, operation* op) { update_cancellation_thread_id(impl); iocp_service_.work_started(); if (!is_open(impl)) iocp_service_.on_completion(op, asio::error::bad_descriptor); else { DWORD bytes_transferred = 0; DWORD recv_flags = flags; int result = ::WSARecvFrom(impl.socket_, buffers, static_cast(buffer_count), &bytes_transferred, &recv_flags, addr, addrlen, op, 0); DWORD last_error = ::WSAGetLastError(); if (last_error == ERROR_PORT_UNREACHABLE) last_error = WSAECONNREFUSED; if (result != 0 && last_error != WSA_IO_PENDING) iocp_service_.on_completion(op, last_error, bytes_transferred); else iocp_service_.on_pending(op); } } void win_iocp_socket_service_base::start_accept_op( win_iocp_socket_service_base::base_implementation_type& impl, bool peer_is_open, socket_holder& new_socket, int family, int type, int protocol, void* output_buffer, DWORD address_length, operation* op) { update_cancellation_thread_id(impl); iocp_service_.work_started(); if (!is_open(impl)) iocp_service_.on_completion(op, asio::error::bad_descriptor); else if (peer_is_open) iocp_service_.on_completion(op, asio::error::already_open); else { asio::error_code ec; new_socket.reset(socket_ops::socket(family, type, protocol, ec)); if (new_socket.get() == invalid_socket) iocp_service_.on_completion(op, ec); else { DWORD bytes_read = 0; BOOL result = ::AcceptEx(impl.socket_, new_socket.get(), output_buffer, 0, address_length, address_length, &bytes_read, op); DWORD last_error = ::WSAGetLastError(); if (!result && last_error != WSA_IO_PENDING) iocp_service_.on_completion(op, last_error); else iocp_service_.on_pending(op); } } } void win_iocp_socket_service_base::restart_accept_op( socket_type s, socket_holder& new_socket, int family, int type, int protocol, void* output_buffer, DWORD address_length, operation* op) { new_socket.reset(); iocp_service_.work_started(); asio::error_code ec; new_socket.reset(socket_ops::socket(family, type, protocol, ec)); if (new_socket.get() == invalid_socket) iocp_service_.on_completion(op, ec); else { DWORD bytes_read = 0; BOOL result = ::AcceptEx(s, new_socket.get(), output_buffer, 0, address_length, address_length, &bytes_read, op); DWORD last_error = ::WSAGetLastError(); if (!result && last_error != WSA_IO_PENDING) iocp_service_.on_completion(op, last_error); else iocp_service_.on_pending(op); } } void win_iocp_socket_service_base::start_reactor_op( win_iocp_socket_service_base::base_implementation_type& impl, int op_type, reactor_op* op) { reactor& r = get_reactor(); update_cancellation_thread_id(impl); if (is_open(impl)) { r.start_op(op_type, impl.socket_, impl.reactor_data_, op, false); return; } else op->ec_ = asio::error::bad_descriptor; iocp_service_.post_immediate_completion(op); } void win_iocp_socket_service_base::start_connect_op( win_iocp_socket_service_base::base_implementation_type& impl, reactor_op* op, const socket_addr_type* addr, std::size_t addrlen) { reactor& r = get_reactor(); update_cancellation_thread_id(impl); if ((impl.state_ & socket_ops::non_blocking) != 0 || socket_ops::set_internal_non_blocking( impl.socket_, impl.state_, op->ec_)) { if (socket_ops::connect(impl.socket_, addr, addrlen, op->ec_) != 0) { if (op->ec_ == asio::error::in_progress || op->ec_ == asio::error::would_block) { op->ec_ = asio::error_code(); r.start_op(reactor::connect_op, impl.socket_, impl.reactor_data_, op, false); return; } } } r.post_immediate_completion(op); } void win_iocp_socket_service_base::close_for_destruction( win_iocp_socket_service_base::base_implementation_type& impl) { if (is_open(impl)) { // Check if the reactor was created, in which case we need to close the // socket on the reactor as well to cancel any operations that might be // running there. reactor* r = static_cast( interlocked_compare_exchange_pointer( reinterpret_cast(&reactor_), 0, 0)); if (r) r->close_descriptor(impl.socket_, impl.reactor_data_); } asio::error_code ignored_ec; socket_ops::close(impl.socket_, impl.state_, true, ignored_ec); impl.socket_ = invalid_socket; impl.state_ = 0; impl.cancel_token_.reset(); #if defined(ASIO_ENABLE_CANCELIO) impl.safe_cancellation_thread_id_ = 0; #endif // defined(ASIO_ENABLE_CANCELIO) } void win_iocp_socket_service_base::update_cancellation_thread_id( win_iocp_socket_service_base::base_implementation_type& impl) { #if defined(ASIO_ENABLE_CANCELIO) if (impl.safe_cancellation_thread_id_ == 0) impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) impl.safe_cancellation_thread_id_ = ~DWORD(0); #else // defined(ASIO_ENABLE_CANCELIO) (void)impl; #endif // defined(ASIO_ENABLE_CANCELIO) } reactor& win_iocp_socket_service_base::get_reactor() { reactor* r = static_cast( interlocked_compare_exchange_pointer( reinterpret_cast(&reactor_), 0, 0)); if (!r) { r = &(use_service(io_service_)); interlocked_exchange_pointer(reinterpret_cast(&reactor_), r); } return *r; } void* win_iocp_socket_service_base::interlocked_compare_exchange_pointer( void** dest, void* exch, void* cmp) { #if defined(_M_IX86) return reinterpret_cast(InterlockedCompareExchange( reinterpret_cast(dest), reinterpret_cast(exch), reinterpret_cast(cmp))); #else return InterlockedCompareExchangePointer(dest, exch, cmp); #endif } void* win_iocp_socket_service_base::interlocked_exchange_pointer( void** dest, void* val) { #if defined(_M_IX86) return reinterpret_cast(InterlockedExchange( reinterpret_cast(dest), reinterpret_cast(val))); #else return InterlockedExchangePointer(dest, val); #endif } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_IOCP) #endif // ASIO_DETAIL_IMPL_WIN_IOCP_SOCKET_SERVICE_BASE_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/win_mutex.ipp0000644000000000000000000000344312247075736025500 0ustar rootroot00000000000000// // detail/impl/win_mutex.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_WIN_MUTEX_IPP #define ASIO_DETAIL_IMPL_WIN_MUTEX_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) #include "asio/detail/throw_error.hpp" #include "asio/detail/win_mutex.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { win_mutex::win_mutex() { int error = do_init(); asio::error_code ec(error, asio::error::get_system_category()); asio::detail::throw_error(ec, "mutex"); } int win_mutex::do_init() { #if defined(__MINGW32__) // Not sure if MinGW supports structured exception handling, so for now // we'll just call the Windows API and hope. # if defined(UNDER_CE) ::InitializeCriticalSection(&crit_section_); # else if (!::InitializeCriticalSectionAndSpinCount(&crit_section_, 0x80000000)) return ::GetLastError(); # endif return 0; #else __try { # if defined(UNDER_CE) ::InitializeCriticalSection(&crit_section_); # else if (!::InitializeCriticalSectionAndSpinCount(&crit_section_, 0x80000000)) return ::GetLastError(); # endif } __except(GetExceptionCode() == STATUS_NO_MEMORY ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { return ERROR_OUTOFMEMORY; } return 0; #endif } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(BOOST_WINDOWS) #endif // ASIO_DETAIL_IMPL_WIN_MUTEX_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/win_thread.ipp0000644000000000000000000000707412247075736025611 0ustar rootroot00000000000000// // detail/impl/win_thread.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_WIN_THREAD_IPP #define ASIO_DETAIL_IMPL_WIN_THREAD_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) && !defined(UNDER_CE) #include #include "asio/detail/throw_error.hpp" #include "asio/detail/win_thread.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { win_thread::~win_thread() { ::CloseHandle(thread_); // The exit_event_ handle is deliberately allowed to leak here since it // is an error for the owner of an internal thread not to join() it. } void win_thread::join() { HANDLE handles[2] = { exit_event_, thread_ }; ::WaitForMultipleObjects(2, handles, FALSE, INFINITE); ::CloseHandle(exit_event_); if (terminate_threads()) { ::TerminateThread(thread_, 0); } else { ::QueueUserAPC(apc_function, thread_, 0); ::WaitForSingleObject(thread_, INFINITE); } } void win_thread::start_thread(func_base* arg, unsigned int stack_size) { ::HANDLE entry_event = 0; arg->entry_event_ = entry_event = ::CreateEvent(0, true, false, 0); if (!entry_event) { DWORD last_error = ::GetLastError(); delete arg; asio::error_code ec(last_error, asio::error::get_system_category()); asio::detail::throw_error(ec, "thread.entry_event"); } arg->exit_event_ = exit_event_ = ::CreateEvent(0, true, false, 0); if (!exit_event_) { DWORD last_error = ::GetLastError(); delete arg; asio::error_code ec(last_error, asio::error::get_system_category()); asio::detail::throw_error(ec, "thread.exit_event"); } unsigned int thread_id = 0; thread_ = reinterpret_cast(::_beginthreadex(0, stack_size, win_thread_function, arg, 0, &thread_id)); if (!thread_) { DWORD last_error = ::GetLastError(); delete arg; if (entry_event) ::CloseHandle(entry_event); if (exit_event_) ::CloseHandle(exit_event_); asio::error_code ec(last_error, asio::error::get_system_category()); asio::detail::throw_error(ec, "thread"); } if (entry_event) { ::WaitForSingleObject(entry_event, INFINITE); ::CloseHandle(entry_event); } } unsigned int __stdcall win_thread_function(void* arg) { std::auto_ptr func( static_cast(arg)); ::SetEvent(func->entry_event_); func->run(); // Signal that the thread has finished its work, but rather than returning go // to sleep to put the thread into a well known state. If the thread is being // joined during global object destruction then it may be killed using // TerminateThread (to avoid a deadlock in DllMain). Otherwise, the SleepEx // call will be interrupted using QueueUserAPC and the thread will shut down // cleanly. HANDLE exit_event = func->exit_event_; func.reset(); ::SetEvent(exit_event); ::SleepEx(INFINITE, TRUE); return 0; } #if defined(WINVER) && (WINVER < 0x0500) void __stdcall apc_function(ULONG) {} #else void __stdcall apc_function(ULONG_PTR) {} #endif } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(BOOST_WINDOWS) && !defined(UNDER_CE) #endif // ASIO_DETAIL_IMPL_WIN_THREAD_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/win_tss_ptr.ipp0000644000000000000000000000244412247075736026034 0ustar rootroot00000000000000// // detail/impl/win_tss_ptr.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_WIN_TSS_PTR_IPP #define ASIO_DETAIL_IMPL_WIN_TSS_PTR_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) #include "asio/detail/throw_error.hpp" #include "asio/detail/win_tss_ptr.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { DWORD win_tss_ptr_create() { #if defined(UNDER_CE) enum { out_of_indexes = 0xFFFFFFFF }; #else enum { out_of_indexes = TLS_OUT_OF_INDEXES }; #endif DWORD tss_key = ::TlsAlloc(); if (tss_key == out_of_indexes) { DWORD last_error = ::GetLastError(); asio::error_code ec(last_error, asio::error::get_system_category()); asio::detail::throw_error(ec, "tss"); } return tss_key; } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(BOOST_WINDOWS) #endif // ASIO_DETAIL_IMPL_WIN_TSS_PTR_IPP percona-xtradb-cluster-galera/asio/asio/detail/impl/winsock_init.ipp0000644000000000000000000000320512247075736026155 0ustar rootroot00000000000000// // detail/impl/winsock_init.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_DETAIL_IMPL_WINSOCK_INIT_IPP #define ASIO_DETAIL_IMPL_WINSOCK_INIT_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) #include "asio/detail/socket_types.hpp" #include "asio/detail/winsock_init.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace detail { void winsock_init_base::startup(data& d, unsigned char major, unsigned char minor) { if (::InterlockedIncrement(&d.init_count_) == 1) { WSADATA wsa_data; long result = ::WSAStartup(MAKEWORD(major, minor), &wsa_data); ::InterlockedExchange(&d.result_, result); } } void winsock_init_base::cleanup(data& d) { if (::InterlockedDecrement(&d.init_count_) == 0) { ::WSACleanup(); } } void winsock_init_base::throw_on_error(data& d) { long result = ::InterlockedExchangeAdd(&d.result_, 0); if (result != 0) { asio::error_code ec(result, asio::error::get_system_category()); asio::detail::throw_error(ec, "winsock"); } } } // namespace detail } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) #endif // ASIO_DETAIL_IMPL_WINSOCK_INIT_IPP percona-xtradb-cluster-galera/asio/asio/impl/error.ipp0000644000000000000000000000136412247075736023350 0ustar rootroot00000000000000// // impl/error.ipp // ~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IMPL_ERROR_IPP #define ASIO_IMPL_ERROR_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace error { // boostify: error category function definitions go here. } // namespace error } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IMPL_ERROR_IPP percona-xtradb-cluster-galera/asio/asio/impl/error_code.ipp0000644000000000000000000000706612247075736024347 0ustar rootroot00000000000000// // impl/error_code.ipp // ~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IMPL_ERROR_CODE_IPP #define ASIO_IMPL_ERROR_CODE_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/detail/local_free_on_block_exit.hpp" #include "asio/detail/socket_types.hpp" #include "asio/error.hpp" #include "asio/error_code.hpp" #include "asio/detail/push_options.hpp" namespace asio { std::string error_code::message() const { if (category_ == error::get_misc_category()) { if (value_ == error::already_open) return "Already open."; if (value_ == error::not_found) return "Not found."; if (value_ == error::fd_set_failure) return "The descriptor does not fit into the select call's fd_set."; if (value_ == error::not_found) return "Element not found."; #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) if (value_ == error::eof) return "End of file."; #endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) } if (category_ == error::get_ssl_category()) return "SSL error."; #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) value_type value = value_; if (category_ == error::get_misc_category() && value_ == error::eof) value = ERROR_HANDLE_EOF; else if (category_ != error::get_system_category()) return "asio error"; char* msg = 0; DWORD length = ::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, value, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, 0); detail::local_free_on_block_exit local_free_obj(msg); if (length && msg[length - 1] == '\n') msg[--length] = '\0'; if (length && msg[length - 1] == '\r') msg[--length] = '\0'; if (length) return msg; else return "asio error"; #else // defined(BOOST_WINDOWS) if (category_ == error::get_netdb_category()) { if (value_ == error::host_not_found) return "Host not found (authoritative)."; if (value_ == error::host_not_found_try_again) return "Host not found (non-authoritative), try again later."; if (value_ == error::no_recovery) return "A non-recoverable error occurred during database lookup."; if (value_ == error::no_data) return "The query is valid, but it does not have associated data."; } if (category_ == error::get_addrinfo_category()) { if (value_ == error::service_not_found) return "Service not found."; if (value_ == error::socket_type_not_supported) return "Socket type not supported."; } if (category_ != error::get_system_category()) return "asio error"; #if !defined(__sun) if (value_ == error::operation_aborted) return "Operation aborted."; #endif // !defined(__sun) #if defined(__sun) || defined(__QNX__) || defined(__SYMBIAN32__) using namespace std; return strerror(value_); #elif defined(__MACH__) && defined(__APPLE__) \ || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) \ || defined(_AIX) || defined(__hpux) || defined(__osf__) char buf[256] = ""; strerror_r(value_, buf, sizeof(buf)); return buf; #else char buf[256] = ""; return strerror_r(value_, buf, sizeof(buf)); #endif #endif // defined(BOOST_WINDOWS) } } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IMPL_ERROR_CODE_IPP percona-xtradb-cluster-galera/asio/asio/impl/io_service.hpp0000644000000000000000000000576012247075736024351 0ustar rootroot00000000000000// // impl/io_service.hpp // ~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IMPL_IO_SERVICE_HPP #define ASIO_IMPL_IO_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/service_registry.hpp" #include "asio/detail/push_options.hpp" namespace asio { template inline Service& use_service(io_service& ios) { // Check that Service meets the necessary type requirements. (void)static_cast(static_cast(0)); (void)static_cast(&Service::id); return ios.service_registry_->template use_service(); } template inline void add_service(io_service& ios, Service* svc) { // Check that Service meets the necessary type requirements. (void)static_cast(static_cast(0)); (void)static_cast(&Service::id); ios.service_registry_->template add_service(svc); } template inline bool has_service(io_service& ios) { // Check that Service meets the necessary type requirements. (void)static_cast(static_cast(0)); (void)static_cast(&Service::id); return ios.service_registry_->template has_service(); } } // namespace asio #include "asio/detail/pop_options.hpp" #if defined(ASIO_HAS_IOCP) # include "asio/detail/win_iocp_io_service.hpp" #else # include "asio/detail/task_io_service.hpp" #endif #include "asio/detail/push_options.hpp" namespace asio { template inline void io_service::dispatch(Handler handler) { impl_.dispatch(handler); } template inline void io_service::post(Handler handler) { impl_.post(handler); } template #if defined(GENERATING_DOCUMENTATION) unspecified #else inline detail::wrapped_handler #endif io_service::wrap(Handler handler) { return detail::wrapped_handler(*this, handler); } inline io_service::work::work(asio::io_service& io_service) : io_service_(io_service) { io_service_.impl_.work_started(); } inline io_service::work::work(const work& other) : io_service_(other.io_service_) { io_service_.impl_.work_started(); } inline io_service::work::~work() { io_service_.impl_.work_finished(); } inline asio::io_service& io_service::work::io_service() { return io_service_; } inline asio::io_service& io_service::work::get_io_service() { return io_service_; } inline asio::io_service& io_service::service::io_service() { return owner_; } inline asio::io_service& io_service::service::get_io_service() { return owner_; } } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IMPL_IO_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/impl/io_service.ipp0000644000000000000000000000525312247075736024347 0ustar rootroot00000000000000// // impl/io_service.ipp // ~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IMPL_IO_SERVICE_IPP #define ASIO_IMPL_IO_SERVICE_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/io_service.hpp" #include "asio/detail/service_registry.hpp" #include "asio/detail/throw_error.hpp" #if defined(ASIO_HAS_IOCP) # include "asio/detail/win_iocp_io_service.hpp" #else # include "asio/detail/task_io_service.hpp" #endif #include "asio/detail/push_options.hpp" namespace asio { io_service::io_service() : service_registry_(new asio::detail::service_registry(*this)), impl_(service_registry_->use_service()) { impl_.init((std::numeric_limits::max)()); } io_service::io_service(std::size_t concurrency_hint) : service_registry_(new asio::detail::service_registry(*this)), impl_(service_registry_->use_service()) { impl_.init(concurrency_hint); } io_service::~io_service() { delete service_registry_; } std::size_t io_service::run() { asio::error_code ec; std::size_t s = impl_.run(ec); asio::detail::throw_error(ec); return s; } std::size_t io_service::run(asio::error_code& ec) { return impl_.run(ec); } std::size_t io_service::run_one() { asio::error_code ec; std::size_t s = impl_.run_one(ec); asio::detail::throw_error(ec); return s; } std::size_t io_service::run_one(asio::error_code& ec) { return impl_.run_one(ec); } std::size_t io_service::poll() { asio::error_code ec; std::size_t s = impl_.poll(ec); asio::detail::throw_error(ec); return s; } std::size_t io_service::poll(asio::error_code& ec) { return impl_.poll(ec); } std::size_t io_service::poll_one() { asio::error_code ec; std::size_t s = impl_.poll_one(ec); asio::detail::throw_error(ec); return s; } std::size_t io_service::poll_one(asio::error_code& ec) { return impl_.poll_one(ec); } void io_service::stop() { impl_.stop(); } void io_service::reset() { impl_.reset(); } io_service::service::service(asio::io_service& owner) : owner_(owner), next_(0) { } io_service::service::~service() { } service_already_exists::service_already_exists() : std::logic_error("Service already exists.") { } invalid_service_owner::invalid_service_owner() : std::logic_error("Invalid service owner.") { } } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IMPL_IO_SERVICE_IPP percona-xtradb-cluster-galera/asio/asio/impl/read.hpp0000644000000000000000000003104312247075736023126 0ustar rootroot00000000000000// // impl/read.hpp // ~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IMPL_READ_HPP #define ASIO_IMPL_READ_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include #include "asio/buffer.hpp" #include "asio/completion_condition.hpp" #include "asio/detail/base_from_completion_cond.hpp" #include "asio/detail/bind_handler.hpp" #include "asio/detail/consuming_buffers.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { template std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers, CompletionCondition completion_condition, asio::error_code& ec) { ec = asio::error_code(); asio::detail::consuming_buffers< mutable_buffer, MutableBufferSequence> tmp(buffers); std::size_t total_transferred = 0; tmp.prepare(detail::adapt_completion_condition_result( completion_condition(ec, total_transferred))); while (tmp.begin() != tmp.end()) { std::size_t bytes_transferred = s.read_some(tmp, ec); tmp.consume(bytes_transferred); total_transferred += bytes_transferred; tmp.prepare(detail::adapt_completion_condition_result( completion_condition(ec, total_transferred))); } return total_transferred; } template inline std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers) { asio::error_code ec; std::size_t bytes_transferred = read(s, buffers, transfer_all(), ec); asio::detail::throw_error(ec); return bytes_transferred; } template inline std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers, CompletionCondition completion_condition) { asio::error_code ec; std::size_t bytes_transferred = read(s, buffers, completion_condition, ec); asio::detail::throw_error(ec); return bytes_transferred; } #if !defined(BOOST_NO_IOSTREAM) template std::size_t read(SyncReadStream& s, asio::basic_streambuf& b, CompletionCondition completion_condition, asio::error_code& ec) { ec = asio::error_code(); std::size_t total_transferred = 0; std::size_t max_size = detail::adapt_completion_condition_result( completion_condition(ec, total_transferred)); std::size_t bytes_available = read_size_helper(b, max_size); while (bytes_available > 0) { std::size_t bytes_transferred = s.read_some(b.prepare(bytes_available), ec); b.commit(bytes_transferred); total_transferred += bytes_transferred; max_size = detail::adapt_completion_condition_result( completion_condition(ec, total_transferred)); bytes_available = read_size_helper(b, max_size); } return total_transferred; } template inline std::size_t read(SyncReadStream& s, asio::basic_streambuf& b) { asio::error_code ec; std::size_t bytes_transferred = read(s, b, transfer_all(), ec); asio::detail::throw_error(ec); return bytes_transferred; } template inline std::size_t read(SyncReadStream& s, asio::basic_streambuf& b, CompletionCondition completion_condition) { asio::error_code ec; std::size_t bytes_transferred = read(s, b, completion_condition, ec); asio::detail::throw_error(ec); return bytes_transferred; } #endif // !defined(BOOST_NO_IOSTREAM) namespace detail { template class read_op : detail::base_from_completion_cond { public: read_op(AsyncReadStream& stream, const MutableBufferSequence& buffers, CompletionCondition completion_condition, ReadHandler handler) : detail::base_from_completion_cond< CompletionCondition>(completion_condition), stream_(stream), buffers_(buffers), total_transferred_(0), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred, int start = 0) { switch (start) { case 1: buffers_.prepare(this->check_for_completion(ec, total_transferred_)); for (;;) { stream_.async_read_some(buffers_, *this); return; default: total_transferred_ += bytes_transferred; buffers_.consume(bytes_transferred); buffers_.prepare(this->check_for_completion(ec, total_transferred_)); if ((!ec && bytes_transferred == 0) || buffers_.begin() == buffers_.end()) break; } handler_(ec, static_cast(total_transferred_)); } } //private: AsyncReadStream& stream_; asio::detail::consuming_buffers< mutable_buffer, MutableBufferSequence> buffers_; std::size_t total_transferred_; ReadHandler handler_; }; template class read_op : detail::base_from_completion_cond { public: read_op(AsyncReadStream& stream, const asio::mutable_buffers_1& buffers, CompletionCondition completion_condition, ReadHandler handler) : detail::base_from_completion_cond< CompletionCondition>(completion_condition), stream_(stream), buffer_(buffers), total_transferred_(0), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred, int start = 0) { std::size_t n = 0; switch (start) { case 1: n = this->check_for_completion(ec, total_transferred_); for (;;) { stream_.async_read_some(asio::buffer( buffer_ + total_transferred_, n), *this); return; default: total_transferred_ += bytes_transferred; if ((!ec && bytes_transferred == 0) || (n = this->check_for_completion(ec, total_transferred_)) == 0 || total_transferred_ == asio::buffer_size(buffer_)) break; } handler_(ec, static_cast(total_transferred_)); } } //private: AsyncReadStream& stream_; asio::mutable_buffer buffer_; std::size_t total_transferred_; ReadHandler handler_; }; template inline void* asio_handler_allocate(std::size_t size, read_op* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, read_op* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, read_op* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } } // namespace detail template inline void async_read(AsyncReadStream& s, const MutableBufferSequence& buffers, CompletionCondition completion_condition, ReadHandler handler) { detail::read_op( s, buffers, completion_condition, handler)( asio::error_code(), 0, 1); } template inline void async_read(AsyncReadStream& s, const MutableBufferSequence& buffers, ReadHandler handler) { async_read(s, buffers, transfer_all(), handler); } #if !defined(BOOST_NO_IOSTREAM) namespace detail { template class read_streambuf_op : detail::base_from_completion_cond { public: read_streambuf_op(AsyncReadStream& stream, basic_streambuf& streambuf, CompletionCondition completion_condition, ReadHandler handler) : detail::base_from_completion_cond< CompletionCondition>(completion_condition), stream_(stream), streambuf_(streambuf), total_transferred_(0), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred, int start = 0) { std::size_t max_size, bytes_available; switch (start) { case 1: max_size = this->check_for_completion(ec, total_transferred_); bytes_available = read_size_helper(streambuf_, max_size); for (;;) { stream_.async_read_some(streambuf_.prepare(bytes_available), *this); return; default: total_transferred_ += bytes_transferred; streambuf_.commit(bytes_transferred); max_size = this->check_for_completion(ec, total_transferred_); bytes_available = read_size_helper(streambuf_, max_size); if ((!ec && bytes_transferred == 0) || bytes_available == 0) break; } handler_(ec, static_cast(total_transferred_)); } } //private: AsyncReadStream& stream_; asio::basic_streambuf& streambuf_; std::size_t total_transferred_; ReadHandler handler_; }; template inline void* asio_handler_allocate(std::size_t size, read_streambuf_op* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, read_streambuf_op* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, read_streambuf_op* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } } // namespace detail template inline void async_read(AsyncReadStream& s, asio::basic_streambuf& b, CompletionCondition completion_condition, ReadHandler handler) { detail::read_streambuf_op( s, b, completion_condition, handler)( asio::error_code(), 0, 1); } template inline void async_read(AsyncReadStream& s, asio::basic_streambuf& b, ReadHandler handler) { async_read(s, b, transfer_all(), handler); } #endif // !defined(BOOST_NO_IOSTREAM) } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IMPL_READ_HPP percona-xtradb-cluster-galera/asio/asio/impl/read.ipp0000644000000000000000000003151712247075736023135 0ustar rootroot00000000000000// // read.ipp // ~~~~~~~~ // // Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_READ_IPP #define ASIO_READ_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/push_options.hpp" #include "asio/detail/push_options.hpp" #include #include "asio/detail/pop_options.hpp" #include "asio/buffer.hpp" #include "asio/completion_condition.hpp" #include "asio/error.hpp" #include "asio/detail/base_from_completion_cond.hpp" #include "asio/detail/bind_handler.hpp" #include "asio/detail/consuming_buffers.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/throw_error.hpp" namespace asio { template std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers, CompletionCondition completion_condition, asio::error_code& ec) { ec = asio::error_code(); asio::detail::consuming_buffers< mutable_buffer, MutableBufferSequence> tmp(buffers); std::size_t total_transferred = 0; tmp.prepare(detail::adapt_completion_condition_result( completion_condition(ec, total_transferred))); while (tmp.begin() != tmp.end()) { std::size_t bytes_transferred = s.read_some(tmp, ec); tmp.consume(bytes_transferred); total_transferred += bytes_transferred; tmp.prepare(detail::adapt_completion_condition_result( completion_condition(ec, total_transferred))); } return total_transferred; } template inline std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers) { asio::error_code ec; std::size_t bytes_transferred = read(s, buffers, transfer_all(), ec); asio::detail::throw_error(ec); return bytes_transferred; } template inline std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers, CompletionCondition completion_condition) { asio::error_code ec; std::size_t bytes_transferred = read(s, buffers, completion_condition, ec); asio::detail::throw_error(ec); return bytes_transferred; } #if !defined(BOOST_NO_IOSTREAM) template std::size_t read(SyncReadStream& s, asio::basic_streambuf& b, CompletionCondition completion_condition, asio::error_code& ec) { ec = asio::error_code(); std::size_t total_transferred = 0; std::size_t max_size = detail::adapt_completion_condition_result( completion_condition(ec, total_transferred)); std::size_t bytes_available = std::min(512, std::min(max_size, b.max_size() - b.size())); while (bytes_available > 0) { std::size_t bytes_transferred = s.read_some(b.prepare(bytes_available), ec); b.commit(bytes_transferred); total_transferred += bytes_transferred; max_size = detail::adapt_completion_condition_result( completion_condition(ec, total_transferred)); bytes_available = std::min(512, std::min(max_size, b.max_size() - b.size())); } return total_transferred; } template inline std::size_t read(SyncReadStream& s, asio::basic_streambuf& b) { asio::error_code ec; std::size_t bytes_transferred = read(s, b, transfer_all(), ec); asio::detail::throw_error(ec); return bytes_transferred; } template inline std::size_t read(SyncReadStream& s, asio::basic_streambuf& b, CompletionCondition completion_condition) { asio::error_code ec; std::size_t bytes_transferred = read(s, b, completion_condition, ec); asio::detail::throw_error(ec); return bytes_transferred; } #endif // !defined(BOOST_NO_IOSTREAM) namespace detail { template class read_op : detail::base_from_completion_cond { public: read_op(AsyncReadStream& stream, const MutableBufferSequence& buffers, CompletionCondition completion_condition, ReadHandler handler) : detail::base_from_completion_cond< CompletionCondition>(completion_condition), stream_(stream), buffers_(buffers), total_transferred_(0), handler_(handler), start_(true) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred) { switch (start_) { case true: start_ = false; buffers_.prepare(this->check(ec, total_transferred_)); for (;;) { stream_.async_read_some(buffers_, *this); return; default: total_transferred_ += bytes_transferred; buffers_.consume(bytes_transferred); buffers_.prepare(this->check(ec, total_transferred_)); if ((!ec && bytes_transferred == 0) || buffers_.begin() == buffers_.end()) break; } handler_(ec, total_transferred_); } } //private: AsyncReadStream& stream_; asio::detail::consuming_buffers< mutable_buffer, MutableBufferSequence> buffers_; std::size_t total_transferred_; ReadHandler handler_; bool start_; }; template class read_op : detail::base_from_completion_cond { public: read_op(AsyncReadStream& stream, const asio::mutable_buffers_1& buffers, CompletionCondition completion_condition, ReadHandler handler) : detail::base_from_completion_cond< CompletionCondition>(completion_condition), stream_(stream), buffer_(buffers), total_transferred_(0), handler_(handler), start_(true) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred) { std::size_t n = 0; switch (start_) { case true: start_ = false; n = this->check(ec, total_transferred_); for (;;) { stream_.async_read_some(asio::buffer( buffer_ + total_transferred_, n), *this); return; default: total_transferred_ += bytes_transferred; if ((!ec && bytes_transferred == 0) || (n = this->check(ec, total_transferred_)) == 0 || total_transferred_ == asio::buffer_size(buffer_)) break; } handler_(ec, total_transferred_); } } //private: AsyncReadStream& stream_; asio::mutable_buffer buffer_; std::size_t total_transferred_; ReadHandler handler_; bool start_; }; template inline void* asio_handler_allocate(std::size_t size, read_op* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, read_op* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, read_op* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } } // namespace detail template inline void async_read(AsyncReadStream& s, const MutableBufferSequence& buffers, CompletionCondition completion_condition, ReadHandler handler) { detail::read_op( s, buffers, completion_condition, handler)( asio::error_code(), 0); } template inline void async_read(AsyncReadStream& s, const MutableBufferSequence& buffers, ReadHandler handler) { async_read(s, buffers, transfer_all(), handler); } #if !defined(BOOST_NO_IOSTREAM) namespace detail { template class read_streambuf_op : detail::base_from_completion_cond { public: read_streambuf_op(AsyncReadStream& stream, basic_streambuf& streambuf, CompletionCondition completion_condition, ReadHandler handler) : detail::base_from_completion_cond< CompletionCondition>(completion_condition), stream_(stream), streambuf_(streambuf), total_transferred_(0), handler_(handler), start_(true) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred) { std::size_t max_size, bytes_available; switch (start_) { case true: start_ = false; max_size = this->check(ec, total_transferred_); bytes_available = std::min(512, std::min(max_size, streambuf_.max_size() - streambuf_.size())); for (;;) { stream_.async_read_some(streambuf_.prepare(bytes_available), *this); return; default: total_transferred_ += bytes_transferred; streambuf_.commit(bytes_transferred); max_size = this->check(ec, total_transferred_); bytes_available = std::min(512, std::min(max_size, streambuf_.max_size() - streambuf_.size())); if ((!ec && bytes_transferred == 0) || bytes_available == 0) break; } handler_(ec, total_transferred_); } } //private: AsyncReadStream& stream_; asio::basic_streambuf& streambuf_; std::size_t total_transferred_; ReadHandler handler_; bool start_; }; template inline void* asio_handler_allocate(std::size_t size, read_streambuf_op* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, read_streambuf_op* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, read_streambuf_op* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } } // namespace detail template inline void async_read(AsyncReadStream& s, asio::basic_streambuf& b, CompletionCondition completion_condition, ReadHandler handler) { detail::read_streambuf_op( s, b, completion_condition, handler)( asio::error_code(), 0); } template inline void async_read(AsyncReadStream& s, asio::basic_streambuf& b, ReadHandler handler) { async_read(s, b, transfer_all(), handler); } #endif // !defined(BOOST_NO_IOSTREAM) } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_READ_IPP percona-xtradb-cluster-galera/asio/asio/impl/read_at.hpp0000644000000000000000000003371512247075736023622 0ustar rootroot00000000000000// // impl/read_at.hpp // ~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IMPL_READ_AT_HPP #define ASIO_IMPL_READ_AT_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include #include "asio/buffer.hpp" #include "asio/completion_condition.hpp" #include "asio/detail/base_from_completion_cond.hpp" #include "asio/detail/bind_handler.hpp" #include "asio/detail/consuming_buffers.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { template std::size_t read_at(SyncRandomAccessReadDevice& d, boost::uint64_t offset, const MutableBufferSequence& buffers, CompletionCondition completion_condition, asio::error_code& ec) { ec = asio::error_code(); asio::detail::consuming_buffers< mutable_buffer, MutableBufferSequence> tmp(buffers); std::size_t total_transferred = 0; tmp.prepare(detail::adapt_completion_condition_result( completion_condition(ec, total_transferred))); while (tmp.begin() != tmp.end()) { std::size_t bytes_transferred = d.read_some_at( offset + total_transferred, tmp, ec); tmp.consume(bytes_transferred); total_transferred += bytes_transferred; tmp.prepare(detail::adapt_completion_condition_result( completion_condition(ec, total_transferred))); } return total_transferred; } template inline std::size_t read_at(SyncRandomAccessReadDevice& d, boost::uint64_t offset, const MutableBufferSequence& buffers) { asio::error_code ec; std::size_t bytes_transferred = read_at( d, offset, buffers, transfer_all(), ec); asio::detail::throw_error(ec); return bytes_transferred; } template inline std::size_t read_at(SyncRandomAccessReadDevice& d, boost::uint64_t offset, const MutableBufferSequence& buffers, CompletionCondition completion_condition) { asio::error_code ec; std::size_t bytes_transferred = read_at( d, offset, buffers, completion_condition, ec); asio::detail::throw_error(ec); return bytes_transferred; } #if !defined(BOOST_NO_IOSTREAM) template std::size_t read_at(SyncRandomAccessReadDevice& d, boost::uint64_t offset, asio::basic_streambuf& b, CompletionCondition completion_condition, asio::error_code& ec) { ec = asio::error_code(); std::size_t total_transferred = 0; std::size_t max_size = detail::adapt_completion_condition_result( completion_condition(ec, total_transferred)); std::size_t bytes_available = read_size_helper(b, max_size); while (bytes_available > 0) { std::size_t bytes_transferred = d.read_some_at( offset + total_transferred, b.prepare(bytes_available), ec); b.commit(bytes_transferred); total_transferred += bytes_transferred; max_size = detail::adapt_completion_condition_result( completion_condition(ec, total_transferred)); bytes_available = read_size_helper(b, max_size); } return total_transferred; } template inline std::size_t read_at(SyncRandomAccessReadDevice& d, boost::uint64_t offset, asio::basic_streambuf& b) { asio::error_code ec; std::size_t bytes_transferred = read_at( d, offset, b, transfer_all(), ec); asio::detail::throw_error(ec); return bytes_transferred; } template inline std::size_t read_at(SyncRandomAccessReadDevice& d, boost::uint64_t offset, asio::basic_streambuf& b, CompletionCondition completion_condition) { asio::error_code ec; std::size_t bytes_transferred = read_at( d, offset, b, completion_condition, ec); asio::detail::throw_error(ec); return bytes_transferred; } #endif // !defined(BOOST_NO_IOSTREAM) namespace detail { template class read_at_op : detail::base_from_completion_cond { public: read_at_op(AsyncRandomAccessReadDevice& device, boost::uint64_t offset, const MutableBufferSequence& buffers, CompletionCondition completion_condition, ReadHandler handler) : detail::base_from_completion_cond< CompletionCondition>(completion_condition), device_(device), offset_(offset), buffers_(buffers), total_transferred_(0), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred, int start = 0) { switch (start) { case 1: buffers_.prepare(this->check_for_completion(ec, total_transferred_)); for (;;) { device_.async_read_some_at( offset_ + total_transferred_, buffers_, *this); return; default: total_transferred_ += bytes_transferred; buffers_.consume(bytes_transferred); buffers_.prepare(this->check_for_completion(ec, total_transferred_)); if ((!ec && bytes_transferred == 0) || buffers_.begin() == buffers_.end()) break; } handler_(ec, static_cast(total_transferred_)); } } //private: AsyncRandomAccessReadDevice& device_; boost::uint64_t offset_; asio::detail::consuming_buffers< mutable_buffer, MutableBufferSequence> buffers_; std::size_t total_transferred_; ReadHandler handler_; }; template class read_at_op : detail::base_from_completion_cond { public: read_at_op(AsyncRandomAccessReadDevice& device, boost::uint64_t offset, const asio::mutable_buffers_1& buffers, CompletionCondition completion_condition, ReadHandler handler) : detail::base_from_completion_cond< CompletionCondition>(completion_condition), device_(device), offset_(offset), buffer_(buffers), total_transferred_(0), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred, int start = 0) { std::size_t n = 0; switch (start) { case 1: n = this->check_for_completion(ec, total_transferred_); for (;;) { device_.async_read_some_at(offset_ + total_transferred_, asio::buffer(buffer_ + total_transferred_, n), *this); return; default: total_transferred_ += bytes_transferred; if ((!ec && bytes_transferred == 0) || (n = this->check_for_completion(ec, total_transferred_)) == 0 || total_transferred_ == asio::buffer_size(buffer_)) break; } handler_(ec, static_cast(total_transferred_)); } } //private: AsyncRandomAccessReadDevice& device_; boost::uint64_t offset_; asio::mutable_buffer buffer_; std::size_t total_transferred_; ReadHandler handler_; }; template inline void* asio_handler_allocate(std::size_t size, read_at_op* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, read_at_op* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, read_at_op* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } } // namespace detail template inline void async_read_at(AsyncRandomAccessReadDevice& d, boost::uint64_t offset, const MutableBufferSequence& buffers, CompletionCondition completion_condition, ReadHandler handler) { detail::read_at_op( d, offset, buffers, completion_condition, handler)( asio::error_code(), 0, 1); } template inline void async_read_at(AsyncRandomAccessReadDevice& d, boost::uint64_t offset, const MutableBufferSequence& buffers, ReadHandler handler) { async_read_at(d, offset, buffers, transfer_all(), handler); } #if !defined(BOOST_NO_IOSTREAM) namespace detail { template class read_at_streambuf_op : detail::base_from_completion_cond { public: read_at_streambuf_op(AsyncRandomAccessReadDevice& device, boost::uint64_t offset, basic_streambuf& streambuf, CompletionCondition completion_condition, ReadHandler handler) : detail::base_from_completion_cond< CompletionCondition>(completion_condition), device_(device), offset_(offset), streambuf_(streambuf), total_transferred_(0), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred, int start = 0) { std::size_t max_size, bytes_available; switch (start) { case 1: max_size = this->check_for_completion(ec, total_transferred_); bytes_available = read_size_helper(streambuf_, max_size); for (;;) { device_.async_read_some_at(offset_ + total_transferred_, streambuf_.prepare(bytes_available), *this); return; default: total_transferred_ += bytes_transferred; streambuf_.commit(bytes_transferred); max_size = this->check_for_completion(ec, total_transferred_); bytes_available = read_size_helper(streambuf_, max_size); if ((!ec && bytes_transferred == 0) || bytes_available == 0) break; } handler_(ec, static_cast(total_transferred_)); } } //private: AsyncRandomAccessReadDevice& device_; boost::uint64_t offset_; asio::basic_streambuf& streambuf_; std::size_t total_transferred_; ReadHandler handler_; }; template inline void* asio_handler_allocate(std::size_t size, read_at_streambuf_op* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, read_at_streambuf_op* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, read_at_streambuf_op* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } } // namespace detail template inline void async_read_at(AsyncRandomAccessReadDevice& d, boost::uint64_t offset, asio::basic_streambuf& b, CompletionCondition completion_condition, ReadHandler handler) { detail::read_at_streambuf_op( d, offset, b, completion_condition, handler)( asio::error_code(), 0, 1); } template inline void async_read_at(AsyncRandomAccessReadDevice& d, boost::uint64_t offset, asio::basic_streambuf& b, ReadHandler handler) { async_read_at(d, offset, b, transfer_all(), handler); } #endif // !defined(BOOST_NO_IOSTREAM) } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IMPL_READ_AT_HPP percona-xtradb-cluster-galera/asio/asio/impl/read_at.ipp0000644000000000000000000003075712247075736023626 0ustar rootroot00000000000000// // read_at.ipp // ~~~~~~~~~~~ // // Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_READ_AT_IPP #define ASIO_READ_AT_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/push_options.hpp" #include "asio/detail/push_options.hpp" #include #include "asio/detail/pop_options.hpp" #include "asio/buffer.hpp" #include "asio/completion_condition.hpp" #include "asio/error.hpp" #include "asio/detail/bind_handler.hpp" #include "asio/detail/consuming_buffers.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/throw_error.hpp" namespace asio { template std::size_t read_at(SyncRandomAccessReadDevice& d, boost::uint64_t offset, const MutableBufferSequence& buffers, CompletionCondition completion_condition, asio::error_code& ec) { ec = asio::error_code(); asio::detail::consuming_buffers< mutable_buffer, MutableBufferSequence> tmp(buffers); std::size_t total_transferred = 0; tmp.prepare(detail::adapt_completion_condition_result( completion_condition(ec, total_transferred))); while (tmp.begin() != tmp.end()) { std::size_t bytes_transferred = d.read_some_at( offset + total_transferred, tmp, ec); tmp.consume(bytes_transferred); total_transferred += bytes_transferred; tmp.prepare(detail::adapt_completion_condition_result( completion_condition(ec, total_transferred))); } return total_transferred; } template inline std::size_t read_at(SyncRandomAccessReadDevice& d, boost::uint64_t offset, const MutableBufferSequence& buffers) { asio::error_code ec; std::size_t bytes_transferred = read_at( d, offset, buffers, transfer_all(), ec); asio::detail::throw_error(ec); return bytes_transferred; } template inline std::size_t read_at(SyncRandomAccessReadDevice& d, boost::uint64_t offset, const MutableBufferSequence& buffers, CompletionCondition completion_condition) { asio::error_code ec; std::size_t bytes_transferred = read_at( d, offset, buffers, completion_condition, ec); asio::detail::throw_error(ec); return bytes_transferred; } #if !defined(BOOST_NO_IOSTREAM) template std::size_t read_at(SyncRandomAccessReadDevice& d, boost::uint64_t offset, asio::basic_streambuf& b, CompletionCondition completion_condition, asio::error_code& ec) { std::size_t total_transferred = 0; for (;;) { std::size_t bytes_available = std::min(512, b.max_size() - b.size()); std::size_t bytes_transferred = d.read_some_at( offset + total_transferred, b.prepare(bytes_available), ec); b.commit(bytes_transferred); total_transferred += bytes_transferred; if (b.size() == b.max_size() || completion_condition(ec, total_transferred)) return total_transferred; } } template inline std::size_t read_at(SyncRandomAccessReadDevice& d, boost::uint64_t offset, asio::basic_streambuf& b) { asio::error_code ec; std::size_t bytes_transferred = read_at( d, offset, b, transfer_all(), ec); asio::detail::throw_error(ec); return bytes_transferred; } template inline std::size_t read_at(SyncRandomAccessReadDevice& d, boost::uint64_t offset, asio::basic_streambuf& b, CompletionCondition completion_condition) { asio::error_code ec; std::size_t bytes_transferred = read_at( d, offset, b, completion_condition, ec); asio::detail::throw_error(ec); return bytes_transferred; } #endif // !defined(BOOST_NO_IOSTREAM) namespace detail { template class read_at_handler { public: typedef asio::detail::consuming_buffers< mutable_buffer, MutableBufferSequence> buffers_type; read_at_handler(AsyncRandomAccessReadDevice& stream, boost::uint64_t offset, const buffers_type& buffers, CompletionCondition completion_condition, ReadHandler handler) : stream_(stream), offset_(offset), buffers_(buffers), total_transferred_(0), completion_condition_(completion_condition), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred) { total_transferred_ += bytes_transferred; buffers_.consume(bytes_transferred); buffers_.prepare(detail::adapt_completion_condition_result( completion_condition_(ec, total_transferred_))); if (buffers_.begin() == buffers_.end()) { handler_(ec, total_transferred_); } else { stream_.async_read_some_at( offset_ + total_transferred_, buffers_, *this); } } //private: AsyncRandomAccessReadDevice& stream_; boost::uint64_t offset_; buffers_type buffers_; std::size_t total_transferred_; CompletionCondition completion_condition_; ReadHandler handler_; }; template inline void* asio_handler_allocate(std::size_t size, read_at_handler* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, read_at_handler* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, read_at_handler* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } } // namespace detail template inline void async_read_at(AsyncRandomAccessReadDevice& d, boost::uint64_t offset, const MutableBufferSequence& buffers, CompletionCondition completion_condition, ReadHandler handler) { asio::detail::consuming_buffers< mutable_buffer, MutableBufferSequence> tmp(buffers); asio::error_code ec; std::size_t total_transferred = 0; tmp.prepare(detail::adapt_completion_condition_result( completion_condition(ec, total_transferred))); if (tmp.begin() == tmp.end()) { d.get_io_service().post(detail::bind_handler( handler, ec, total_transferred)); return; } d.async_read_some_at(offset, tmp, detail::read_at_handler( d, offset, tmp, completion_condition, handler)); } template inline void async_read_at(AsyncRandomAccessReadDevice& d, boost::uint64_t offset, const MutableBufferSequence& buffers, ReadHandler handler) { async_read_at(d, offset, buffers, transfer_all(), handler); } #if !defined(BOOST_NO_IOSTREAM) namespace detail { template class read_at_streambuf_handler { public: read_at_streambuf_handler(AsyncRandomAccessReadDevice& stream, boost::uint64_t offset, basic_streambuf& streambuf, CompletionCondition completion_condition, ReadHandler handler) : stream_(stream), offset_(offset), streambuf_(streambuf), total_transferred_(0), completion_condition_(completion_condition), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred) { total_transferred_ += bytes_transferred; streambuf_.commit(bytes_transferred); std::size_t max_size = detail::adapt_completion_condition_result( completion_condition_(ec, total_transferred_)); std::size_t bytes_available = std::min(512, std::min(max_size, streambuf_.max_size() - streambuf_.size())); if (bytes_available == 0) { handler_(ec, total_transferred_); } else { stream_.async_read_some_at(offset_ + total_transferred_, streambuf_.prepare(bytes_available), *this); } } //private: AsyncRandomAccessReadDevice& stream_; boost::uint64_t offset_; asio::basic_streambuf& streambuf_; std::size_t total_transferred_; CompletionCondition completion_condition_; ReadHandler handler_; }; template inline void* asio_handler_allocate(std::size_t size, read_at_streambuf_handler* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, read_at_streambuf_handler* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, read_at_streambuf_handler* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } } // namespace detail template inline void async_read_at(AsyncRandomAccessReadDevice& d, boost::uint64_t offset, asio::basic_streambuf& b, CompletionCondition completion_condition, ReadHandler handler) { asio::error_code ec; std::size_t total_transferred = 0; std::size_t max_size = detail::adapt_completion_condition_result( completion_condition(ec, total_transferred)); std::size_t bytes_available = std::min(512, std::min(max_size, b.max_size() - b.size())); if (bytes_available == 0) { d.get_io_service().post(detail::bind_handler( handler, ec, total_transferred)); return; } d.async_read_some_at(offset, b.prepare(bytes_available), detail::read_at_streambuf_handler( d, offset, b, completion_condition, handler)); } template inline void async_read_at(AsyncRandomAccessReadDevice& d, boost::uint64_t offset, asio::basic_streambuf& b, ReadHandler handler) { async_read_at(d, offset, b, transfer_all(), handler); } #endif // !defined(BOOST_NO_IOSTREAM) } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_READ_AT_IPP percona-xtradb-cluster-galera/asio/asio/impl/read_until.hpp0000644000000000000000000006733612247075736024357 0ustar rootroot00000000000000// // impl/read_until.hpp // ~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IMPL_READ_UNTIL_HPP #define ASIO_IMPL_READ_UNTIL_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include #include #include #include #include #include "asio/buffer.hpp" #include "asio/buffers_iterator.hpp" #include "asio/detail/bind_handler.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/throw_error.hpp" #include "asio/detail/push_options.hpp" namespace asio { template inline std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, char delim) { asio::error_code ec; std::size_t bytes_transferred = read_until(s, b, delim, ec); asio::detail::throw_error(ec); return bytes_transferred; } template std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, char delim, asio::error_code& ec) { std::size_t search_position = 0; for (;;) { // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; typedef asio::buffers_iterator iterator; const_buffers_type buffers = b.data(); iterator begin = iterator::begin(buffers); iterator start = begin + search_position; iterator end = iterator::end(buffers); // Look for a match. iterator iter = std::find(start, end, delim); if (iter != end) { // Found a match. We're done. ec = asio::error_code(); return iter - begin + 1; } else { // No match. Next search can start with the new data. search_position = end - begin; } // Check if buffer is full. if (b.size() == b.max_size()) { ec = error::not_found; return 0; } // Need more data. std::size_t bytes_to_read = read_size_helper(b, 65536); b.commit(s.read_some(b.prepare(bytes_to_read), ec)); if (ec) return 0; } } template inline std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, const std::string& delim) { asio::error_code ec; std::size_t bytes_transferred = read_until(s, b, delim, ec); asio::detail::throw_error(ec); return bytes_transferred; } namespace detail { // Algorithm that finds a subsequence of equal values in a sequence. Returns // (iterator,true) if a full match was found, in which case the iterator // points to the beginning of the match. Returns (iterator,false) if a // partial match was found at the end of the first sequence, in which case // the iterator points to the beginning of the partial match. Returns // (last1,false) if no full or partial match was found. template std::pair partial_search( Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2) { for (Iterator1 iter1 = first1; iter1 != last1; ++iter1) { Iterator1 test_iter1 = iter1; Iterator2 test_iter2 = first2; for (;; ++test_iter1, ++test_iter2) { if (test_iter2 == last2) return std::make_pair(iter1, true); if (test_iter1 == last1) { if (test_iter2 != first2) return std::make_pair(iter1, false); else break; } if (*test_iter1 != *test_iter2) break; } } return std::make_pair(last1, false); } } // namespace detail template std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, const std::string& delim, asio::error_code& ec) { std::size_t search_position = 0; for (;;) { // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; typedef asio::buffers_iterator iterator; const_buffers_type buffers = b.data(); iterator begin = iterator::begin(buffers); iterator start = begin + search_position; iterator end = iterator::end(buffers); // Look for a match. std::pair result = detail::partial_search( start, end, delim.begin(), delim.end()); if (result.first != end) { if (result.second) { // Full match. We're done. ec = asio::error_code(); return result.first - begin + delim.length(); } else { // Partial match. Next search needs to start from beginning of match. search_position = result.first - begin; } } else { // No match. Next search can start with the new data. search_position = end - begin; } // Check if buffer is full. if (b.size() == b.max_size()) { ec = error::not_found; return 0; } // Need more data. std::size_t bytes_to_read = read_size_helper(b, 65536); b.commit(s.read_some(b.prepare(bytes_to_read), ec)); if (ec) return 0; } } template inline std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, const boost::regex& expr) { asio::error_code ec; std::size_t bytes_transferred = read_until(s, b, expr, ec); asio::detail::throw_error(ec); return bytes_transferred; } template std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, const boost::regex& expr, asio::error_code& ec) { std::size_t search_position = 0; for (;;) { // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; typedef asio::buffers_iterator iterator; const_buffers_type buffers = b.data(); iterator begin = iterator::begin(buffers); iterator start = begin + search_position; iterator end = iterator::end(buffers); // Look for a match. boost::match_results >::allocator_type> match_results; if (regex_search(start, end, match_results, expr, boost::match_default | boost::match_partial)) { if (match_results[0].matched) { // Full match. We're done. ec = asio::error_code(); return match_results[0].second - begin; } else { // Partial match. Next search needs to start from beginning of match. search_position = match_results[0].first - begin; } } else { // No match. Next search can start with the new data. search_position = end - begin; } // Check if buffer is full. if (b.size() == b.max_size()) { ec = error::not_found; return 0; } // Need more data. std::size_t bytes_to_read = read_size_helper(b, 65536); b.commit(s.read_some(b.prepare(bytes_to_read), ec)); if (ec) return 0; } } template std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, MatchCondition match_condition, asio::error_code& ec, typename boost::enable_if >::type*) { std::size_t search_position = 0; for (;;) { // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; typedef asio::buffers_iterator iterator; const_buffers_type buffers = b.data(); iterator begin = iterator::begin(buffers); iterator start = begin + search_position; iterator end = iterator::end(buffers); // Look for a match. std::pair result = match_condition(start, end); if (result.second) { // Full match. We're done. ec = asio::error_code(); return result.first - begin; } else if (result.first != end) { // Partial match. Next search needs to start from beginning of match. search_position = result.first - begin; } else { // No match. Next search can start with the new data. search_position = end - begin; } // Check if buffer is full. if (b.size() == b.max_size()) { ec = error::not_found; return 0; } // Need more data. std::size_t bytes_to_read = read_size_helper(b, 65536); b.commit(s.read_some(b.prepare(bytes_to_read), ec)); if (ec) return 0; } } template inline std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, MatchCondition match_condition, typename boost::enable_if >::type*) { asio::error_code ec; std::size_t bytes_transferred = read_until(s, b, match_condition, ec); asio::detail::throw_error(ec); return bytes_transferred; } namespace detail { template class read_until_delim_op { public: read_until_delim_op(AsyncReadStream& stream, asio::basic_streambuf& streambuf, char delim, ReadHandler handler) : stream_(stream), streambuf_(streambuf), delim_(delim), search_position_(0), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred, int start = 0) { const std::size_t not_found = (std::numeric_limits::max)(); std::size_t bytes_to_read; switch (start) { case 1: for (;;) { { // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; typedef asio::buffers_iterator iterator; const_buffers_type buffers = streambuf_.data(); iterator begin = iterator::begin(buffers); iterator start = begin + search_position_; iterator end = iterator::end(buffers); // Look for a match. iterator iter = std::find(start, end, delim_); if (iter != end) { // Found a match. We're done. search_position_ = iter - begin + 1; bytes_to_read = 0; } // No match yet. Check if buffer is full. else if (streambuf_.size() == streambuf_.max_size()) { search_position_ = not_found; bytes_to_read = 0; } // Need to read some more data. else { // Next search can start with the new data. search_position_ = end - begin; bytes_to_read = read_size_helper(streambuf_, 65536); } } // Check if we're done. if (!start && bytes_to_read == 0) break; // Start a new asynchronous read operation to obtain more data. stream_.async_read_some(streambuf_.prepare(bytes_to_read), *this); return; default: streambuf_.commit(bytes_transferred); if (ec || bytes_transferred == 0) break; } const asio::error_code result_ec = (search_position_ == not_found) ? error::not_found : ec; const std::size_t result_n = (ec || search_position_ == not_found) ? 0 : search_position_; handler_(result_ec, result_n); } } //private: AsyncReadStream& stream_; asio::basic_streambuf& streambuf_; char delim_; std::size_t search_position_; ReadHandler handler_; }; template inline void* asio_handler_allocate(std::size_t size, read_until_delim_op* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, read_until_delim_op* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, read_until_delim_op* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } } // namespace detail template void async_read_until(AsyncReadStream& s, asio::basic_streambuf& b, char delim, ReadHandler handler) { detail::read_until_delim_op< AsyncReadStream, Allocator, ReadHandler>( s, b, delim, handler)( asio::error_code(), 0, 1); } namespace detail { template class read_until_delim_string_op { public: read_until_delim_string_op(AsyncReadStream& stream, asio::basic_streambuf& streambuf, const std::string& delim, ReadHandler handler) : stream_(stream), streambuf_(streambuf), delim_(delim), search_position_(0), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred, int start = 0) { const std::size_t not_found = (std::numeric_limits::max)(); std::size_t bytes_to_read; switch (start) { case 1: for (;;) { { // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; typedef asio::buffers_iterator iterator; const_buffers_type buffers = streambuf_.data(); iterator begin = iterator::begin(buffers); iterator start = begin + search_position_; iterator end = iterator::end(buffers); // Look for a match. std::pair result = detail::partial_search( start, end, delim_.begin(), delim_.end()); if (result.first != end && result.second) { // Full match. We're done. search_position_ = result.first - begin + delim_.length(); bytes_to_read = 0; } // No match yet. Check if buffer is full. else if (streambuf_.size() == streambuf_.max_size()) { search_position_ = not_found; bytes_to_read = 0; } // Need to read some more data. else { if (result.first != end) { // Partial match. Next search needs to start from beginning of // match. search_position_ = result.first - begin; } else { // Next search can start with the new data. search_position_ = end - begin; } bytes_to_read = read_size_helper(streambuf_, 65536); } } // Check if we're done. if (!start && bytes_to_read == 0) break; // Start a new asynchronous read operation to obtain more data. stream_.async_read_some(streambuf_.prepare(bytes_to_read), *this); return; default: streambuf_.commit(bytes_transferred); if (ec || bytes_transferred == 0) break; } const asio::error_code result_ec = (search_position_ == not_found) ? error::not_found : ec; const std::size_t result_n = (ec || search_position_ == not_found) ? 0 : search_position_; handler_(result_ec, result_n); } } //private: AsyncReadStream& stream_; asio::basic_streambuf& streambuf_; std::string delim_; std::size_t search_position_; ReadHandler handler_; }; template inline void* asio_handler_allocate(std::size_t size, read_until_delim_string_op* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, read_until_delim_string_op* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, read_until_delim_string_op* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } } // namespace detail template void async_read_until(AsyncReadStream& s, asio::basic_streambuf& b, const std::string& delim, ReadHandler handler) { detail::read_until_delim_string_op< AsyncReadStream, Allocator, ReadHandler>( s, b, delim, handler)( asio::error_code(), 0, 1); } namespace detail { template class read_until_expr_op { public: read_until_expr_op(AsyncReadStream& stream, asio::basic_streambuf& streambuf, const boost::regex& expr, ReadHandler handler) : stream_(stream), streambuf_(streambuf), expr_(expr), search_position_(0), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred, int start = 0) { const std::size_t not_found = (std::numeric_limits::max)(); std::size_t bytes_to_read; switch (start) { case 1: for (;;) { { // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; typedef asio::buffers_iterator iterator; const_buffers_type buffers = streambuf_.data(); iterator begin = iterator::begin(buffers); iterator start = begin + search_position_; iterator end = iterator::end(buffers); // Look for a match. boost::match_results >::allocator_type> match_results; bool match = regex_search(start, end, match_results, expr_, boost::match_default | boost::match_partial); if (match && match_results[0].matched) { // Full match. We're done. search_position_ = match_results[0].second - begin; bytes_to_read = 0; } // No match yet. Check if buffer is full. else if (streambuf_.size() == streambuf_.max_size()) { search_position_ = not_found; bytes_to_read = 0; } // Need to read some more data. else { if (match) { // Partial match. Next search needs to start from beginning of // match. search_position_ = match_results[0].first - begin; } else { // Next search can start with the new data. search_position_ = end - begin; } bytes_to_read = read_size_helper(streambuf_, 65536); } } // Check if we're done. if (!start && bytes_to_read == 0) break; // Start a new asynchronous read operation to obtain more data. stream_.async_read_some(streambuf_.prepare(bytes_to_read), *this); return; default: streambuf_.commit(bytes_transferred); if (ec || bytes_transferred == 0) break; } const asio::error_code result_ec = (search_position_ == not_found) ? error::not_found : ec; const std::size_t result_n = (ec || search_position_ == not_found) ? 0 : search_position_; handler_(result_ec, result_n); } } //private: AsyncReadStream& stream_; asio::basic_streambuf& streambuf_; RegEx expr_; std::size_t search_position_; ReadHandler handler_; }; template inline void* asio_handler_allocate(std::size_t size, read_until_expr_op* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, read_until_expr_op* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, read_until_expr_op* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } } // namespace detail template void async_read_until(AsyncReadStream& s, asio::basic_streambuf& b, const boost::regex& expr, ReadHandler handler) { detail::read_until_expr_op( s, b, expr, handler)( asio::error_code(), 0, 1); } namespace detail { template class read_until_match_op { public: read_until_match_op(AsyncReadStream& stream, asio::basic_streambuf& streambuf, MatchCondition match_condition, ReadHandler handler) : stream_(stream), streambuf_(streambuf), match_condition_(match_condition), search_position_(0), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred, int start = 0) { const std::size_t not_found = (std::numeric_limits::max)(); std::size_t bytes_to_read; switch (start) { case 1: for (;;) { { // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; typedef asio::buffers_iterator iterator; const_buffers_type buffers = streambuf_.data(); iterator begin = iterator::begin(buffers); iterator start = begin + search_position_; iterator end = iterator::end(buffers); // Look for a match. std::pair result = match_condition_(start, end); if (result.second) { // Full match. We're done. search_position_ = result.first - begin; bytes_to_read = 0; } // No match yet. Check if buffer is full. else if (streambuf_.size() == streambuf_.max_size()) { search_position_ = not_found; bytes_to_read = 0; } // Need to read some more data. else { if (result.first != end) { // Partial match. Next search needs to start from beginning of // match. search_position_ = result.first - begin; } else { // Next search can start with the new data. search_position_ = end - begin; } bytes_to_read = read_size_helper(streambuf_, 65536); } } // Check if we're done. if (!start && bytes_to_read == 0) break; // Start a new asynchronous read operation to obtain more data. stream_.async_read_some(streambuf_.prepare(bytes_to_read), *this); return; default: streambuf_.commit(bytes_transferred); if (ec || bytes_transferred == 0) break; } const asio::error_code result_ec = (search_position_ == not_found) ? error::not_found : ec; const std::size_t result_n = (ec || search_position_ == not_found) ? 0 : search_position_; handler_(result_ec, result_n); } } //private: AsyncReadStream& stream_; asio::basic_streambuf& streambuf_; MatchCondition match_condition_; std::size_t search_position_; ReadHandler handler_; }; template inline void* asio_handler_allocate(std::size_t size, read_until_match_op* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, read_until_match_op* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, read_until_match_op* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } } // namespace detail template void async_read_until(AsyncReadStream& s, asio::basic_streambuf& b, MatchCondition match_condition, ReadHandler handler, typename boost::enable_if >::type*) { detail::read_until_match_op< AsyncReadStream, Allocator, MatchCondition, ReadHandler>( s, b, match_condition, handler)( asio::error_code(), 0, 1); } } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IMPL_READ_UNTIL_HPP percona-xtradb-cluster-galera/asio/asio/impl/read_until.ipp0000644000000000000000000007464512247075736024361 0ustar rootroot00000000000000// // read_until.ipp // ~~~~~~~~~~~~~~ // // Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_READ_UNTIL_IPP #define ASIO_READ_UNTIL_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/push_options.hpp" #include "asio/detail/push_options.hpp" #include #include #include #include #include "asio/detail/pop_options.hpp" #include "asio/buffer.hpp" #include "asio/buffers_iterator.hpp" #include "asio/detail/bind_handler.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/throw_error.hpp" namespace asio { template inline std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, char delim) { asio::error_code ec; std::size_t bytes_transferred = read_until(s, b, delim, ec); asio::detail::throw_error(ec); return bytes_transferred; } template std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, char delim, asio::error_code& ec) { std::size_t next_search_start = 0; for (;;) { // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; typedef asio::buffers_iterator iterator; const_buffers_type buffers = b.data(); iterator begin = iterator::begin(buffers); iterator start = begin + next_search_start; iterator end = iterator::end(buffers); // Look for a match. iterator iter = std::find(start, end, delim); if (iter != end) { // Found a match. We're done. ec = asio::error_code(); return iter - begin + 1; } else { // No match. Next search can start with the new data. next_search_start = end - begin; } // Check if buffer is full. if (b.size() == b.max_size()) { ec = error::not_found; return 0; } // Need more data. std::size_t bytes_available = std::min(512, b.max_size() - b.size()); b.commit(s.read_some(b.prepare(bytes_available), ec)); if (ec) return 0; } } template inline std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, const std::string& delim) { asio::error_code ec; std::size_t bytes_transferred = read_until(s, b, delim, ec); asio::detail::throw_error(ec); return bytes_transferred; } namespace detail { // Algorithm that finds a subsequence of equal values in a sequence. Returns // (iterator,true) if a full match was found, in which case the iterator // points to the beginning of the match. Returns (iterator,false) if a // partial match was found at the end of the first sequence, in which case // the iterator points to the beginning of the partial match. Returns // (last1,false) if no full or partial match was found. template std::pair partial_search( Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2) { for (Iterator1 iter1 = first1; iter1 != last1; ++iter1) { Iterator1 test_iter1 = iter1; Iterator2 test_iter2 = first2; for (;; ++test_iter1, ++test_iter2) { if (test_iter2 == last2) return std::make_pair(iter1, true); if (test_iter1 == last1) { if (test_iter2 != first2) return std::make_pair(iter1, false); else break; } if (*test_iter1 != *test_iter2) break; } } return std::make_pair(last1, false); } } // namespace detail template std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, const std::string& delim, asio::error_code& ec) { std::size_t next_search_start = 0; for (;;) { // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; typedef asio::buffers_iterator iterator; const_buffers_type buffers = b.data(); iterator begin = iterator::begin(buffers); iterator start = begin + next_search_start; iterator end = iterator::end(buffers); // Look for a match. std::pair result = asio::detail::partial_search( start, end, delim.begin(), delim.end()); if (result.first != end) { if (result.second) { // Full match. We're done. ec = asio::error_code(); return result.first - begin + delim.length(); } else { // Partial match. Next search needs to start from beginning of match. next_search_start = result.first - begin; } } else { // No match. Next search can start with the new data. next_search_start = end - begin; } // Check if buffer is full. if (b.size() == b.max_size()) { ec = error::not_found; return 0; } // Need more data. std::size_t bytes_available = std::min(512, b.max_size() - b.size()); b.commit(s.read_some(b.prepare(bytes_available), ec)); if (ec) return 0; } } template inline std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, const boost::regex& expr) { asio::error_code ec; std::size_t bytes_transferred = read_until(s, b, expr, ec); asio::detail::throw_error(ec); return bytes_transferred; } template std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, const boost::regex& expr, asio::error_code& ec) { std::size_t next_search_start = 0; for (;;) { // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; typedef asio::buffers_iterator iterator; const_buffers_type buffers = b.data(); iterator begin = iterator::begin(buffers); iterator start = begin + next_search_start; iterator end = iterator::end(buffers); // Look for a match. boost::match_results match_results; if (boost::regex_search(start, end, match_results, expr, boost::match_default | boost::match_partial)) { if (match_results[0].matched) { // Full match. We're done. ec = asio::error_code(); return match_results[0].second - begin; } else { // Partial match. Next search needs to start from beginning of match. next_search_start = match_results[0].first - begin; } } else { // No match. Next search can start with the new data. next_search_start = end - begin; } // Check if buffer is full. if (b.size() == b.max_size()) { ec = error::not_found; return 0; } // Need more data. std::size_t bytes_available = std::min(512, b.max_size() - b.size()); b.commit(s.read_some(b.prepare(bytes_available), ec)); if (ec) return 0; } } template std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, MatchCondition match_condition, asio::error_code& ec, typename boost::enable_if >::type*) { std::size_t next_search_start = 0; for (;;) { // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; typedef asio::buffers_iterator iterator; const_buffers_type buffers = b.data(); iterator begin = iterator::begin(buffers); iterator start = begin + next_search_start; iterator end = iterator::end(buffers); // Look for a match. std::pair result = match_condition(start, end); if (result.second) { // Full match. We're done. ec = asio::error_code(); return result.first - begin; } else if (result.first != end) { // Partial match. Next search needs to start from beginning of match. next_search_start = result.first - begin; } else { // No match. Next search can start with the new data. next_search_start = end - begin; } // Check if buffer is full. if (b.size() == b.max_size()) { ec = error::not_found; return 0; } // Need more data. std::size_t bytes_available = std::min(512, b.max_size() - b.size()); b.commit(s.read_some(b.prepare(bytes_available), ec)); if (ec) return 0; } } template inline std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, MatchCondition match_condition, typename boost::enable_if >::type*) { asio::error_code ec; std::size_t bytes_transferred = read_until(s, b, match_condition, ec); asio::detail::throw_error(ec); return bytes_transferred; } namespace detail { template class read_until_delim_handler { public: read_until_delim_handler(AsyncReadStream& stream, asio::basic_streambuf& streambuf, char delim, std::size_t next_search_start, ReadHandler handler) : stream_(stream), streambuf_(streambuf), delim_(delim), next_search_start_(next_search_start), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred) { // Check for errors. if (ec) { std::size_t bytes = 0; handler_(ec, bytes); return; } // Commit received data to streambuf's get area. streambuf_.commit(bytes_transferred); // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; typedef asio::buffers_iterator iterator; const_buffers_type buffers = streambuf_.data(); iterator begin = iterator::begin(buffers); iterator start = begin + next_search_start_; iterator end = iterator::end(buffers); // Look for a match. iterator iter = std::find(start, end, delim_); if (iter != end) { // Found a match. We're done. std::size_t bytes = iter - begin + 1; handler_(ec, bytes); return; } // No match. Check if buffer is full. if (streambuf_.size() == streambuf_.max_size()) { std::size_t bytes = 0; asio::error_code ec(error::not_found); handler_(ec, bytes); return; } // Next search can start with the new data. next_search_start_ = end - begin; // Start a new asynchronous read operation to obtain more data. std::size_t bytes_available = std::min(512, streambuf_.max_size() - streambuf_.size()); stream_.async_read_some(streambuf_.prepare(bytes_available), *this); } //private: AsyncReadStream& stream_; asio::basic_streambuf& streambuf_; char delim_; std::size_t next_search_start_; ReadHandler handler_; }; template inline void* asio_handler_allocate(std::size_t size, read_until_delim_handler* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, read_until_delim_handler* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, read_until_delim_handler* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } } // namespace detail template void async_read_until(AsyncReadStream& s, asio::basic_streambuf& b, char delim, ReadHandler handler) { // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; typedef asio::buffers_iterator iterator; const_buffers_type buffers = b.data(); iterator begin = iterator::begin(buffers); iterator end = iterator::end(buffers); // Look for a match. iterator iter = std::find(begin, end, delim); if (iter != end) { // Found a match. We're done. asio::error_code ec; std::size_t bytes = iter - begin + 1; s.get_io_service().post(detail::bind_handler(handler, ec, bytes)); return; } // No match. Check if buffer is full. if (b.size() == b.max_size()) { asio::error_code ec(error::not_found); s.get_io_service().post(detail::bind_handler(handler, ec, 0)); return; } // Start a new asynchronous read operation to obtain more data. std::size_t bytes_available = std::min(512, b.max_size() - b.size()); s.async_read_some(b.prepare(bytes_available), detail::read_until_delim_handler( s, b, delim, end - begin, handler)); } namespace detail { template class read_until_delim_string_handler { public: read_until_delim_string_handler(AsyncReadStream& stream, asio::basic_streambuf& streambuf, const std::string& delim, std::size_t next_search_start, ReadHandler handler) : stream_(stream), streambuf_(streambuf), delim_(delim), next_search_start_(next_search_start), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred) { // Check for errors. if (ec) { std::size_t bytes = 0; handler_(ec, bytes); return; } // Commit received data to streambuf's get area. streambuf_.commit(bytes_transferred); // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; typedef asio::buffers_iterator iterator; const_buffers_type buffers = streambuf_.data(); iterator begin = iterator::begin(buffers); iterator start = begin + next_search_start_; iterator end = iterator::end(buffers); // Look for a match. std::pair result = asio::detail::partial_search( start, end, delim_.begin(), delim_.end()); if (result.first != end) { if (result.second) { // Full match. We're done. std::size_t bytes = result.first - begin + delim_.length(); handler_(ec, bytes); return; } else { // Partial match. Next search needs to start from beginning of match. next_search_start_ = result.first - begin; } } else { // No match. Next search can start with the new data. next_search_start_ = end - begin; } // Check if buffer is full. if (streambuf_.size() == streambuf_.max_size()) { std::size_t bytes = 0; asio::error_code ec2(error::not_found); handler_(ec2, bytes); return; } // Start a new asynchronous read operation to obtain more data. std::size_t bytes_available = std::min(512, streambuf_.max_size() - streambuf_.size()); stream_.async_read_some(streambuf_.prepare(bytes_available), *this); } //private: AsyncReadStream& stream_; asio::basic_streambuf& streambuf_; std::string delim_; std::size_t next_search_start_; ReadHandler handler_; }; template inline void* asio_handler_allocate(std::size_t size, read_until_delim_string_handler* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, read_until_delim_string_handler* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, read_until_delim_string_handler* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } } // namespace detail template void async_read_until(AsyncReadStream& s, asio::basic_streambuf& b, const std::string& delim, ReadHandler handler) { // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; typedef asio::buffers_iterator iterator; const_buffers_type buffers = b.data(); iterator begin = iterator::begin(buffers); iterator end = iterator::end(buffers); // Look for a match. std::size_t next_search_start; std::pair result = asio::detail::partial_search( begin, end, delim.begin(), delim.end()); if (result.first != end) { if (result.second) { // Full match. We're done. asio::error_code ec; std::size_t bytes = result.first - begin + delim.length(); s.get_io_service().post(detail::bind_handler(handler, ec, bytes)); return; } else { // Partial match. Next search needs to start from beginning of match. next_search_start = result.first - begin; } } else { // No match. Next search can start with the new data. next_search_start = end - begin; } // Check if buffer is full. if (b.size() == b.max_size()) { asio::error_code ec(error::not_found); s.get_io_service().post(detail::bind_handler(handler, ec, 0)); return; } // Start a new asynchronous read operation to obtain more data. std::size_t bytes_available = std::min(512, b.max_size() - b.size()); s.async_read_some(b.prepare(bytes_available), detail::read_until_delim_string_handler< AsyncReadStream, Allocator, ReadHandler>( s, b, delim, next_search_start, handler)); } namespace detail { template class read_until_expr_handler { public: read_until_expr_handler(AsyncReadStream& stream, asio::basic_streambuf& streambuf, const boost::regex& expr, std::size_t next_search_start, ReadHandler handler) : stream_(stream), streambuf_(streambuf), expr_(expr), next_search_start_(next_search_start), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred) { // Check for errors. if (ec) { std::size_t bytes = 0; handler_(ec, bytes); return; } // Commit received data to streambuf's get area. streambuf_.commit(bytes_transferred); // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; typedef asio::buffers_iterator iterator; const_buffers_type buffers = streambuf_.data(); iterator begin = iterator::begin(buffers); iterator start = begin + next_search_start_; iterator end = iterator::end(buffers); // Look for a match. boost::match_results match_results; if (boost::regex_search(start, end, match_results, expr_, boost::match_default | boost::match_partial)) { if (match_results[0].matched) { // Full match. We're done. std::size_t bytes = match_results[0].second - begin; handler_(ec, bytes); return; } else { // Partial match. Next search needs to start from beginning of match. next_search_start_ = match_results[0].first - begin; } } else { // No match. Next search can start with the new data. next_search_start_ = end - begin; } // Check if buffer is full. if (streambuf_.size() == streambuf_.max_size()) { std::size_t bytes = 0; asio::error_code ec(error::not_found); handler_(ec, bytes); return; } // Start a new asynchronous read operation to obtain more data. std::size_t bytes_available = std::min(512, streambuf_.max_size() - streambuf_.size()); stream_.async_read_some(streambuf_.prepare(bytes_available), *this); } //private: AsyncReadStream& stream_; asio::basic_streambuf& streambuf_; boost::regex expr_; std::size_t next_search_start_; ReadHandler handler_; }; template inline void* asio_handler_allocate(std::size_t size, read_until_expr_handler* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, read_until_expr_handler* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, read_until_expr_handler* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } } // namespace detail template void async_read_until(AsyncReadStream& s, asio::basic_streambuf& b, const boost::regex& expr, ReadHandler handler) { // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; typedef asio::buffers_iterator iterator; const_buffers_type buffers = b.data(); iterator begin = iterator::begin(buffers); iterator end = iterator::end(buffers); // Look for a match. std::size_t next_search_start; boost::match_results match_results; if (boost::regex_search(begin, end, match_results, expr, boost::match_default | boost::match_partial)) { if (match_results[0].matched) { // Full match. We're done. asio::error_code ec; std::size_t bytes = match_results[0].second - begin; s.get_io_service().post(detail::bind_handler(handler, ec, bytes)); return; } else { // Partial match. Next search needs to start from beginning of match. next_search_start = match_results[0].first - begin; } } else { // No match. Next search can start with the new data. next_search_start = end - begin; } // Check if buffer is full. if (b.size() == b.max_size()) { asio::error_code ec(error::not_found); s.get_io_service().post(detail::bind_handler(handler, ec, 0)); return; } // Start a new asynchronous read operation to obtain more data. std::size_t bytes_available = std::min(512, b.max_size() - b.size()); s.async_read_some(b.prepare(bytes_available), detail::read_until_expr_handler( s, b, expr, next_search_start, handler)); } namespace detail { template class read_until_match_handler { public: read_until_match_handler(AsyncReadStream& stream, asio::basic_streambuf& streambuf, MatchCondition match_condition, std::size_t next_search_start, ReadHandler handler) : stream_(stream), streambuf_(streambuf), match_condition_(match_condition), next_search_start_(next_search_start), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred) { // Check for errors. if (ec) { std::size_t bytes = 0; handler_(ec, bytes); return; } // Commit received data to streambuf's get area. streambuf_.commit(bytes_transferred); // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; typedef asio::buffers_iterator iterator; const_buffers_type buffers = streambuf_.data(); iterator begin = iterator::begin(buffers); iterator start = begin + next_search_start_; iterator end = iterator::end(buffers); // Look for a match. std::pair result = match_condition_(start, end); if (result.second) { // Full match. We're done. std::size_t bytes = result.first - begin; handler_(ec, bytes); return; } else if (result.first != end) { // Partial match. Next search needs to start from beginning of match. next_search_start_ = result.first - begin; } else { // No match. Next search can start with the new data. next_search_start_ = end - begin; } // Check if buffer is full. if (streambuf_.size() == streambuf_.max_size()) { std::size_t bytes = 0; asio::error_code ec(error::not_found); handler_(ec, bytes); return; } // Start a new asynchronous read operation to obtain more data. std::size_t bytes_available = std::min(512, streambuf_.max_size() - streambuf_.size()); stream_.async_read_some(streambuf_.prepare(bytes_available), *this); } //private: AsyncReadStream& stream_; asio::basic_streambuf& streambuf_; MatchCondition match_condition_; std::size_t next_search_start_; ReadHandler handler_; }; template inline void* asio_handler_allocate(std::size_t size, read_until_match_handler* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, read_until_match_handler* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, read_until_match_handler* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } } // namespace detail template void async_read_until(AsyncReadStream& s, asio::basic_streambuf& b, MatchCondition match_condition, ReadHandler handler, typename boost::enable_if >::type*) { // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; typedef asio::buffers_iterator iterator; const_buffers_type buffers = b.data(); iterator begin = iterator::begin(buffers); iterator end = iterator::end(buffers); // Look for a match. std::size_t next_search_start; std::pair result = match_condition(begin, end); if (result.second) { // Full match. We're done. asio::error_code ec; std::size_t bytes = result.first - begin; s.get_io_service().post(detail::bind_handler(handler, ec, bytes)); return; } else if (result.first != end) { // Partial match. Next search needs to start from beginning of match. next_search_start = result.first - begin; } else { // No match. Next search can start with the new data. next_search_start = end - begin; } // Check if buffer is full. if (b.size() == b.max_size()) { asio::error_code ec(error::not_found); s.get_io_service().post(detail::bind_handler(handler, ec, 0)); return; } // Start a new asynchronous read operation to obtain more data. std::size_t bytes_available = std::min(512, b.max_size() - b.size()); s.async_read_some(b.prepare(bytes_available), detail::read_until_match_handler< AsyncReadStream, Allocator, MatchCondition, ReadHandler>( s, b, match_condition, next_search_start, handler)); } } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_READ_UNTIL_IPP percona-xtradb-cluster-galera/asio/asio/impl/serial_port_base.hpp0000644000000000000000000000243212247075736025530 0ustar rootroot00000000000000// // impl/serial_port_base.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IMPL_SERIAL_PORT_BASE_HPP #define ASIO_IMPL_SERIAL_PORT_BASE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/push_options.hpp" namespace asio { inline serial_port_base::baud_rate::baud_rate(unsigned int rate) : value_(rate) { } inline unsigned int serial_port_base::baud_rate::value() const { return value_; } inline serial_port_base::flow_control::type serial_port_base::flow_control::value() const { return value_; } inline serial_port_base::parity::type serial_port_base::parity::value() const { return value_; } inline serial_port_base::stop_bits::type serial_port_base::stop_bits::value() const { return value_; } inline unsigned int serial_port_base::character_size::value() const { return value_; } } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IMPL_SERIAL_PORT_BASE_HPP percona-xtradb-cluster-galera/asio/asio/impl/serial_port_base.ipp0000644000000000000000000002775412247075736025547 0ustar rootroot00000000000000// // impl/serial_port_base.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IMPL_SERIAL_PORT_BASE_IPP #define ASIO_IMPL_SERIAL_PORT_BASE_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_SERIAL_PORT) #include #include #include "asio/error.hpp" #include "asio/serial_port_base.hpp" #if defined(GENERATING_DOCUMENTATION) # define ASIO_OPTION_STORAGE implementation_defined #elif defined(BOOST_WINDOWS) || defined(__CYGWIN__) # define ASIO_OPTION_STORAGE DCB #else # define ASIO_OPTION_STORAGE termios #endif #include "asio/detail/push_options.hpp" namespace asio { asio::error_code serial_port_base::baud_rate::store( ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) storage.BaudRate = value_; #else speed_t baud; switch (value_) { // Do POSIX-specified rates first. case 0: baud = B0; break; case 50: baud = B50; break; case 75: baud = B75; break; case 110: baud = B110; break; case 134: baud = B134; break; case 150: baud = B150; break; case 200: baud = B200; break; case 300: baud = B300; break; case 600: baud = B600; break; case 1200: baud = B1200; break; case 1800: baud = B1800; break; case 2400: baud = B2400; break; case 4800: baud = B4800; break; case 9600: baud = B9600; break; case 19200: baud = B19200; break; case 38400: baud = B38400; break; // And now the extended ones conditionally. # ifdef B7200 case 7200: baud = B7200; break; # endif # ifdef B14400 case 14400: baud = B14400; break; # endif # ifdef B57600 case 57600: baud = B57600; break; # endif # ifdef B115200 case 115200: baud = B115200; break; # endif # ifdef B230400 case 230400: baud = B230400; break; # endif # ifdef B460800 case 460800: baud = B460800; break; # endif # ifdef B500000 case 500000: baud = B500000; break; # endif # ifdef B576000 case 576000: baud = B576000; break; # endif # ifdef B921600 case 921600: baud = B921600; break; # endif # ifdef B1000000 case 1000000: baud = B1000000; break; # endif # ifdef B1152000 case 1152000: baud = B1152000; break; # endif # ifdef B2000000 case 2000000: baud = B2000000; break; # endif # ifdef B3000000 case 3000000: baud = B3000000; break; # endif # ifdef B3500000 case 3500000: baud = B3500000; break; # endif # ifdef B4000000 case 4000000: baud = B4000000; break; # endif default: baud = B0; ec = asio::error::invalid_argument; return ec; } # if defined(_BSD_SOURCE) ::cfsetspeed(&storage, baud); # else ::cfsetispeed(&storage, baud); ::cfsetospeed(&storage, baud); # endif #endif ec = asio::error_code(); return ec; } asio::error_code serial_port_base::baud_rate::load( const ASIO_OPTION_STORAGE& storage, asio::error_code& ec) { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) value_ = storage.BaudRate; #else speed_t baud = ::cfgetospeed(&storage); switch (baud) { // First do those specified by POSIX. case B0: value_ = 0; break; case B50: value_ = 50; break; case B75: value_ = 75; break; case B110: value_ = 110; break; case B134: value_ = 134; break; case B150: value_ = 150; break; case B200: value_ = 200; break; case B300: value_ = 300; break; case B600: value_ = 600; break; case B1200: value_ = 1200; break; case B1800: value_ = 1800; break; case B2400: value_ = 2400; break; case B4800: value_ = 4800; break; case B9600: value_ = 9600; break; case B19200: value_ = 19200; break; case B38400: value_ = 38400; break; // Now conditionally handle a bunch of extended rates. # ifdef B7200 case B7200: value_ = 7200; break; # endif # ifdef B14400 case B14400: value_ = 14400; break; # endif # ifdef B57600 case B57600: value_ = 57600; break; # endif # ifdef B115200 case B115200: value_ = 115200; break; # endif # ifdef B230400 case B230400: value_ = 230400; break; # endif # ifdef B460800 case B460800: value_ = 460800; break; # endif # ifdef B500000 case B500000: value_ = 500000; break; # endif # ifdef B576000 case B576000: value_ = 576000; break; # endif # ifdef B921600 case B921600: value_ = 921600; break; # endif # ifdef B1000000 case B1000000: value_ = 1000000; break; # endif # ifdef B1152000 case B1152000: value_ = 1152000; break; # endif # ifdef B2000000 case B2000000: value_ = 2000000; break; # endif # ifdef B3000000 case B3000000: value_ = 3000000; break; # endif # ifdef B3500000 case B3500000: value_ = 3500000; break; # endif # ifdef B4000000 case B4000000: value_ = 4000000; break; # endif default: value_ = 0; ec = asio::error::invalid_argument; return ec; } #endif ec = asio::error_code(); return ec; } serial_port_base::flow_control::flow_control( serial_port_base::flow_control::type t) : value_(t) { if (t != none && t != software && t != hardware) { std::out_of_range ex("invalid flow_control value"); boost::throw_exception(ex); } } asio::error_code serial_port_base::flow_control::store( ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) storage.fOutxCtsFlow = FALSE; storage.fOutxDsrFlow = FALSE; storage.fTXContinueOnXoff = TRUE; storage.fDtrControl = DTR_CONTROL_ENABLE; storage.fDsrSensitivity = FALSE; storage.fOutX = FALSE; storage.fInX = FALSE; storage.fRtsControl = RTS_CONTROL_ENABLE; switch (value_) { case none: break; case software: storage.fOutX = TRUE; storage.fInX = TRUE; break; case hardware: storage.fOutxCtsFlow = TRUE; storage.fRtsControl = RTS_CONTROL_HANDSHAKE; break; default: break; } #else switch (value_) { case none: storage.c_iflag &= ~(IXOFF | IXON); # if defined(_BSD_SOURCE) storage.c_cflag &= ~CRTSCTS; # elif defined(__QNXNTO__) storage.c_cflag &= ~(IHFLOW | OHFLOW); # endif break; case software: storage.c_iflag |= IXOFF | IXON; # if defined(_BSD_SOURCE) storage.c_cflag &= ~CRTSCTS; # elif defined(__QNXNTO__) storage.c_cflag &= ~(IHFLOW | OHFLOW); # endif break; case hardware: # if defined(_BSD_SOURCE) storage.c_iflag &= ~(IXOFF | IXON); storage.c_cflag |= CRTSCTS; break; # elif defined(__QNXNTO__) storage.c_iflag &= ~(IXOFF | IXON); storage.c_cflag |= (IHFLOW | OHFLOW); break; # else ec = asio::error::operation_not_supported; return ec; # endif default: break; } #endif ec = asio::error_code(); return ec; } asio::error_code serial_port_base::flow_control::load( const ASIO_OPTION_STORAGE& storage, asio::error_code& ec) { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) if (storage.fOutX && storage.fInX) { value_ = software; } else if (storage.fOutxCtsFlow && storage.fRtsControl == RTS_CONTROL_HANDSHAKE) { value_ = hardware; } else { value_ = none; } #else if (storage.c_iflag & (IXOFF | IXON)) { value_ = software; } # if defined(_BSD_SOURCE) else if (storage.c_cflag & CRTSCTS) { value_ = hardware; } # elif defined(__QNXNTO__) else if (storage.c_cflag & IHFLOW && storage.c_cflag & OHFLOW) { value_ = hardware; } # endif else { value_ = none; } #endif ec = asio::error_code(); return ec; } serial_port_base::parity::parity(serial_port_base::parity::type t) : value_(t) { if (t != none && t != odd && t != even) { std::out_of_range ex("invalid parity value"); boost::throw_exception(ex); } } asio::error_code serial_port_base::parity::store( ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) switch (value_) { case none: storage.fParity = FALSE; storage.Parity = NOPARITY; break; case odd: storage.fParity = TRUE; storage.Parity = ODDPARITY; break; case even: storage.fParity = TRUE; storage.Parity = EVENPARITY; break; default: break; } #else switch (value_) { case none: storage.c_iflag |= IGNPAR; storage.c_cflag &= ~(PARENB | PARODD); break; case even: storage.c_iflag &= ~(IGNPAR | PARMRK); storage.c_iflag |= INPCK; storage.c_cflag |= PARENB; storage.c_cflag &= ~PARODD; break; case odd: storage.c_iflag &= ~(IGNPAR | PARMRK); storage.c_iflag |= INPCK; storage.c_cflag |= (PARENB | PARODD); break; default: break; } #endif ec = asio::error_code(); return ec; } asio::error_code serial_port_base::parity::load( const ASIO_OPTION_STORAGE& storage, asio::error_code& ec) { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) if (storage.Parity == EVENPARITY) { value_ = even; } else if (storage.Parity == ODDPARITY) { value_ = odd; } else { value_ = none; } #else if (storage.c_cflag & PARENB) { if (storage.c_cflag & PARODD) { value_ = odd; } else { value_ = even; } } else { value_ = none; } #endif ec = asio::error_code(); return ec; } serial_port_base::stop_bits::stop_bits( serial_port_base::stop_bits::type t) : value_(t) { if (t != one && t != onepointfive && t != two) { std::out_of_range ex("invalid stop_bits value"); boost::throw_exception(ex); } } asio::error_code serial_port_base::stop_bits::store( ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) switch (value_) { case one: storage.StopBits = ONESTOPBIT; break; case onepointfive: storage.StopBits = ONE5STOPBITS; break; case two: storage.StopBits = TWOSTOPBITS; break; default: break; } #else switch (value_) { case one: storage.c_cflag &= ~CSTOPB; break; case two: storage.c_cflag |= CSTOPB; break; default: ec = asio::error::operation_not_supported; return ec; } #endif ec = asio::error_code(); return ec; } asio::error_code serial_port_base::stop_bits::load( const ASIO_OPTION_STORAGE& storage, asio::error_code& ec) { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) if (storage.StopBits == ONESTOPBIT) { value_ = one; } else if (storage.StopBits == ONE5STOPBITS) { value_ = onepointfive; } else if (storage.StopBits == TWOSTOPBITS) { value_ = two; } else { value_ = one; } #else value_ = (storage.c_cflag & CSTOPB) ? two : one; #endif ec = asio::error_code(); return ec; } serial_port_base::character_size::character_size(unsigned int t) : value_(t) { if (t < 5 || t > 8) { std::out_of_range ex("invalid character_size value"); boost::throw_exception(ex); } } asio::error_code serial_port_base::character_size::store( ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) storage.ByteSize = value_; #else storage.c_cflag &= ~CSIZE; switch (value_) { case 5: storage.c_cflag |= CS5; break; case 6: storage.c_cflag |= CS6; break; case 7: storage.c_cflag |= CS7; break; case 8: storage.c_cflag |= CS8; break; default: break; } #endif ec = asio::error_code(); return ec; } asio::error_code serial_port_base::character_size::load( const ASIO_OPTION_STORAGE& storage, asio::error_code& ec) { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) value_ = storage.ByteSize; #else if ((storage.c_cflag & CSIZE) == CS5) { value_ = 5; } else if ((storage.c_cflag & CSIZE) == CS6) { value_ = 6; } else if ((storage.c_cflag & CSIZE) == CS7) { value_ = 7; } else if ((storage.c_cflag & CSIZE) == CS8) { value_ = 8; } else { // Hmmm, use 8 for now. value_ = 8; } #endif ec = asio::error_code(); return ec; } } // namespace asio #include "asio/detail/pop_options.hpp" #undef ASIO_OPTION_STORAGE #endif // defined(ASIO_HAS_SERIAL_PORT) #endif // ASIO_IMPL_SERIAL_PORT_BASE_IPP percona-xtradb-cluster-galera/asio/asio/impl/src.cpp0000644000000000000000000000126612247075736023001 0ustar rootroot00000000000000// // impl/src.cpp // ~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #if defined(_MSC_VER) \ || defined(__BORLANDC__) \ || defined(__DMC__) # pragma message ( \ "This file is deprecated. " \ "Please #include instead.") #elif defined(__GNUC__) \ || defined(__HP_aCC) \ || defined(__SUNPRO_CC) \ || defined(__IBMCPP__) # warning "This file is deprecated." # warning "Please #include instead." #endif #include "asio/impl/src.hpp" percona-xtradb-cluster-galera/asio/asio/impl/src.hpp0000644000000000000000000000471312247075736023006 0ustar rootroot00000000000000// // impl/src.hpp // ~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IMPL_SRC_HPP #define ASIO_IMPL_SRC_HPP #define ASIO_SOURCE #include "asio/detail/config.hpp" #if defined(ASIO_HEADER_ONLY) # error Do not compile Asio library source with ASIO_HEADER_ONLY defined #endif #include "asio/impl/error.ipp" #include "asio/impl/error_code.ipp" #include "asio/impl/io_service.ipp" #include "asio/impl/serial_port_base.ipp" #include "asio/detail/impl/descriptor_ops.ipp" #include "asio/detail/impl/dev_poll_reactor.ipp" #include "asio/detail/impl/epoll_reactor.ipp" #include "asio/detail/impl/eventfd_select_interrupter.ipp" #include "asio/detail/impl/kqueue_reactor.ipp" #include "asio/detail/impl/pipe_select_interrupter.ipp" #include "asio/detail/impl/posix_event.ipp" #include "asio/detail/impl/posix_mutex.ipp" #include "asio/detail/impl/posix_thread.ipp" #include "asio/detail/impl/posix_tss_ptr.ipp" #include "asio/detail/impl/reactive_descriptor_service.ipp" #include "asio/detail/impl/reactive_serial_port_service.ipp" #include "asio/detail/impl/reactive_socket_service_base.ipp" #include "asio/detail/impl/resolver_service_base.ipp" #include "asio/detail/impl/select_reactor.ipp" #include "asio/detail/impl/service_registry.ipp" #include "asio/detail/impl/socket_ops.ipp" #include "asio/detail/impl/socket_select_interrupter.ipp" #include "asio/detail/impl/strand_service.ipp" #include "asio/detail/impl/task_io_service.ipp" #include "asio/detail/impl/throw_error.ipp" #include "asio/detail/impl/timer_queue.ipp" #include "asio/detail/impl/timer_queue_set.ipp" #include "asio/detail/impl/win_iocp_handle_service.ipp" #include "asio/detail/impl/win_iocp_io_service.ipp" #include "asio/detail/impl/win_iocp_serial_port_service.ipp" #include "asio/detail/impl/win_iocp_socket_service_base.ipp" #include "asio/detail/impl/win_event.ipp" #include "asio/detail/impl/win_mutex.ipp" #include "asio/detail/impl/win_thread.ipp" #include "asio/detail/impl/win_tss_ptr.ipp" #include "asio/detail/impl/winsock_init.ipp" #include "asio/ip/impl/address.ipp" #include "asio/ip/impl/address_v4.ipp" #include "asio/ip/impl/address_v6.ipp" #include "asio/ip/impl/host_name.ipp" #include "asio/ip/detail/impl/endpoint.ipp" #include "asio/local/detail/impl/endpoint.ipp" #endif // ASIO_IMPL_SRC_HPP percona-xtradb-cluster-galera/asio/asio/impl/write.hpp0000644000000000000000000003060612247075736023351 0ustar rootroot00000000000000// // impl/write.hpp // ~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IMPL_WRITE_HPP #define ASIO_IMPL_WRITE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/buffer.hpp" #include "asio/completion_condition.hpp" #include "asio/detail/base_from_completion_cond.hpp" #include "asio/detail/bind_handler.hpp" #include "asio/detail/consuming_buffers.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/throw_error.hpp" #include "asio/detail/push_options.hpp" namespace asio { template std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers, CompletionCondition completion_condition, asio::error_code& ec) { ec = asio::error_code(); asio::detail::consuming_buffers< const_buffer, ConstBufferSequence> tmp(buffers); std::size_t total_transferred = 0; tmp.prepare(detail::adapt_completion_condition_result( completion_condition(ec, total_transferred))); while (tmp.begin() != tmp.end()) { std::size_t bytes_transferred = s.write_some(tmp, ec); tmp.consume(bytes_transferred); total_transferred += bytes_transferred; tmp.prepare(detail::adapt_completion_condition_result( completion_condition(ec, total_transferred))); } return total_transferred; } template inline std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers) { asio::error_code ec; std::size_t bytes_transferred = write(s, buffers, transfer_all(), ec); asio::detail::throw_error(ec); return bytes_transferred; } template inline std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers, CompletionCondition completion_condition) { asio::error_code ec; std::size_t bytes_transferred = write(s, buffers, completion_condition, ec); asio::detail::throw_error(ec); return bytes_transferred; } #if !defined(BOOST_NO_IOSTREAM) template std::size_t write(SyncWriteStream& s, asio::basic_streambuf& b, CompletionCondition completion_condition, asio::error_code& ec) { std::size_t bytes_transferred = write(s, b.data(), completion_condition, ec); b.consume(bytes_transferred); return bytes_transferred; } template inline std::size_t write(SyncWriteStream& s, asio::basic_streambuf& b) { asio::error_code ec; std::size_t bytes_transferred = write(s, b, transfer_all(), ec); asio::detail::throw_error(ec); return bytes_transferred; } template inline std::size_t write(SyncWriteStream& s, asio::basic_streambuf& b, CompletionCondition completion_condition) { asio::error_code ec; std::size_t bytes_transferred = write(s, b, completion_condition, ec); asio::detail::throw_error(ec); return bytes_transferred; } #endif // !defined(BOOST_NO_IOSTREAM) namespace detail { template class write_op : detail::base_from_completion_cond { public: write_op(AsyncWriteStream& stream, const ConstBufferSequence& buffers, CompletionCondition completion_condition, WriteHandler handler) : detail::base_from_completion_cond< CompletionCondition>(completion_condition), stream_(stream), buffers_(buffers), total_transferred_(0), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred, int start = 0) { switch (start) { case 1: buffers_.prepare(this->check_for_completion(ec, total_transferred_)); for (;;) { stream_.async_write_some(buffers_, *this); return; default: total_transferred_ += bytes_transferred; buffers_.consume(bytes_transferred); buffers_.prepare(this->check_for_completion(ec, total_transferred_)); if ((!ec && bytes_transferred == 0) || buffers_.begin() == buffers_.end()) break; } handler_(ec, static_cast(total_transferred_)); } } //private: AsyncWriteStream& stream_; asio::detail::consuming_buffers< const_buffer, ConstBufferSequence> buffers_; std::size_t total_transferred_; WriteHandler handler_; }; template class write_op : detail::base_from_completion_cond { public: write_op(AsyncWriteStream& stream, const asio::mutable_buffers_1& buffers, CompletionCondition completion_condition, WriteHandler handler) : detail::base_from_completion_cond< CompletionCondition>(completion_condition), stream_(stream), buffer_(buffers), total_transferred_(0), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred, int start = 0) { std::size_t n = 0; switch (start) { case 1: n = this->check_for_completion(ec, total_transferred_); for (;;) { stream_.async_write_some(asio::buffer( buffer_ + total_transferred_, n), *this); return; default: total_transferred_ += bytes_transferred; if ((!ec && bytes_transferred == 0) || (n = this->check_for_completion(ec, total_transferred_)) == 0 || total_transferred_ == asio::buffer_size(buffer_)) break; } handler_(ec, static_cast(total_transferred_)); } } //private: AsyncWriteStream& stream_; asio::mutable_buffer buffer_; std::size_t total_transferred_; WriteHandler handler_; }; template class write_op : detail::base_from_completion_cond { public: write_op(AsyncWriteStream& stream, const asio::const_buffers_1& buffers, CompletionCondition completion_condition, WriteHandler handler) : detail::base_from_completion_cond< CompletionCondition>(completion_condition), stream_(stream), buffer_(buffers), total_transferred_(0), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred, int start = 0) { std::size_t n = 0; switch (start) { case 1: n = this->check_for_completion(ec, total_transferred_); for (;;) { stream_.async_write_some(asio::buffer( buffer_ + total_transferred_, n), *this); return; default: total_transferred_ += bytes_transferred; if ((!ec && bytes_transferred == 0) || (n = this->check_for_completion(ec, total_transferred_)) == 0 || total_transferred_ == asio::buffer_size(buffer_)) break; } handler_(ec, static_cast(total_transferred_)); } } //private: AsyncWriteStream& stream_; asio::const_buffer buffer_; std::size_t total_transferred_; WriteHandler handler_; }; template inline void* asio_handler_allocate(std::size_t size, write_op* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, write_op* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, write_op* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } } // namespace detail template inline void async_write(AsyncWriteStream& s, const ConstBufferSequence& buffers, CompletionCondition completion_condition, WriteHandler handler) { detail::write_op( s, buffers, completion_condition, handler)( asio::error_code(), 0, 1); } template inline void async_write(AsyncWriteStream& s, const ConstBufferSequence& buffers, WriteHandler handler) { async_write(s, buffers, transfer_all(), handler); } #if !defined(BOOST_NO_IOSTREAM) namespace detail { template class write_streambuf_handler { public: write_streambuf_handler(asio::basic_streambuf& streambuf, WriteHandler handler) : streambuf_(streambuf), handler_(handler) { } void operator()(const asio::error_code& ec, const std::size_t bytes_transferred) { streambuf_.consume(bytes_transferred); handler_(ec, bytes_transferred); } //private: asio::basic_streambuf& streambuf_; WriteHandler handler_; }; template inline void* asio_handler_allocate(std::size_t size, write_streambuf_handler* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, write_streambuf_handler* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, write_streambuf_handler* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } } // namespace detail template inline void async_write(AsyncWriteStream& s, asio::basic_streambuf& b, CompletionCondition completion_condition, WriteHandler handler) { async_write(s, b.data(), completion_condition, detail::write_streambuf_handler< AsyncWriteStream, Allocator, WriteHandler>(b, handler)); } template inline void async_write(AsyncWriteStream& s, asio::basic_streambuf& b, WriteHandler handler) { async_write(s, b, transfer_all(), handler); } #endif // !defined(BOOST_NO_IOSTREAM) } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IMPL_WRITE_HPP percona-xtradb-cluster-galera/asio/asio/impl/write.ipp0000644000000000000000000003045312247075736023352 0ustar rootroot00000000000000// // write.ipp // ~~~~~~~~~ // // Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_WRITE_IPP #define ASIO_WRITE_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/push_options.hpp" #include "asio/buffer.hpp" #include "asio/completion_condition.hpp" #include "asio/detail/base_from_completion_cond.hpp" #include "asio/detail/bind_handler.hpp" #include "asio/detail/consuming_buffers.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/throw_error.hpp" namespace asio { template std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers, CompletionCondition completion_condition, asio::error_code& ec) { ec = asio::error_code(); asio::detail::consuming_buffers< const_buffer, ConstBufferSequence> tmp(buffers); std::size_t total_transferred = 0; tmp.prepare(detail::adapt_completion_condition_result( completion_condition(ec, total_transferred))); while (tmp.begin() != tmp.end()) { std::size_t bytes_transferred = s.write_some(tmp, ec); tmp.consume(bytes_transferred); total_transferred += bytes_transferred; tmp.prepare(detail::adapt_completion_condition_result( completion_condition(ec, total_transferred))); } return total_transferred; } template inline std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers) { asio::error_code ec; std::size_t bytes_transferred = write(s, buffers, transfer_all(), ec); asio::detail::throw_error(ec); return bytes_transferred; } template inline std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers, CompletionCondition completion_condition) { asio::error_code ec; std::size_t bytes_transferred = write(s, buffers, completion_condition, ec); asio::detail::throw_error(ec); return bytes_transferred; } #if !defined(BOOST_NO_IOSTREAM) template std::size_t write(SyncWriteStream& s, asio::basic_streambuf& b, CompletionCondition completion_condition, asio::error_code& ec) { std::size_t bytes_transferred = write(s, b.data(), completion_condition, ec); b.consume(bytes_transferred); return bytes_transferred; } template inline std::size_t write(SyncWriteStream& s, asio::basic_streambuf& b) { asio::error_code ec; std::size_t bytes_transferred = write(s, b, transfer_all(), ec); asio::detail::throw_error(ec); return bytes_transferred; } template inline std::size_t write(SyncWriteStream& s, asio::basic_streambuf& b, CompletionCondition completion_condition) { asio::error_code ec; std::size_t bytes_transferred = write(s, b, completion_condition, ec); asio::detail::throw_error(ec); return bytes_transferred; } #endif // !defined(BOOST_NO_IOSTREAM) namespace detail { template class write_op : detail::base_from_completion_cond { public: write_op(AsyncWriteStream& stream, const ConstBufferSequence& buffers, CompletionCondition completion_condition, WriteHandler handler) : detail::base_from_completion_cond< CompletionCondition>(completion_condition), stream_(stream), buffers_(buffers), total_transferred_(0), handler_(handler), start_(true) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred) { switch (start_) { case true: start_ = false; buffers_.prepare(this->check(ec, total_transferred_)); for (;;) { stream_.async_write_some(buffers_, *this); return; default: total_transferred_ += bytes_transferred; buffers_.consume(bytes_transferred); buffers_.prepare(this->check(ec, total_transferred_)); if ((!ec && bytes_transferred == 0) || buffers_.begin() == buffers_.end()) break; } handler_(ec, total_transferred_); } } //private: AsyncWriteStream& stream_; asio::detail::consuming_buffers< const_buffer, ConstBufferSequence> buffers_; std::size_t total_transferred_; WriteHandler handler_; bool start_; }; template class write_op : detail::base_from_completion_cond { public: write_op(AsyncWriteStream& stream, const asio::mutable_buffers_1& buffers, CompletionCondition completion_condition, WriteHandler handler) : detail::base_from_completion_cond< CompletionCondition>(completion_condition), stream_(stream), buffer_(buffers), total_transferred_(0), handler_(handler), start_(true) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred) { std::size_t n = 0; switch (start_) { case true: start_ = false; n = this->check(ec, total_transferred_); for (;;) { stream_.async_write_some(asio::buffer( buffer_ + total_transferred_, n), *this); return; default: total_transferred_ += bytes_transferred; if ((!ec && bytes_transferred == 0) || (n = this->check(ec, total_transferred_)) == 0 || total_transferred_ == asio::buffer_size(buffer_)) break; } handler_(ec, total_transferred_); } } //private: AsyncWriteStream& stream_; asio::mutable_buffer buffer_; std::size_t total_transferred_; WriteHandler handler_; bool start_; }; template class write_op : detail::base_from_completion_cond { public: write_op(AsyncWriteStream& stream, const asio::const_buffers_1& buffers, CompletionCondition completion_condition, WriteHandler handler) : detail::base_from_completion_cond< CompletionCondition>(completion_condition), stream_(stream), buffer_(buffers), total_transferred_(0), handler_(handler), start_(true) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred) { std::size_t n = 0; switch (start_) { case true: start_ = false; n = this->check(ec, total_transferred_); for (;;) { stream_.async_write_some(asio::buffer( buffer_ + total_transferred_, n), *this); return; default: total_transferred_ += bytes_transferred; if ((!ec && bytes_transferred == 0) || (n = this->check(ec, total_transferred_)) == 0 || total_transferred_ == asio::buffer_size(buffer_)) break; } handler_(ec, total_transferred_); } } //private: AsyncWriteStream& stream_; asio::const_buffer buffer_; std::size_t total_transferred_; WriteHandler handler_; bool start_; }; template inline void* asio_handler_allocate(std::size_t size, write_op* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, write_op* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, write_op* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } } // namespace detail template inline void async_write(AsyncWriteStream& s, const ConstBufferSequence& buffers, CompletionCondition completion_condition, WriteHandler handler) { detail::write_op( s, buffers, completion_condition, handler)( asio::error_code(), 0); } template inline void async_write(AsyncWriteStream& s, const ConstBufferSequence& buffers, WriteHandler handler) { async_write(s, buffers, transfer_all(), handler); } #if !defined(BOOST_NO_IOSTREAM) namespace detail { template class write_streambuf_handler { public: write_streambuf_handler(asio::basic_streambuf& streambuf, WriteHandler handler) : streambuf_(streambuf), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred) { streambuf_.consume(bytes_transferred); handler_(ec, bytes_transferred); } //private: asio::basic_streambuf& streambuf_; WriteHandler handler_; }; template inline void* asio_handler_allocate(std::size_t size, write_streambuf_handler* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, write_streambuf_handler* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, write_streambuf_handler* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } } // namespace detail template inline void async_write(AsyncWriteStream& s, asio::basic_streambuf& b, CompletionCondition completion_condition, WriteHandler handler) { async_write(s, b.data(), completion_condition, detail::write_streambuf_handler< AsyncWriteStream, Allocator, WriteHandler>(b, handler)); } template inline void async_write(AsyncWriteStream& s, asio::basic_streambuf& b, WriteHandler handler) { async_write(s, b, transfer_all(), handler); } #endif // !defined(BOOST_NO_IOSTREAM) } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_WRITE_IPP percona-xtradb-cluster-galera/asio/asio/impl/write_at.hpp0000644000000000000000000003340612247075736024036 0ustar rootroot00000000000000// // impl/write_at.hpp // ~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IMPL_WRITE_AT_HPP #define ASIO_IMPL_WRITE_AT_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/buffer.hpp" #include "asio/completion_condition.hpp" #include "asio/detail/base_from_completion_cond.hpp" #include "asio/detail/bind_handler.hpp" #include "asio/detail/consuming_buffers.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/throw_error.hpp" #include "asio/detail/push_options.hpp" namespace asio { template std::size_t write_at(SyncRandomAccessWriteDevice& d, boost::uint64_t offset, const ConstBufferSequence& buffers, CompletionCondition completion_condition, asio::error_code& ec) { ec = asio::error_code(); asio::detail::consuming_buffers< const_buffer, ConstBufferSequence> tmp(buffers); std::size_t total_transferred = 0; tmp.prepare(detail::adapt_completion_condition_result( completion_condition(ec, total_transferred))); while (tmp.begin() != tmp.end()) { std::size_t bytes_transferred = d.write_some_at( offset + total_transferred, tmp, ec); tmp.consume(bytes_transferred); total_transferred += bytes_transferred; tmp.prepare(detail::adapt_completion_condition_result( completion_condition(ec, total_transferred))); } return total_transferred; } template inline std::size_t write_at(SyncRandomAccessWriteDevice& d, boost::uint64_t offset, const ConstBufferSequence& buffers) { asio::error_code ec; std::size_t bytes_transferred = write_at( d, offset, buffers, transfer_all(), ec); asio::detail::throw_error(ec); return bytes_transferred; } template inline std::size_t write_at(SyncRandomAccessWriteDevice& d, boost::uint64_t offset, const ConstBufferSequence& buffers, CompletionCondition completion_condition) { asio::error_code ec; std::size_t bytes_transferred = write_at( d, offset, buffers, completion_condition, ec); asio::detail::throw_error(ec); return bytes_transferred; } #if !defined(BOOST_NO_IOSTREAM) template std::size_t write_at(SyncRandomAccessWriteDevice& d, boost::uint64_t offset, asio::basic_streambuf& b, CompletionCondition completion_condition, asio::error_code& ec) { std::size_t bytes_transferred = write_at( d, offset, b.data(), completion_condition, ec); b.consume(bytes_transferred); return bytes_transferred; } template inline std::size_t write_at(SyncRandomAccessWriteDevice& d, boost::uint64_t offset, asio::basic_streambuf& b) { asio::error_code ec; std::size_t bytes_transferred = write_at(d, offset, b, transfer_all(), ec); asio::detail::throw_error(ec); return bytes_transferred; } template inline std::size_t write_at(SyncRandomAccessWriteDevice& d, boost::uint64_t offset, asio::basic_streambuf& b, CompletionCondition completion_condition) { asio::error_code ec; std::size_t bytes_transferred = write_at( d, offset, b, completion_condition, ec); asio::detail::throw_error(ec); return bytes_transferred; } #endif // !defined(BOOST_NO_IOSTREAM) namespace detail { template class write_at_op : detail::base_from_completion_cond { public: write_at_op(AsyncRandomAccessWriteDevice& device, boost::uint64_t offset, const ConstBufferSequence& buffers, CompletionCondition completion_condition, WriteHandler handler) : detail::base_from_completion_cond< CompletionCondition>(completion_condition), device_(device), offset_(offset), buffers_(buffers), total_transferred_(0), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred, int start = 0) { switch (start) { case 1: buffers_.prepare(this->check_for_completion(ec, total_transferred_)); for (;;) { device_.async_write_some_at( offset_ + total_transferred_, buffers_, *this); return; default: total_transferred_ += bytes_transferred; buffers_.consume(bytes_transferred); buffers_.prepare(this->check_for_completion(ec, total_transferred_)); if ((!ec && bytes_transferred == 0) || buffers_.begin() == buffers_.end()) break; } handler_(ec, static_cast(total_transferred_)); } } //private: AsyncRandomAccessWriteDevice& device_; boost::uint64_t offset_; asio::detail::consuming_buffers< const_buffer, ConstBufferSequence> buffers_; std::size_t total_transferred_; WriteHandler handler_; }; template class write_at_op : detail::base_from_completion_cond { public: write_at_op(AsyncRandomAccessWriteDevice& device, boost::uint64_t offset, const asio::mutable_buffers_1& buffers, CompletionCondition completion_condition, WriteHandler handler) : detail::base_from_completion_cond< CompletionCondition>(completion_condition), device_(device), offset_(offset), buffer_(buffers), total_transferred_(0), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred, int start = 0) { std::size_t n = 0; switch (start) { case 1: n = this->check_for_completion(ec, total_transferred_); for (;;) { device_.async_write_some_at(offset_ + total_transferred_, asio::buffer(buffer_ + total_transferred_, n), *this); return; default: total_transferred_ += bytes_transferred; if ((!ec && bytes_transferred == 0) || (n = this->check_for_completion(ec, total_transferred_)) == 0 || total_transferred_ == asio::buffer_size(buffer_)) break; } handler_(ec, static_cast(total_transferred_)); } } //private: AsyncRandomAccessWriteDevice& device_; boost::uint64_t offset_; asio::mutable_buffer buffer_; std::size_t total_transferred_; WriteHandler handler_; }; template class write_at_op : detail::base_from_completion_cond { public: write_at_op(AsyncRandomAccessWriteDevice& device, boost::uint64_t offset, const asio::const_buffers_1& buffers, CompletionCondition completion_condition, WriteHandler handler) : detail::base_from_completion_cond< CompletionCondition>(completion_condition), device_(device), offset_(offset), buffer_(buffers), total_transferred_(0), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred, int start = 0) { std::size_t n = 0; switch (start) { case 1: n = this->check_for_completion(ec, total_transferred_); for (;;) { device_.async_write_some_at(offset_ + total_transferred_, asio::buffer(buffer_ + total_transferred_, n), *this); return; default: total_transferred_ += bytes_transferred; if ((!ec && bytes_transferred == 0) || (n = this->check_for_completion(ec, total_transferred_)) == 0 || total_transferred_ == asio::buffer_size(buffer_)) break; } handler_(ec, static_cast(total_transferred_)); } } //private: AsyncRandomAccessWriteDevice& device_; boost::uint64_t offset_; asio::const_buffer buffer_; std::size_t total_transferred_; WriteHandler handler_; }; template inline void* asio_handler_allocate(std::size_t size, write_at_op* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, write_at_op* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, write_at_op* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } } // namespace detail template inline void async_write_at(AsyncRandomAccessWriteDevice& d, boost::uint64_t offset, const ConstBufferSequence& buffers, CompletionCondition completion_condition, WriteHandler handler) { detail::write_at_op( d, offset, buffers, completion_condition, handler)( asio::error_code(), 0, 1); } template inline void async_write_at(AsyncRandomAccessWriteDevice& d, boost::uint64_t offset, const ConstBufferSequence& buffers, WriteHandler handler) { async_write_at(d, offset, buffers, transfer_all(), handler); } #if !defined(BOOST_NO_IOSTREAM) namespace detail { template class write_at_streambuf_op { public: write_at_streambuf_op( asio::basic_streambuf& streambuf, WriteHandler handler) : streambuf_(streambuf), handler_(handler) { } void operator()(const asio::error_code& ec, const std::size_t bytes_transferred) { streambuf_.consume(bytes_transferred); handler_(ec, bytes_transferred); } //private: asio::basic_streambuf& streambuf_; WriteHandler handler_; }; template inline void* asio_handler_allocate(std::size_t size, write_at_streambuf_op* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, write_at_streambuf_op* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, write_at_streambuf_op* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } } // namespace detail template inline void async_write_at(AsyncRandomAccessWriteDevice& d, boost::uint64_t offset, asio::basic_streambuf& b, CompletionCondition completion_condition, WriteHandler handler) { async_write_at(d, offset, b.data(), completion_condition, detail::write_at_streambuf_op< AsyncRandomAccessWriteDevice, Allocator, WriteHandler>(b, handler)); } template inline void async_write_at(AsyncRandomAccessWriteDevice& d, boost::uint64_t offset, asio::basic_streambuf& b, WriteHandler handler) { async_write_at(d, offset, b, transfer_all(), handler); } #endif // !defined(BOOST_NO_IOSTREAM) } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IMPL_WRITE_AT_HPP percona-xtradb-cluster-galera/asio/asio/impl/write_at.ipp0000644000000000000000000002500212247075736024030 0ustar rootroot00000000000000// // write_at.ipp // ~~~~~~~~~~~~ // // Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_WRITE_AT_IPP #define ASIO_WRITE_AT_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/push_options.hpp" #include "asio/buffer.hpp" #include "asio/completion_condition.hpp" #include "asio/detail/bind_handler.hpp" #include "asio/detail/consuming_buffers.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/throw_error.hpp" namespace asio { template std::size_t write_at(SyncRandomAccessWriteDevice& d, boost::uint64_t offset, const ConstBufferSequence& buffers, CompletionCondition completion_condition, asio::error_code& ec) { ec = asio::error_code(); asio::detail::consuming_buffers< const_buffer, ConstBufferSequence> tmp(buffers); std::size_t total_transferred = 0; tmp.prepare(detail::adapt_completion_condition_result( completion_condition(ec, total_transferred))); while (tmp.begin() != tmp.end()) { std::size_t bytes_transferred = d.write_some_at( offset + total_transferred, tmp, ec); tmp.consume(bytes_transferred); total_transferred += bytes_transferred; tmp.prepare(detail::adapt_completion_condition_result( completion_condition(ec, total_transferred))); } return total_transferred; } template inline std::size_t write_at(SyncRandomAccessWriteDevice& d, boost::uint64_t offset, const ConstBufferSequence& buffers) { asio::error_code ec; std::size_t bytes_transferred = write_at( d, offset, buffers, transfer_all(), ec); asio::detail::throw_error(ec); return bytes_transferred; } template inline std::size_t write_at(SyncRandomAccessWriteDevice& d, boost::uint64_t offset, const ConstBufferSequence& buffers, CompletionCondition completion_condition) { asio::error_code ec; std::size_t bytes_transferred = write_at( d, offset, buffers, completion_condition, ec); asio::detail::throw_error(ec); return bytes_transferred; } #if !defined(BOOST_NO_IOSTREAM) template std::size_t write_at(SyncRandomAccessWriteDevice& d, boost::uint64_t offset, asio::basic_streambuf& b, CompletionCondition completion_condition, asio::error_code& ec) { std::size_t bytes_transferred = write_at( d, offset, b.data(), completion_condition, ec); b.consume(bytes_transferred); return bytes_transferred; } template inline std::size_t write_at(SyncRandomAccessWriteDevice& d, boost::uint64_t offset, asio::basic_streambuf& b) { asio::error_code ec; std::size_t bytes_transferred = write_at(d, offset, b, transfer_all(), ec); asio::detail::throw_error(ec); return bytes_transferred; } template inline std::size_t write_at(SyncRandomAccessWriteDevice& d, boost::uint64_t offset, asio::basic_streambuf& b, CompletionCondition completion_condition) { asio::error_code ec; std::size_t bytes_transferred = write_at( d, offset, b, completion_condition, ec); asio::detail::throw_error(ec); return bytes_transferred; } #endif // !defined(BOOST_NO_IOSTREAM) namespace detail { template class write_at_handler { public: typedef asio::detail::consuming_buffers< const_buffer, ConstBufferSequence> buffers_type; write_at_handler(AsyncRandomAccessWriteDevice& stream, boost::uint64_t offset, const buffers_type& buffers, CompletionCondition completion_condition, WriteHandler handler) : stream_(stream), buffers_(buffers), offset_(offset), total_transferred_(0), completion_condition_(completion_condition), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred) { total_transferred_ += bytes_transferred; buffers_.consume(bytes_transferred); buffers_.prepare(detail::adapt_completion_condition_result( completion_condition_(ec, total_transferred_))); if (buffers_.begin() == buffers_.end()) { handler_(ec, total_transferred_); } else { stream_.async_write_some_at( offset_ + total_transferred_, buffers_, *this); } } //private: AsyncRandomAccessWriteDevice& stream_; buffers_type buffers_; boost::uint64_t offset_; std::size_t total_transferred_; CompletionCondition completion_condition_; WriteHandler handler_; }; template inline void* asio_handler_allocate(std::size_t size, write_at_handler* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, write_at_handler* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, write_at_handler* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } } // namespace detail template inline void async_write_at(AsyncRandomAccessWriteDevice& d, boost::uint64_t offset, const ConstBufferSequence& buffers, CompletionCondition completion_condition, WriteHandler handler) { asio::detail::consuming_buffers< const_buffer, ConstBufferSequence> tmp(buffers); asio::error_code ec; std::size_t total_transferred = 0; tmp.prepare(detail::adapt_completion_condition_result( completion_condition(ec, total_transferred))); if (tmp.begin() == tmp.end()) { d.get_io_service().post(detail::bind_handler( handler, ec, total_transferred)); return; } d.async_write_some_at(offset, tmp, detail::write_at_handler( d, offset, tmp, completion_condition, handler)); } template inline void async_write_at(AsyncRandomAccessWriteDevice& d, boost::uint64_t offset, const ConstBufferSequence& buffers, WriteHandler handler) { async_write_at(d, offset, buffers, transfer_all(), handler); } #if !defined(BOOST_NO_IOSTREAM) namespace detail { template class write_at_streambuf_handler { public: write_at_streambuf_handler( asio::basic_streambuf& streambuf, WriteHandler handler) : streambuf_(streambuf), handler_(handler) { } void operator()(const asio::error_code& ec, std::size_t bytes_transferred) { streambuf_.consume(bytes_transferred); handler_(ec, bytes_transferred); } //private: asio::basic_streambuf& streambuf_; WriteHandler handler_; }; template inline void* asio_handler_allocate(std::size_t size, write_at_streambuf_handler* this_handler) { return asio_handler_alloc_helpers::allocate( size, this_handler->handler_); } template inline void asio_handler_deallocate(void* pointer, std::size_t size, write_at_streambuf_handler* this_handler) { asio_handler_alloc_helpers::deallocate( pointer, size, this_handler->handler_); } template inline void asio_handler_invoke(const Function& function, write_at_streambuf_handler* this_handler) { asio_handler_invoke_helpers::invoke( function, this_handler->handler_); } } // namespace detail template inline void async_write_at(AsyncRandomAccessWriteDevice& d, boost::uint64_t offset, asio::basic_streambuf& b, CompletionCondition completion_condition, WriteHandler handler) { async_write_at(d, offset, b.data(), completion_condition, detail::write_at_streambuf_handler< AsyncRandomAccessWriteDevice, Allocator, WriteHandler>(b, handler)); } template inline void async_write_at(AsyncRandomAccessWriteDevice& d, boost::uint64_t offset, asio::basic_streambuf& b, WriteHandler handler) { async_write_at(d, offset, b, transfer_all(), handler); } #endif // !defined(BOOST_NO_IOSTREAM) } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_WRITE_AT_IPP percona-xtradb-cluster-galera/asio/asio/ip/address.hpp0000644000000000000000000001151612247075736023312 0ustar rootroot00000000000000// // ip/address.hpp // ~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IP_ADDRESS_HPP #define ASIO_IP_ADDRESS_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/error_code.hpp" #include "asio/ip/address_v4.hpp" #include "asio/ip/address_v6.hpp" #if !defined(BOOST_NO_IOSTREAM) # include #endif // !defined(BOOST_NO_IOSTREAM) #include "asio/detail/push_options.hpp" namespace asio { namespace ip { /// Implements version-independent IP addresses. /** * The asio::ip::address class provides the ability to use either IP * version 4 or version 6 addresses. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. */ class address { public: /// Default constructor. ASIO_DECL address(); /// Construct an address from an IPv4 address. ASIO_DECL address(const asio::ip::address_v4& ipv4_address); /// Construct an address from an IPv6 address. ASIO_DECL address(const asio::ip::address_v6& ipv6_address); /// Copy constructor. ASIO_DECL address(const address& other); /// Assign from another address. ASIO_DECL address& operator=(const address& other); /// Assign from an IPv4 address. ASIO_DECL address& operator=( const asio::ip::address_v4& ipv4_address); /// Assign from an IPv6 address. ASIO_DECL address& operator=( const asio::ip::address_v6& ipv6_address); /// Get whether the address is an IP version 4 address. bool is_v4() const { return type_ == ipv4; } /// Get whether the address is an IP version 6 address. bool is_v6() const { return type_ == ipv6; } /// Get the address as an IP version 4 address. ASIO_DECL asio::ip::address_v4 to_v4() const; /// Get the address as an IP version 6 address. ASIO_DECL asio::ip::address_v6 to_v6() const; /// Get the address as a string in dotted decimal format. ASIO_DECL std::string to_string() const; /// Get the address as a string in dotted decimal format. ASIO_DECL std::string to_string(asio::error_code& ec) const; /// Create an address from an IPv4 address string in dotted decimal form, /// or from an IPv6 address in hexadecimal notation. ASIO_DECL static address from_string(const char* str); /// Create an address from an IPv4 address string in dotted decimal form, /// or from an IPv6 address in hexadecimal notation. ASIO_DECL static address from_string( const char* str, asio::error_code& ec); /// Create an address from an IPv4 address string in dotted decimal form, /// or from an IPv6 address in hexadecimal notation. ASIO_DECL static address from_string(const std::string& str); /// Create an address from an IPv4 address string in dotted decimal form, /// or from an IPv6 address in hexadecimal notation. ASIO_DECL static address from_string( const std::string& str, asio::error_code& ec); /// Compare two addresses for equality. ASIO_DECL friend bool operator==(const address& a1, const address& a2); /// Compare two addresses for inequality. friend bool operator!=(const address& a1, const address& a2) { return !(a1 == a2); } /// Compare addresses for ordering. ASIO_DECL friend bool operator<(const address& a1, const address& a2); /// Compare addresses for ordering. friend bool operator>(const address& a1, const address& a2) { return a2 < a1; } /// Compare addresses for ordering. friend bool operator<=(const address& a1, const address& a2) { return !(a2 < a1); } /// Compare addresses for ordering. friend bool operator>=(const address& a1, const address& a2) { return !(a1 < a2); } private: // The type of the address. enum { ipv4, ipv6 } type_; // The underlying IPv4 address. asio::ip::address_v4 ipv4_address_; // The underlying IPv6 address. asio::ip::address_v6 ipv6_address_; }; #if !defined(BOOST_NO_IOSTREAM) /// Output an address as a string. /** * Used to output a human-readable string for a specified address. * * @param os The output stream to which the string will be written. * * @param addr The address to be written. * * @return The output stream. * * @relates asio::ip::address */ template std::basic_ostream& operator<<( std::basic_ostream& os, const address& addr); #endif // !defined(BOOST_NO_IOSTREAM) } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" #include "asio/ip/impl/address.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/ip/impl/address.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // ASIO_IP_ADDRESS_HPP percona-xtradb-cluster-galera/asio/asio/ip/address_v4.hpp0000644000000000000000000001341612247075736023724 0ustar rootroot00000000000000// // ip/address_v4.hpp // ~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IP_ADDRESS_V4_HPP #define ASIO_IP_ADDRESS_V4_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include "asio/detail/socket_types.hpp" #include "asio/detail/winsock_init.hpp" #include "asio/error_code.hpp" #if !defined(BOOST_NO_IOSTREAM) # include #endif // !defined(BOOST_NO_IOSTREAM) #include "asio/detail/push_options.hpp" namespace asio { namespace ip { /// Implements IP version 4 style addresses. /** * The asio::ip::address_v4 class provides the ability to use and * manipulate IP version 4 addresses. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. */ class address_v4 { public: /// The type used to represent an address as an array of bytes. typedef boost::array bytes_type; /// Default constructor. address_v4() { addr_.s_addr = 0; } /// Construct an address from raw bytes. ASIO_DECL explicit address_v4(const bytes_type& bytes); /// Construct an address from a unsigned long in host byte order. ASIO_DECL explicit address_v4(unsigned long addr); /// Copy constructor. address_v4(const address_v4& other) : addr_(other.addr_) { } /// Assign from another address. address_v4& operator=(const address_v4& other) { addr_ = other.addr_; return *this; } /// Get the address in bytes, in network byte order. ASIO_DECL bytes_type to_bytes() const; /// Get the address as an unsigned long in host byte order ASIO_DECL unsigned long to_ulong() const; /// Get the address as a string in dotted decimal format. ASIO_DECL std::string to_string() const; /// Get the address as a string in dotted decimal format. ASIO_DECL std::string to_string(asio::error_code& ec) const; /// Create an address from an IP address string in dotted decimal form. ASIO_DECL static address_v4 from_string(const char* str); /// Create an address from an IP address string in dotted decimal form. ASIO_DECL static address_v4 from_string( const char* str, asio::error_code& ec); /// Create an address from an IP address string in dotted decimal form. ASIO_DECL static address_v4 from_string(const std::string& str); /// Create an address from an IP address string in dotted decimal form. ASIO_DECL static address_v4 from_string( const std::string& str, asio::error_code& ec); /// Determine whether the address is a class A address. ASIO_DECL bool is_class_a() const; /// Determine whether the address is a class B address. ASIO_DECL bool is_class_b() const; /// Determine whether the address is a class C address. ASIO_DECL bool is_class_c() const; /// Determine whether the address is a multicast address. ASIO_DECL bool is_multicast() const; /// Compare two addresses for equality. friend bool operator==(const address_v4& a1, const address_v4& a2) { return a1.addr_.s_addr == a2.addr_.s_addr; } /// Compare two addresses for inequality. friend bool operator!=(const address_v4& a1, const address_v4& a2) { return a1.addr_.s_addr != a2.addr_.s_addr; } /// Compare addresses for ordering. friend bool operator<(const address_v4& a1, const address_v4& a2) { return a1.to_ulong() < a2.to_ulong(); } /// Compare addresses for ordering. friend bool operator>(const address_v4& a1, const address_v4& a2) { return a1.to_ulong() > a2.to_ulong(); } /// Compare addresses for ordering. friend bool operator<=(const address_v4& a1, const address_v4& a2) { return a1.to_ulong() <= a2.to_ulong(); } /// Compare addresses for ordering. friend bool operator>=(const address_v4& a1, const address_v4& a2) { return a1.to_ulong() >= a2.to_ulong(); } /// Obtain an address object that represents any address. static address_v4 any() { return address_v4(static_cast(INADDR_ANY)); } /// Obtain an address object that represents the loopback address. static address_v4 loopback() { return address_v4(static_cast(INADDR_LOOPBACK)); } /// Obtain an address object that represents the broadcast address. static address_v4 broadcast() { return address_v4(static_cast(INADDR_BROADCAST)); } /// Obtain an address object that represents the broadcast address that /// corresponds to the specified address and netmask. ASIO_DECL static address_v4 broadcast( const address_v4& addr, const address_v4& mask); /// Obtain the netmask that corresponds to the address, based on its address /// class. ASIO_DECL static address_v4 netmask(const address_v4& addr); private: // The underlying IPv4 address. asio::detail::in4_addr_type addr_; }; #if !defined(BOOST_NO_IOSTREAM) /// Output an address as a string. /** * Used to output a human-readable string for a specified address. * * @param os The output stream to which the string will be written. * * @param addr The address to be written. * * @return The output stream. * * @relates asio::ip::address_v4 */ template std::basic_ostream& operator<<( std::basic_ostream& os, const address_v4& addr); #endif // !defined(BOOST_NO_IOSTREAM) } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" #include "asio/ip/impl/address_v4.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/ip/impl/address_v4.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // ASIO_IP_ADDRESS_V4_HPP percona-xtradb-cluster-galera/asio/asio/ip/address_v6.hpp0000644000000000000000000001434312247075736023726 0ustar rootroot00000000000000// // ip/address_v6.hpp // ~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IP_ADDRESS_V6_HPP #define ASIO_IP_ADDRESS_V6_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include "asio/detail/socket_types.hpp" #include "asio/detail/winsock_init.hpp" #include "asio/error_code.hpp" #include "asio/ip/address_v4.hpp" #if !defined(BOOST_NO_IOSTREAM) # include #endif // !defined(BOOST_NO_IOSTREAM) #include "asio/detail/push_options.hpp" namespace asio { namespace ip { /// Implements IP version 6 style addresses. /** * The asio::ip::address_v6 class provides the ability to use and * manipulate IP version 6 addresses. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. */ class address_v6 { public: /// The type used to represent an address as an array of bytes. typedef boost::array bytes_type; /// Default constructor. ASIO_DECL address_v6(); /// Construct an address from raw bytes and scope ID. ASIO_DECL explicit address_v6(const bytes_type& bytes, unsigned long scope_id = 0); /// Copy constructor. ASIO_DECL address_v6(const address_v6& other); /// Assign from another address. ASIO_DECL address_v6& operator=(const address_v6& other); /// The scope ID of the address. /** * Returns the scope ID associated with the IPv6 address. */ unsigned long scope_id() const { return scope_id_; } /// The scope ID of the address. /** * Modifies the scope ID associated with the IPv6 address. */ void scope_id(unsigned long id) { scope_id_ = id; } /// Get the address in bytes, in network byte order. ASIO_DECL bytes_type to_bytes() const; /// Get the address as a string. ASIO_DECL std::string to_string() const; /// Get the address as a string. ASIO_DECL std::string to_string(asio::error_code& ec) const; /// Create an address from an IP address string. ASIO_DECL static address_v6 from_string(const char* str); /// Create an address from an IP address string. ASIO_DECL static address_v6 from_string( const char* str, asio::error_code& ec); /// Create an address from an IP address string. ASIO_DECL static address_v6 from_string(const std::string& str); /// Create an address from an IP address string. ASIO_DECL static address_v6 from_string( const std::string& str, asio::error_code& ec); /// Converts an IPv4-mapped or IPv4-compatible address to an IPv4 address. ASIO_DECL address_v4 to_v4() const; /// Determine whether the address is a loopback address. ASIO_DECL bool is_loopback() const; /// Determine whether the address is unspecified. ASIO_DECL bool is_unspecified() const; /// Determine whether the address is link local. ASIO_DECL bool is_link_local() const; /// Determine whether the address is site local. ASIO_DECL bool is_site_local() const; /// Determine whether the address is a mapped IPv4 address. ASIO_DECL bool is_v4_mapped() const; /// Determine whether the address is an IPv4-compatible address. ASIO_DECL bool is_v4_compatible() const; /// Determine whether the address is a multicast address. ASIO_DECL bool is_multicast() const; /// Determine whether the address is a global multicast address. ASIO_DECL bool is_multicast_global() const; /// Determine whether the address is a link-local multicast address. ASIO_DECL bool is_multicast_link_local() const; /// Determine whether the address is a node-local multicast address. ASIO_DECL bool is_multicast_node_local() const; /// Determine whether the address is a org-local multicast address. ASIO_DECL bool is_multicast_org_local() const; /// Determine whether the address is a site-local multicast address. ASIO_DECL bool is_multicast_site_local() const; /// Compare two addresses for equality. ASIO_DECL friend bool operator==( const address_v6& a1, const address_v6& a2); /// Compare two addresses for inequality. friend bool operator!=(const address_v6& a1, const address_v6& a2) { return !(a1 == a2); } /// Compare addresses for ordering. ASIO_DECL friend bool operator<( const address_v6& a1, const address_v6& a2); /// Compare addresses for ordering. friend bool operator>(const address_v6& a1, const address_v6& a2) { return a2 < a1; } /// Compare addresses for ordering. friend bool operator<=(const address_v6& a1, const address_v6& a2) { return !(a2 < a1); } /// Compare addresses for ordering. friend bool operator>=(const address_v6& a1, const address_v6& a2) { return !(a1 < a2); } /// Obtain an address object that represents any address. static address_v6 any() { return address_v6(); } /// Obtain an address object that represents the loopback address. ASIO_DECL static address_v6 loopback(); /// Create an IPv4-mapped IPv6 address. ASIO_DECL static address_v6 v4_mapped(const address_v4& addr); /// Create an IPv4-compatible IPv6 address. ASIO_DECL static address_v6 v4_compatible(const address_v4& addr); private: // The underlying IPv6 address. asio::detail::in6_addr_type addr_; // The scope ID associated with the address. unsigned long scope_id_; }; #if !defined(BOOST_NO_IOSTREAM) /// Output an address as a string. /** * Used to output a human-readable string for a specified address. * * @param os The output stream to which the string will be written. * * @param addr The address to be written. * * @return The output stream. * * @relates asio::ip::address_v6 */ template std::basic_ostream& operator<<( std::basic_ostream& os, const address_v6& addr); #endif // !defined(BOOST_NO_IOSTREAM) } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" #include "asio/ip/impl/address_v6.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/ip/impl/address_v6.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // ASIO_IP_ADDRESS_V6_HPP percona-xtradb-cluster-galera/asio/asio/ip/basic_endpoint.hpp0000644000000000000000000001415212247075736024645 0ustar rootroot00000000000000// // ip/basic_endpoint.hpp // ~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IP_BASIC_ENDPOINT_HPP #define ASIO_IP_BASIC_ENDPOINT_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/ip/address.hpp" #include "asio/ip/detail/endpoint.hpp" #if !defined(BOOST_NO_IOSTREAM) # include #endif // !defined(BOOST_NO_IOSTREAM) #include "asio/detail/push_options.hpp" namespace asio { namespace ip { /// Describes an endpoint for a version-independent IP socket. /** * The asio::ip::basic_endpoint class template describes an endpoint that * may be associated with a particular socket. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. * * @par Concepts: * Endpoint. */ template class basic_endpoint { public: /// The protocol type associated with the endpoint. typedef InternetProtocol protocol_type; /// The type of the endpoint structure. This type is dependent on the /// underlying implementation of the socket layer. #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined data_type; #else typedef asio::detail::socket_addr_type data_type; #endif /// Default constructor. basic_endpoint() : impl_() { } /// Construct an endpoint using a port number, specified in the host's byte /// order. The IP address will be the any address (i.e. INADDR_ANY or /// in6addr_any). This constructor would typically be used for accepting new /// connections. /** * @par Examples * To initialise an IPv4 TCP endpoint for port 1234, use: * @code * asio::ip::tcp::endpoint ep(asio::ip::tcp::v4(), 1234); * @endcode * * To specify an IPv6 UDP endpoint for port 9876, use: * @code * asio::ip::udp::endpoint ep(asio::ip::udp::v6(), 9876); * @endcode */ basic_endpoint(const InternetProtocol& protocol, unsigned short port_num) : impl_(protocol.family(), port_num) { } /// Construct an endpoint using a port number and an IP address. This /// constructor may be used for accepting connections on a specific interface /// or for making a connection to a remote endpoint. basic_endpoint(const asio::ip::address& addr, unsigned short port_num) : impl_(addr, port_num) { } /// Copy constructor. basic_endpoint(const basic_endpoint& other) : impl_(other.impl_) { } /// Assign from another endpoint. basic_endpoint& operator=(const basic_endpoint& other) { impl_ = other.impl_; return *this; } /// The protocol associated with the endpoint. protocol_type protocol() const { if (impl_.is_v4()) return InternetProtocol::v4(); return InternetProtocol::v6(); } /// Get the underlying endpoint in the native type. data_type* data() { return impl_.data(); } /// Get the underlying endpoint in the native type. const data_type* data() const { return impl_.data(); } /// Get the underlying size of the endpoint in the native type. std::size_t size() const { return impl_.size(); } /// Set the underlying size of the endpoint in the native type. void resize(std::size_t size) { impl_.resize(size); } /// Get the capacity of the endpoint in the native type. std::size_t capacity() const { return impl_.capacity(); } /// Get the port associated with the endpoint. The port number is always in /// the host's byte order. unsigned short port() const { return impl_.port(); } /// Set the port associated with the endpoint. The port number is always in /// the host's byte order. void port(unsigned short port_num) { impl_.port(port_num); } /// Get the IP address associated with the endpoint. asio::ip::address address() const { return impl_.address(); } /// Set the IP address associated with the endpoint. void address(const asio::ip::address& addr) { impl_.address(addr); } /// Compare two endpoints for equality. friend bool operator==(const basic_endpoint& e1, const basic_endpoint& e2) { return e1.impl_ == e2.impl_; } /// Compare two endpoints for inequality. friend bool operator!=(const basic_endpoint& e1, const basic_endpoint& e2) { return !(e1 == e2); } /// Compare endpoints for ordering. friend bool operator<(const basic_endpoint& e1, const basic_endpoint& e2) { return e1.impl_ < e2.impl_; } /// Compare endpoints for ordering. friend bool operator>(const basic_endpoint& e1, const basic_endpoint& e2) { return e2.impl_ < e1.impl_; } /// Compare endpoints for ordering. friend bool operator<=(const basic_endpoint& e1, const basic_endpoint& e2) { return !(e2 < e1); } /// Compare endpoints for ordering. friend bool operator>=(const basic_endpoint& e1, const basic_endpoint& e2) { return !(e1 < e2); } private: // The underlying IP endpoint. asio::ip::detail::endpoint impl_; }; #if !defined(BOOST_NO_IOSTREAM) /// Output an endpoint as a string. /** * Used to output a human-readable string for a specified endpoint. * * @param os The output stream to which the string will be written. * * @param endpoint The endpoint to be written. * * @return The output stream. * * @relates asio::ip::basic_endpoint */ template std::basic_ostream& operator<<( std::basic_ostream& os, const basic_endpoint& endpoint); #endif // !defined(BOOST_NO_IOSTREAM) } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" #include "asio/ip/impl/basic_endpoint.hpp" #endif // ASIO_IP_BASIC_ENDPOINT_HPP percona-xtradb-cluster-galera/asio/asio/ip/basic_resolver.hpp0000644000000000000000000002041112247075736024661 0ustar rootroot00000000000000// // ip/basic_resolver.hpp // ~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IP_BASIC_RESOLVER_HPP #define ASIO_IP_BASIC_RESOLVER_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/basic_io_object.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/ip/basic_resolver_iterator.hpp" #include "asio/ip/basic_resolver_query.hpp" #include "asio/ip/resolver_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ip { /// Provides endpoint resolution functionality. /** * The basic_resolver class template provides the ability to resolve a query * to a list of endpoints. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. */ template > class basic_resolver : public basic_io_object { public: /// The protocol type. typedef InternetProtocol protocol_type; /// The endpoint type. typedef typename InternetProtocol::endpoint endpoint_type; /// The query type. typedef basic_resolver_query query; /// The iterator type. typedef basic_resolver_iterator iterator; /// Constructor. /** * This constructor creates a basic_resolver. * * @param io_service The io_service object that the resolver will use to * dispatch handlers for any asynchronous operations performed on the timer. */ explicit basic_resolver(asio::io_service& io_service) : basic_io_object(io_service) { } /// Cancel any asynchronous operations that are waiting on the resolver. /** * This function forces the completion of any pending asynchronous * operations on the host resolver. The handler for each cancelled operation * will be invoked with the asio::error::operation_aborted error code. */ void cancel() { return this->service.cancel(this->implementation); } /// Perform forward resolution of a query to a list of entries. /** * This function is used to resolve a query into a list of endpoint entries. * * @param q A query object that determines what endpoints will be returned. * * @returns A forward-only iterator that can be used to traverse the list * of endpoint entries. * * @throws asio::system_error Thrown on failure. * * @note A default constructed iterator represents the end of the list. * * A successful call to this function is guaranteed to return at least one * entry. */ iterator resolve(const query& q) { asio::error_code ec; iterator i = this->service.resolve(this->implementation, q, ec); asio::detail::throw_error(ec); return i; } /// Perform forward resolution of a query to a list of entries. /** * This function is used to resolve a query into a list of endpoint entries. * * @param q A query object that determines what endpoints will be returned. * * @param ec Set to indicate what error occurred, if any. * * @returns A forward-only iterator that can be used to traverse the list * of endpoint entries. Returns a default constructed iterator if an error * occurs. * * @note A default constructed iterator represents the end of the list. * * A successful call to this function is guaranteed to return at least one * entry. */ iterator resolve(const query& q, asio::error_code& ec) { return this->service.resolve(this->implementation, q, ec); } /// Asynchronously perform forward resolution of a query to a list of entries. /** * This function is used to asynchronously resolve a query into a list of * endpoint entries. * * @param q A query object that determines what endpoints will be returned. * * @param handler The handler to be called when the resolve operation * completes. Copies will be made of the handler as required. The function * signature of the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * resolver::iterator iterator // Forward-only iterator that can * // be used to traverse the list * // of endpoint entries. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note A default constructed iterator represents the end of the list. * * A successful resolve operation is guaranteed to pass at least one entry to * the handler. */ template void async_resolve(const query& q, ResolveHandler handler) { return this->service.async_resolve(this->implementation, q, handler); } /// Perform reverse resolution of an endpoint to a list of entries. /** * This function is used to resolve an endpoint into a list of endpoint * entries. * * @param e An endpoint object that determines what endpoints will be * returned. * * @returns A forward-only iterator that can be used to traverse the list * of endpoint entries. * * @throws asio::system_error Thrown on failure. * * @note A default constructed iterator represents the end of the list. * * A successful call to this function is guaranteed to return at least one * entry. */ iterator resolve(const endpoint_type& e) { asio::error_code ec; iterator i = this->service.resolve(this->implementation, e, ec); asio::detail::throw_error(ec); return i; } /// Perform reverse resolution of an endpoint to a list of entries. /** * This function is used to resolve an endpoint into a list of endpoint * entries. * * @param e An endpoint object that determines what endpoints will be * returned. * * @param ec Set to indicate what error occurred, if any. * * @returns A forward-only iterator that can be used to traverse the list * of endpoint entries. Returns a default constructed iterator if an error * occurs. * * @note A default constructed iterator represents the end of the list. * * A successful call to this function is guaranteed to return at least one * entry. */ iterator resolve(const endpoint_type& e, asio::error_code& ec) { return this->service.resolve(this->implementation, e, ec); } /// Asynchronously perform reverse resolution of an endpoint to a list of /// entries. /** * This function is used to asynchronously resolve an endpoint into a list of * endpoint entries. * * @param e An endpoint object that determines what endpoints will be * returned. * * @param handler The handler to be called when the resolve operation * completes. Copies will be made of the handler as required. The function * signature of the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * resolver::iterator iterator // Forward-only iterator that can * // be used to traverse the list * // of endpoint entries. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note A default constructed iterator represents the end of the list. * * A successful resolve operation is guaranteed to pass at least one entry to * the handler. */ template void async_resolve(const endpoint_type& e, ResolveHandler handler) { return this->service.async_resolve(this->implementation, e, handler); } }; } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IP_BASIC_RESOLVER_HPP percona-xtradb-cluster-galera/asio/asio/ip/basic_resolver_entry.hpp0000644000000000000000000000424012247075736026104 0ustar rootroot00000000000000// // ip/basic_resolver_entry.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IP_BASIC_RESOLVER_ENTRY_HPP #define ASIO_IP_BASIC_RESOLVER_ENTRY_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/detail/push_options.hpp" namespace asio { namespace ip { /// An entry produced by a resolver. /** * The asio::ip::basic_resolver_entry class template describes an entry * as returned by a resolver. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. */ template class basic_resolver_entry { public: /// The protocol type associated with the endpoint entry. typedef InternetProtocol protocol_type; /// The endpoint type associated with the endpoint entry. typedef typename InternetProtocol::endpoint endpoint_type; /// Default constructor. basic_resolver_entry() { } /// Construct with specified endpoint, host name and service name. basic_resolver_entry(const endpoint_type& endpoint, const std::string& host_name, const std::string& service_name) : endpoint_(endpoint), host_name_(host_name), service_name_(service_name) { } /// Get the endpoint associated with the entry. endpoint_type endpoint() const { return endpoint_; } /// Convert to the endpoint associated with the entry. operator endpoint_type() const { return endpoint_; } /// Get the host name associated with the entry. std::string host_name() const { return host_name_; } /// Get the service name associated with the entry. std::string service_name() const { return service_name_; } private: endpoint_type endpoint_; std::string host_name_; std::string service_name_; }; } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IP_BASIC_RESOLVER_ENTRY_HPP percona-xtradb-cluster-galera/asio/asio/ip/basic_resolver_iterator.hpp0000644000000000000000000001160112247075736026573 0ustar rootroot00000000000000// // ip/basic_resolver_iterator.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IP_BASIC_RESOLVER_ITERATOR_HPP #define ASIO_IP_BASIC_RESOLVER_ITERATOR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include #include #include "asio/detail/shared_ptr.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/detail/socket_types.hpp" #include "asio/ip/basic_resolver_entry.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ip { /// An iterator over the entries produced by a resolver. /** * The asio::ip::basic_resolver_iterator class template is used to define * iterators over the results returned by a resolver. * * The iterator's value_type, obtained when the iterator is dereferenced, is: * @code const basic_resolver_entry @endcode * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. */ template class basic_resolver_iterator #if defined(GENERATING_DOCUMENTATION) : public std::iterator< #else // defined(GENERATING_DOCUMENTATION) : public boost::iterator< #endif // defined(GENERATING_DOCUMENTATION) std::forward_iterator_tag, const basic_resolver_entry > { public: /// Default constructor creates an end iterator. basic_resolver_iterator() : index_(0) { } /// Create an iterator from an addrinfo list returned by getaddrinfo. static basic_resolver_iterator create( asio::detail::addrinfo_type* address_info, const std::string& host_name, const std::string& service_name) { basic_resolver_iterator iter; if (!address_info) return iter; std::string actual_host_name = host_name; if (address_info->ai_canonname) actual_host_name = address_info->ai_canonname; iter.values_.reset(new values_type); while (address_info) { if (address_info->ai_family == PF_INET || address_info->ai_family == PF_INET6) { using namespace std; // For memcpy. typename InternetProtocol::endpoint endpoint; endpoint.resize(static_cast(address_info->ai_addrlen)); memcpy(endpoint.data(), address_info->ai_addr, address_info->ai_addrlen); iter.values_->push_back( basic_resolver_entry(endpoint, actual_host_name, service_name)); } address_info = address_info->ai_next; } return iter; } /// Create an iterator from an endpoint, host name and service name. static basic_resolver_iterator create( const typename InternetProtocol::endpoint& endpoint, const std::string& host_name, const std::string& service_name) { basic_resolver_iterator iter; iter.values_.reset(new values_type); iter.values_->push_back( basic_resolver_entry( endpoint, host_name, service_name)); return iter; } /// Dereference an iterator. const basic_resolver_entry& operator*() const { return dereference(); } /// Dereference an iterator. const basic_resolver_entry* operator->() const { return &dereference(); } /// Increment operator (prefix). basic_resolver_iterator& operator++() { increment(); return *this; } /// Increment operator (postfix). basic_resolver_iterator operator++(int) { basic_resolver_iterator tmp(*this); ++*this; return tmp; } /// Test two iterators for equality. friend bool operator==(const basic_resolver_iterator& a, const basic_resolver_iterator& b) { return a.equal(b); } /// Test two iterators for inequality. friend bool operator!=(const basic_resolver_iterator& a, const basic_resolver_iterator& b) { return !a.equal(b); } private: void increment() { if (++index_ == values_->size()) { // Reset state to match a default constructed end iterator. values_.reset(); index_ = 0; } } bool equal(const basic_resolver_iterator& other) const { if (!values_ && !other.values_) return true; if (values_ != other.values_) return false; return index_ == other.index_; } const basic_resolver_entry& dereference() const { return (*values_)[index_]; } typedef std::vector > values_type; asio::detail::shared_ptr values_; std::size_t index_; }; } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IP_BASIC_RESOLVER_ITERATOR_HPP percona-xtradb-cluster-galera/asio/asio/ip/basic_resolver_query.hpp0000644000000000000000000002215512247075736026115 0ustar rootroot00000000000000// // ip/basic_resolver_query.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IP_BASIC_RESOLVER_QUERY_HPP #define ASIO_IP_BASIC_RESOLVER_QUERY_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/detail/socket_ops.hpp" #include "asio/ip/resolver_query_base.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ip { /// An query to be passed to a resolver. /** * The asio::ip::basic_resolver_query class template describes a query * that can be passed to a resolver. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. */ template class basic_resolver_query : public resolver_query_base { public: /// The protocol type associated with the endpoint query. typedef InternetProtocol protocol_type; /// Construct with specified service name for any protocol. /** * This constructor is typically used to perform name resolution for local * service binding. * * @param service_name A string identifying the requested service. This may * be a descriptive name or a numeric string corresponding to a port number. * * @param resolve_flags A set of flags that determine how name resolution * should be performed. The default flags are suitable for local service * binding. * * @note On POSIX systems, service names are typically defined in the file * /etc/services. On Windows, service names may be found in the file * c:\\windows\\system32\\drivers\\etc\\services. Operating systems * may use additional locations when resolving service names. */ basic_resolver_query(const std::string& service_name, resolver_query_base::flags resolve_flags = passive | address_configured) : hints_(), host_name_(), service_name_(service_name) { typename InternetProtocol::endpoint endpoint; hints_.ai_flags = static_cast(resolve_flags); hints_.ai_family = PF_UNSPEC; hints_.ai_socktype = endpoint.protocol().type(); hints_.ai_protocol = endpoint.protocol().protocol(); hints_.ai_addrlen = 0; hints_.ai_canonname = 0; hints_.ai_addr = 0; hints_.ai_next = 0; } /// Construct with specified service name for a given protocol. /** * This constructor is typically used to perform name resolution for local * service binding with a specific protocol version. * * @param protocol A protocol object, normally representing either the IPv4 or * IPv6 version of an internet protocol. * * @param service_name A string identifying the requested service. This may * be a descriptive name or a numeric string corresponding to a port number. * * @param resolve_flags A set of flags that determine how name resolution * should be performed. The default flags are suitable for local service * binding. * * @note On POSIX systems, service names are typically defined in the file * /etc/services. On Windows, service names may be found in the file * c:\\windows\\system32\\drivers\\etc\\services. Operating systems * may use additional locations when resolving service names. */ basic_resolver_query(const protocol_type& protocol, const std::string& service_name, resolver_query_base::flags resolve_flags = passive | address_configured) : hints_(), host_name_(), service_name_(service_name) { hints_.ai_flags = static_cast(resolve_flags); hints_.ai_family = protocol.family(); hints_.ai_socktype = protocol.type(); hints_.ai_protocol = protocol.protocol(); hints_.ai_addrlen = 0; hints_.ai_canonname = 0; hints_.ai_addr = 0; hints_.ai_next = 0; } /// Construct with specified host name and service name for any protocol. /** * This constructor is typically used to perform name resolution for * communication with remote hosts. * * @param host_name A string identifying a location. May be a descriptive name * or a numeric address string. If an empty string and the passive flag has * been specified, the resolved endpoints are suitable for local service * binding. If an empty string and passive is not specified, the resolved * endpoints will use the loopback address. * * @param service_name A string identifying the requested service. This may * be a descriptive name or a numeric string corresponding to a port number. * May be an empty string, in which case all resolved endpoints will have a * port number of 0. * * @param resolve_flags A set of flags that determine how name resolution * should be performed. The default flags are suitable for communication with * remote hosts. * * @note On POSIX systems, host names may be locally defined in the file * /etc/hosts. On Windows, host names may be defined in the file * c:\\windows\\system32\\drivers\\etc\\hosts. Remote host name * resolution is performed using DNS. Operating systems may use additional * locations when resolving host names (such as NETBIOS names on Windows). * * On POSIX systems, service names are typically defined in the file * /etc/services. On Windows, service names may be found in the file * c:\\windows\\system32\\drivers\\etc\\services. Operating systems * may use additional locations when resolving service names. */ basic_resolver_query(const std::string& host_name, const std::string& service_name, resolver_query_base::flags resolve_flags = address_configured) : hints_(), host_name_(host_name), service_name_(service_name) { typename InternetProtocol::endpoint endpoint; hints_.ai_flags = static_cast(resolve_flags); hints_.ai_family = PF_UNSPEC; hints_.ai_socktype = endpoint.protocol().type(); hints_.ai_protocol = endpoint.protocol().protocol(); hints_.ai_addrlen = 0; hints_.ai_canonname = 0; hints_.ai_addr = 0; hints_.ai_next = 0; } /// Construct with specified host name and service name for a given protocol. /** * This constructor is typically used to perform name resolution for * communication with remote hosts. * * @param protocol A protocol object, normally representing either the IPv4 or * IPv6 version of an internet protocol. * * @param host_name A string identifying a location. May be a descriptive name * or a numeric address string. If an empty string and the passive flag has * been specified, the resolved endpoints are suitable for local service * binding. If an empty string and passive is not specified, the resolved * endpoints will use the loopback address. * * @param service_name A string identifying the requested service. This may * be a descriptive name or a numeric string corresponding to a port number. * May be an empty string, in which case all resolved endpoints will have a * port number of 0. * * @param resolve_flags A set of flags that determine how name resolution * should be performed. The default flags are suitable for communication with * remote hosts. * * @note On POSIX systems, host names may be locally defined in the file * /etc/hosts. On Windows, host names may be defined in the file * c:\\windows\\system32\\drivers\\etc\\hosts. Remote host name * resolution is performed using DNS. Operating systems may use additional * locations when resolving host names (such as NETBIOS names on Windows). * * On POSIX systems, service names are typically defined in the file * /etc/services. On Windows, service names may be found in the file * c:\\windows\\system32\\drivers\\etc\\services. Operating systems * may use additional locations when resolving service names. */ basic_resolver_query(const protocol_type& protocol, const std::string& host_name, const std::string& service_name, resolver_query_base::flags resolve_flags = address_configured) : hints_(), host_name_(host_name), service_name_(service_name) { hints_.ai_flags = static_cast(resolve_flags); hints_.ai_family = protocol.family(); hints_.ai_socktype = protocol.type(); hints_.ai_protocol = protocol.protocol(); hints_.ai_addrlen = 0; hints_.ai_canonname = 0; hints_.ai_addr = 0; hints_.ai_next = 0; } /// Get the hints associated with the query. const asio::detail::addrinfo_type& hints() const { return hints_; } /// Get the host name associated with the query. std::string host_name() const { return host_name_; } /// Get the service name associated with the query. std::string service_name() const { return service_name_; } private: asio::detail::addrinfo_type hints_; std::string host_name_; std::string service_name_; }; } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IP_BASIC_RESOLVER_QUERY_HPP percona-xtradb-cluster-galera/asio/asio/ip/detail/0000755000000000000000000000000012247075736022412 5ustar rootroot00000000000000percona-xtradb-cluster-galera/asio/asio/ip/host_name.hpp0000644000000000000000000000173012247075736023637 0ustar rootroot00000000000000// // ip/host_name.hpp // ~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IP_HOST_NAME_HPP #define ASIO_IP_HOST_NAME_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/error_code.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ip { /// Get the current host name. ASIO_DECL std::string host_name(); /// Get the current host name. ASIO_DECL std::string host_name(asio::error_code& ec); } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/ip/impl/host_name.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // ASIO_IP_HOST_NAME_HPP percona-xtradb-cluster-galera/asio/asio/ip/icmp.hpp0000644000000000000000000000530212247075736022611 0ustar rootroot00000000000000// // ip/icmp.hpp // ~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IP_ICMP_HPP #define ASIO_IP_ICMP_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/detail/socket_types.hpp" #include "asio/basic_raw_socket.hpp" #include "asio/ip/basic_endpoint.hpp" #include "asio/ip/basic_resolver.hpp" #include "asio/ip/basic_resolver_iterator.hpp" #include "asio/ip/basic_resolver_query.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ip { /// Encapsulates the flags needed for ICMP. /** * The asio::ip::icmp class contains flags necessary for ICMP sockets. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Safe. * * @par Concepts: * Protocol, InternetProtocol. */ class icmp { public: /// The type of a ICMP endpoint. typedef basic_endpoint endpoint; /// (Deprecated: use resolver::query.) The type of a resolver query. typedef basic_resolver_query resolver_query; /// (Deprecated: use resolver::iterator.) The type of a resolver iterator. typedef basic_resolver_iterator resolver_iterator; /// Construct to represent the IPv4 ICMP protocol. static icmp v4() { return icmp(IPPROTO_ICMP, PF_INET); } /// Construct to represent the IPv6 ICMP protocol. static icmp v6() { return icmp(IPPROTO_ICMPV6, PF_INET6); } /// Obtain an identifier for the type of the protocol. int type() const { return SOCK_RAW; } /// Obtain an identifier for the protocol. int protocol() const { return protocol_; } /// Obtain an identifier for the protocol family. int family() const { return family_; } /// The ICMP socket type. typedef basic_raw_socket socket; /// The ICMP resolver type. typedef basic_resolver resolver; /// Compare two protocols for equality. friend bool operator==(const icmp& p1, const icmp& p2) { return p1.protocol_ == p2.protocol_ && p1.family_ == p2.family_; } /// Compare two protocols for inequality. friend bool operator!=(const icmp& p1, const icmp& p2) { return p1.protocol_ != p2.protocol_ || p1.family_ != p2.family_; } private: // Construct with a specific family. explicit icmp(int protocol, int family) : protocol_(protocol), family_(family) { } int protocol_; int family_; }; } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IP_ICMP_HPP percona-xtradb-cluster-galera/asio/asio/ip/impl/0000755000000000000000000000000012247075736022111 5ustar rootroot00000000000000percona-xtradb-cluster-galera/asio/asio/ip/multicast.hpp0000644000000000000000000001135112247075736023667 0ustar rootroot00000000000000// // ip/multicast.hpp // ~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IP_MULTICAST_HPP #define ASIO_IP_MULTICAST_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/ip/detail/socket_option.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ip { namespace multicast { /// Socket option to join a multicast group on a specified interface. /** * Implements the IPPROTO_IP/IP_ADD_MEMBERSHIP socket option. * * @par Examples * Setting the option to join a multicast group: * @code * asio::ip::udp::socket socket(io_service); * ... * asio::ip::address multicast_address = * asio::ip::address::from_string("225.0.0.1"); * asio::ip::multicast::join_group option(multicast_address); * socket.set_option(option); * @endcode * * @par Concepts: * SettableSocketOption. */ #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined join_group; #else typedef asio::ip::detail::socket_option::multicast_request< IPPROTO_IP, IP_ADD_MEMBERSHIP, IPPROTO_IPV6, IPV6_JOIN_GROUP> join_group; #endif /// Socket option to leave a multicast group on a specified interface. /** * Implements the IPPROTO_IP/IP_DROP_MEMBERSHIP socket option. * * @par Examples * Setting the option to leave a multicast group: * @code * asio::ip::udp::socket socket(io_service); * ... * asio::ip::address multicast_address = * asio::ip::address::from_string("225.0.0.1"); * asio::ip::multicast::leave_group option(multicast_address); * socket.set_option(option); * @endcode * * @par Concepts: * SettableSocketOption. */ #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined leave_group; #else typedef asio::ip::detail::socket_option::multicast_request< IPPROTO_IP, IP_DROP_MEMBERSHIP, IPPROTO_IPV6, IPV6_LEAVE_GROUP> leave_group; #endif /// Socket option for local interface to use for outgoing multicast packets. /** * Implements the IPPROTO_IP/IP_MULTICAST_IF socket option. * * @par Examples * Setting the option: * @code * asio::ip::udp::socket socket(io_service); * ... * asio::ip::address_v4 local_interface = * asio::ip::address_v4::from_string("1.2.3.4"); * asio::ip::multicast::outbound_interface option(local_interface); * socket.set_option(option); * @endcode * * @par Concepts: * SettableSocketOption. */ #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined outbound_interface; #else typedef asio::ip::detail::socket_option::network_interface< IPPROTO_IP, IP_MULTICAST_IF, IPPROTO_IPV6, IPV6_MULTICAST_IF> outbound_interface; #endif /// Socket option for time-to-live associated with outgoing multicast packets. /** * Implements the IPPROTO_IP/IP_MULTICAST_TTL socket option. * * @par Examples * Setting the option: * @code * asio::ip::udp::socket socket(io_service); * ... * asio::ip::multicast::hops option(4); * socket.set_option(option); * @endcode * * @par * Getting the current option value: * @code * asio::ip::udp::socket socket(io_service); * ... * asio::ip::multicast::hops option; * socket.get_option(option); * int ttl = option.value(); * @endcode * * @par Concepts: * GettableSocketOption, SettableSocketOption. */ #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined hops; #else typedef asio::ip::detail::socket_option::multicast_hops< IPPROTO_IP, IP_MULTICAST_TTL, IPPROTO_IPV6, IPV6_MULTICAST_HOPS> hops; #endif /// Socket option determining whether outgoing multicast packets will be /// received on the same socket if it is a member of the multicast group. /** * Implements the IPPROTO_IP/IP_MULTICAST_LOOP socket option. * * @par Examples * Setting the option: * @code * asio::ip::udp::socket socket(io_service); * ... * asio::ip::multicast::enable_loopback option(true); * socket.set_option(option); * @endcode * * @par * Getting the current option value: * @code * asio::ip::udp::socket socket(io_service); * ... * asio::ip::multicast::enable_loopback option; * socket.get_option(option); * bool is_set = option.value(); * @endcode * * @par Concepts: * GettableSocketOption, SettableSocketOption. */ #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined enable_loopback; #else typedef asio::ip::detail::socket_option::multicast_enable_loopback< IPPROTO_IP, IP_MULTICAST_LOOP, IPPROTO_IPV6, IPV6_MULTICAST_LOOP> enable_loopback; #endif } // namespace multicast } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IP_MULTICAST_HPP percona-xtradb-cluster-galera/asio/asio/ip/resolver_query_base.hpp0000644000000000000000000001032212247075736025737 0ustar rootroot00000000000000// // ip/resolver_query_base.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IP_RESOLVER_QUERY_BASE_HPP #define ASIO_IP_RESOLVER_QUERY_BASE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/detail/socket_types.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ip { /// The resolver_query_base class is used as a base for the /// basic_resolver_query class templates to provide a common place to define /// the flag constants. class resolver_query_base { public: #if defined(GENERATING_DOCUMENTATION) /// A bitmask type (C++ Std [lib.bitmask.types]). typedef unspecified flags; /// Determine the canonical name of the host specified in the query. static const flags canonical_name = implementation_defined; /// Indicate that returned endpoint is intended for use as a locally bound /// socket endpoint. static const flags passive = implementation_defined; /// Host name should be treated as a numeric string defining an IPv4 or IPv6 /// address and no name resolution should be attempted. static const flags numeric_host = implementation_defined; /// Service name should be treated as a numeric string defining a port number /// and no name resolution should be attempted. static const flags numeric_service = implementation_defined; /// If the query protocol family is specified as IPv6, return IPv4-mapped /// IPv6 addresses on finding no IPv6 addresses. static const flags v4_mapped = implementation_defined; /// If used with v4_mapped, return all matching IPv6 and IPv4 addresses. static const flags all_matching = implementation_defined; /// Only return IPv4 addresses if a non-loopback IPv4 address is configured /// for the system. Only return IPv6 addresses if a non-loopback IPv6 address /// is configured for the system. static const flags address_configured = implementation_defined; #else enum flags { canonical_name = AI_CANONNAME, passive = AI_PASSIVE, numeric_host = AI_NUMERICHOST, # if defined(AI_NUMERICSERV) numeric_service = AI_NUMERICSERV, # else numeric_service = 0, # endif // Note: QNX Neutrino 6.3 defines AI_V4MAPPED, AI_ALL and AI_ADDRCONFIG but // does not implement them. Therefore they are specifically excluded here. # if defined(AI_V4MAPPED) && !defined(__QNXNTO__) v4_mapped = AI_V4MAPPED, # else v4_mapped = 0, # endif # if defined(AI_ALL) && !defined(__QNXNTO__) all_matching = AI_ALL, # else all_matching = 0, # endif # if defined(AI_ADDRCONFIG) && !defined(__QNXNTO__) address_configured = AI_ADDRCONFIG # else address_configured = 0 # endif }; // Implement bitmask operations as shown in C++ Std [lib.bitmask.types]. friend flags operator&(flags x, flags y) { return static_cast( static_cast(x) & static_cast(y)); } friend flags operator|(flags x, flags y) { return static_cast( static_cast(x) | static_cast(y)); } friend flags operator^(flags x, flags y) { return static_cast( static_cast(x) ^ static_cast(y)); } friend flags operator~(flags x) { return static_cast(static_cast(~x)); } friend flags& operator&=(flags& x, flags y) { x = x & y; return x; } friend flags& operator|=(flags& x, flags y) { x = x | y; return x; } friend flags& operator^=(flags& x, flags y) { x = x ^ y; return x; } #endif protected: /// Protected destructor to prevent deletion through this type. ~resolver_query_base() { } #if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) private: // Workaround to enable the empty base optimisation with Borland C++. char dummy_; #endif }; } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IP_RESOLVER_QUERY_BASE_HPP percona-xtradb-cluster-galera/asio/asio/ip/resolver_service.hpp0000644000000000000000000000744212247075736025251 0ustar rootroot00000000000000// // ip/resolver_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IP_RESOLVER_SERVICE_HPP #define ASIO_IP_RESOLVER_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/error_code.hpp" #include "asio/detail/resolver_service.hpp" #include "asio/io_service.hpp" #include "asio/ip/basic_resolver_iterator.hpp" #include "asio/ip/basic_resolver_query.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ip { /// Default service implementation for a resolver. template class resolver_service #if defined(GENERATING_DOCUMENTATION) : public asio::io_service::service #else : public asio::detail::service_base< resolver_service > #endif { public: #if defined(GENERATING_DOCUMENTATION) /// The unique service identifier. static asio::io_service::id id; #endif /// The protocol type. typedef InternetProtocol protocol_type; /// The endpoint type. typedef typename InternetProtocol::endpoint endpoint_type; /// The query type. typedef basic_resolver_query query_type; /// The iterator type. typedef basic_resolver_iterator iterator_type; private: // The type of the platform-specific implementation. typedef asio::detail::resolver_service service_impl_type; public: /// The type of a resolver implementation. #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined implementation_type; #else typedef typename service_impl_type::implementation_type implementation_type; #endif /// Construct a new resolver service for the specified io_service. explicit resolver_service(asio::io_service& io_service) : asio::detail::service_base< resolver_service >(io_service), service_impl_(io_service) { } /// Destroy all user-defined handler objects owned by the service. void shutdown_service() { service_impl_.shutdown_service(); } /// Construct a new resolver implementation. void construct(implementation_type& impl) { service_impl_.construct(impl); } /// Destroy a resolver implementation. void destroy(implementation_type& impl) { service_impl_.destroy(impl); } /// Cancel pending asynchronous operations. void cancel(implementation_type& impl) { service_impl_.cancel(impl); } /// Resolve a query to a list of entries. iterator_type resolve(implementation_type& impl, const query_type& query, asio::error_code& ec) { return service_impl_.resolve(impl, query, ec); } /// Asynchronously resolve a query to a list of entries. template void async_resolve(implementation_type& impl, const query_type& query, Handler handler) { service_impl_.async_resolve(impl, query, handler); } /// Resolve an endpoint to a list of entries. iterator_type resolve(implementation_type& impl, const endpoint_type& endpoint, asio::error_code& ec) { return service_impl_.resolve(impl, endpoint, ec); } /// Asynchronously resolve an endpoint to a list of entries. template void async_resolve(implementation_type& impl, const endpoint_type& endpoint, ResolveHandler handler) { return service_impl_.async_resolve(impl, endpoint, handler); } private: // The platform-specific implementation. service_impl_type service_impl_; }; } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IP_RESOLVER_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/ip/tcp.hpp0000644000000000000000000000725512247075736022460 0ustar rootroot00000000000000// // ip/tcp.hpp // ~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IP_TCP_HPP #define ASIO_IP_TCP_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/basic_socket_acceptor.hpp" #include "asio/basic_socket_iostream.hpp" #include "asio/basic_stream_socket.hpp" #include "asio/detail/socket_option.hpp" #include "asio/detail/socket_types.hpp" #include "asio/ip/basic_endpoint.hpp" #include "asio/ip/basic_resolver.hpp" #include "asio/ip/basic_resolver_iterator.hpp" #include "asio/ip/basic_resolver_query.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ip { /// Encapsulates the flags needed for TCP. /** * The asio::ip::tcp class contains flags necessary for TCP sockets. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Safe. * * @par Concepts: * Protocol, InternetProtocol. */ class tcp { public: /// The type of a TCP endpoint. typedef basic_endpoint endpoint; /// (Deprecated: use resolver::query.) The type of a resolver query. typedef basic_resolver_query resolver_query; /// (Deprecated: use resolver::iterator.) The type of a resolver iterator. typedef basic_resolver_iterator resolver_iterator; /// Construct to represent the IPv4 TCP protocol. static tcp v4() { return tcp(PF_INET); } /// Construct to represent the IPv6 TCP protocol. static tcp v6() { return tcp(PF_INET6); } /// Obtain an identifier for the type of the protocol. int type() const { return SOCK_STREAM; } /// Obtain an identifier for the protocol. int protocol() const { return IPPROTO_TCP; } /// Obtain an identifier for the protocol family. int family() const { return family_; } /// The TCP socket type. typedef basic_stream_socket socket; /// The TCP acceptor type. typedef basic_socket_acceptor acceptor; /// The TCP resolver type. typedef basic_resolver resolver; #if !defined(BOOST_NO_IOSTREAM) /// The TCP iostream type. typedef basic_socket_iostream iostream; #endif // !defined(BOOST_NO_IOSTREAM) /// Socket option for disabling the Nagle algorithm. /** * Implements the IPPROTO_TCP/TCP_NODELAY socket option. * * @par Examples * Setting the option: * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::ip::tcp::no_delay option(true); * socket.set_option(option); * @endcode * * @par * Getting the current option value: * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::ip::tcp::no_delay option; * socket.get_option(option); * bool is_set = option.value(); * @endcode * * @par Concepts: * Socket_Option, Boolean_Socket_Option. */ #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined no_delay; #else typedef asio::detail::socket_option::boolean< IPPROTO_TCP, TCP_NODELAY> no_delay; #endif /// Compare two protocols for equality. friend bool operator==(const tcp& p1, const tcp& p2) { return p1.family_ == p2.family_; } /// Compare two protocols for inequality. friend bool operator!=(const tcp& p1, const tcp& p2) { return p1.family_ != p2.family_; } private: // Construct with a specific family. explicit tcp(int family) : family_(family) { } int family_; }; } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IP_TCP_HPP percona-xtradb-cluster-galera/asio/asio/ip/udp.hpp0000644000000000000000000000503412247075736022453 0ustar rootroot00000000000000// // ip/udp.hpp // ~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IP_UDP_HPP #define ASIO_IP_UDP_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/basic_datagram_socket.hpp" #include "asio/detail/socket_types.hpp" #include "asio/ip/basic_endpoint.hpp" #include "asio/ip/basic_resolver.hpp" #include "asio/ip/basic_resolver_iterator.hpp" #include "asio/ip/basic_resolver_query.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ip { /// Encapsulates the flags needed for UDP. /** * The asio::ip::udp class contains flags necessary for UDP sockets. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Safe. * * @par Concepts: * Protocol, InternetProtocol. */ class udp { public: /// The type of a UDP endpoint. typedef basic_endpoint endpoint; /// (Deprecated: use resolver::query.) The type of a resolver query. typedef basic_resolver_query resolver_query; /// (Deprecated: use resolver::iterator.) The type of a resolver iterator. typedef basic_resolver_iterator resolver_iterator; /// Construct to represent the IPv4 UDP protocol. static udp v4() { return udp(PF_INET); } /// Construct to represent the IPv6 UDP protocol. static udp v6() { return udp(PF_INET6); } /// Obtain an identifier for the type of the protocol. int type() const { return SOCK_DGRAM; } /// Obtain an identifier for the protocol. int protocol() const { return IPPROTO_UDP; } /// Obtain an identifier for the protocol family. int family() const { return family_; } /// The UDP socket type. typedef basic_datagram_socket socket; /// The UDP resolver type. typedef basic_resolver resolver; /// Compare two protocols for equality. friend bool operator==(const udp& p1, const udp& p2) { return p1.family_ == p2.family_; } /// Compare two protocols for inequality. friend bool operator!=(const udp& p1, const udp& p2) { return p1.family_ != p2.family_; } private: // Construct with a specific family. explicit udp(int family) : family_(family) { } int family_; }; } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IP_UDP_HPP percona-xtradb-cluster-galera/asio/asio/ip/unicast.hpp0000644000000000000000000000306512247075736023333 0ustar rootroot00000000000000// // ip/unicast.hpp // ~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IP_UNICAST_HPP #define ASIO_IP_UNICAST_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/ip/detail/socket_option.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ip { namespace unicast { /// Socket option for time-to-live associated with outgoing unicast packets. /** * Implements the IPPROTO_IP/IP_UNICAST_TTL socket option. * * @par Examples * Setting the option: * @code * asio::ip::udp::socket socket(io_service); * ... * asio::ip::unicast::hops option(4); * socket.set_option(option); * @endcode * * @par * Getting the current option value: * @code * asio::ip::udp::socket socket(io_service); * ... * asio::ip::unicast::hops option; * socket.get_option(option); * int ttl = option.value(); * @endcode * * @par Concepts: * GettableSocketOption, SettableSocketOption. */ #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined hops; #else typedef asio::ip::detail::socket_option::unicast_hops< IPPROTO_IP, IP_TTL, IPPROTO_IPV6, IPV6_UNICAST_HOPS> hops; #endif } // namespace unicast } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IP_UNICAST_HPP percona-xtradb-cluster-galera/asio/asio/ip/v6_only.hpp0000644000000000000000000000321212247075736023253 0ustar rootroot00000000000000// // ip/v6_only.hpp // ~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IP_V6_ONLY_HPP #define ASIO_IP_V6_ONLY_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/detail/socket_option.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ip { /// Socket option for determining whether an IPv6 socket supports IPv6 /// communication only. /** * Implements the IPPROTO_IPV6/IP_V6ONLY socket option. * * @par Examples * Setting the option: * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::ip::v6_only option(true); * socket.set_option(option); * @endcode * * @par * Getting the current option value: * @code * asio::ip::tcp::socket socket(io_service); * ... * asio::ip::v6_only option; * socket.get_option(option); * bool v6_only = option.value(); * @endcode * * @par Concepts: * GettableSocketOption, SettableSocketOption. */ #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined v6_only; #elif defined(IPV6_V6ONLY) typedef asio::detail::socket_option::boolean< IPPROTO_IPV6, IPV6_V6ONLY> v6_only; #else typedef asio::detail::socket_option::boolean< asio::detail::custom_socket_option_level, asio::detail::always_fail_option> v6_only; #endif } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IP_V6_ONLY_HPP percona-xtradb-cluster-galera/asio/asio/ip/detail/endpoint.hpp0000644000000000000000000000677212247075736024757 0ustar rootroot00000000000000// // ip/detail/endpoint.hpp // ~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IP_DETAIL_ENDPOINT_HPP #define ASIO_IP_DETAIL_ENDPOINT_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/detail/socket_types.hpp" #include "asio/detail/winsock_init.hpp" #include "asio/error_code.hpp" #include "asio/ip/address.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ip { namespace detail { // Helper class for implementating an IP endpoint. class endpoint { public: // Default constructor. ASIO_DECL endpoint(); // Construct an endpoint using a family and port number. ASIO_DECL endpoint(int family, unsigned short port_num); // Construct an endpoint using an address and port number. ASIO_DECL endpoint(const asio::ip::address& addr, unsigned short port_num); // Copy constructor. endpoint(const endpoint& other) : data_(other.data_) { } // Assign from another endpoint. endpoint& operator=(const endpoint& other) { data_ = other.data_; return *this; } // Get the underlying endpoint in the native type. asio::detail::socket_addr_type* data() { return &data_.base; } // Get the underlying endpoint in the native type. const asio::detail::socket_addr_type* data() const { return &data_.base; } // Get the underlying size of the endpoint in the native type. std::size_t size() const { if (is_v4()) return sizeof(asio::detail::sockaddr_in4_type); else return sizeof(asio::detail::sockaddr_in6_type); } // Set the underlying size of the endpoint in the native type. ASIO_DECL void resize(std::size_t size); // Get the capacity of the endpoint in the native type. std::size_t capacity() const { return sizeof(asio::detail::sockaddr_storage_type); } // Get the port associated with the endpoint. ASIO_DECL unsigned short port() const; // Set the port associated with the endpoint. ASIO_DECL void port(unsigned short port_num); // Get the IP address associated with the endpoint. ASIO_DECL asio::ip::address address() const; // Set the IP address associated with the endpoint. ASIO_DECL void address(const asio::ip::address& addr); // Compare two endpoints for equality. ASIO_DECL friend bool operator==( const endpoint& e1, const endpoint& e2); // Compare endpoints for ordering. ASIO_DECL friend bool operator<( const endpoint& e1, const endpoint& e2); // Determine whether the endpoint is IPv4. bool is_v4() const { return data_.base.sa_family == AF_INET; } #if !defined(BOOST_NO_IOSTREAM) // Convert to a string. ASIO_DECL std::string to_string(asio::error_code& ec) const; #endif // !defined(BOOST_NO_IOSTREAM) private: // The underlying IP socket address. union data_union { asio::detail::socket_addr_type base; asio::detail::sockaddr_storage_type storage; asio::detail::sockaddr_in4_type v4; asio::detail::sockaddr_in6_type v6; } data_; }; } // namespace detail } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/ip/detail/impl/endpoint.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // ASIO_IP_DETAIL_ENDPOINT_HPP percona-xtradb-cluster-galera/asio/asio/ip/detail/impl/0000755000000000000000000000000012247075736023353 5ustar rootroot00000000000000percona-xtradb-cluster-galera/asio/asio/ip/detail/socket_option.hpp0000644000000000000000000003512412247075736026010 0ustar rootroot00000000000000// // detail/socket_option.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IP_DETAIL_SOCKET_OPTION_HPP #define ASIO_IP_DETAIL_SOCKET_OPTION_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include #include "asio/detail/socket_ops.hpp" #include "asio/detail/socket_types.hpp" #include "asio/ip/address.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ip { namespace detail { namespace socket_option { // Helper template for implementing multicast enable loopback options. template class multicast_enable_loopback { public: #if defined(__sun) || defined(__osf__) typedef unsigned char ipv4_value_type; typedef unsigned char ipv6_value_type; #elif defined(_AIX) || defined(__hpux) || defined(__QNXNTO__) typedef unsigned char ipv4_value_type; typedef unsigned int ipv6_value_type; #else typedef int ipv4_value_type; typedef int ipv6_value_type; #endif // Default constructor. multicast_enable_loopback() : ipv4_value_(0), ipv6_value_(0) { } // Construct with a specific option value. explicit multicast_enable_loopback(bool v) : ipv4_value_(v ? 1 : 0), ipv6_value_(v ? 1 : 0) { } // Set the value of the boolean. multicast_enable_loopback& operator=(bool v) { ipv4_value_ = v ? 1 : 0; ipv6_value_ = v ? 1 : 0; return *this; } // Get the current value of the boolean. bool value() const { return !!ipv4_value_; } // Convert to bool. operator bool() const { return !!ipv4_value_; } // Test for false. bool operator!() const { return !ipv4_value_; } // Get the level of the socket option. template int level(const Protocol& protocol) const { if (protocol.family() == PF_INET6) return IPv6_Level; return IPv4_Level; } // Get the name of the socket option. template int name(const Protocol& protocol) const { if (protocol.family() == PF_INET6) return IPv6_Name; return IPv4_Name; } // Get the address of the boolean data. template void* data(const Protocol& protocol) { if (protocol.family() == PF_INET6) return &ipv6_value_; return &ipv4_value_; } // Get the address of the boolean data. template const void* data(const Protocol& protocol) const { if (protocol.family() == PF_INET6) return &ipv6_value_; return &ipv4_value_; } // Get the size of the boolean data. template std::size_t size(const Protocol& protocol) const { if (protocol.family() == PF_INET6) return sizeof(ipv6_value_); return sizeof(ipv4_value_); } // Set the size of the boolean data. template void resize(const Protocol& protocol, std::size_t s) { if (protocol.family() == PF_INET6) { if (s != sizeof(ipv6_value_)) { std::length_error ex("multicast_enable_loopback socket option resize"); boost::throw_exception(ex); } ipv4_value_ = ipv6_value_ ? 1 : 0; } else { if (s != sizeof(ipv4_value_)) { std::length_error ex("multicast_enable_loopback socket option resize"); boost::throw_exception(ex); } ipv6_value_ = ipv4_value_ ? 1 : 0; } } private: ipv4_value_type ipv4_value_; ipv6_value_type ipv6_value_; }; // Helper template for implementing unicast hops options. template class unicast_hops { public: // Default constructor. unicast_hops() : value_(0) { } // Construct with a specific option value. explicit unicast_hops(int v) : value_(v) { } // Set the value of the option. unicast_hops& operator=(int v) { value_ = v; return *this; } // Get the current value of the option. int value() const { return value_; } // Get the level of the socket option. template int level(const Protocol& protocol) const { if (protocol.family() == PF_INET6) return IPv6_Level; return IPv4_Level; } // Get the name of the socket option. template int name(const Protocol& protocol) const { if (protocol.family() == PF_INET6) return IPv6_Name; return IPv4_Name; } // Get the address of the data. template int* data(const Protocol&) { return &value_; } // Get the address of the data. template const int* data(const Protocol&) const { return &value_; } // Get the size of the data. template std::size_t size(const Protocol&) const { return sizeof(value_); } // Set the size of the data. template void resize(const Protocol&, std::size_t s) { if (s != sizeof(value_)) { std::length_error ex("unicast hops socket option resize"); boost::throw_exception(ex); } #if defined(__hpux) if (value_ < 0) value_ = value_ & 0xFF; #endif } private: int value_; }; // Helper template for implementing multicast hops options. template class multicast_hops { public: #if defined(BOOST_WINDOWS) && defined(UNDER_CE) typedef int ipv4_value_type; #else typedef unsigned char ipv4_value_type; #endif typedef int ipv6_value_type; // Default constructor. multicast_hops() : ipv4_value_(0), ipv6_value_(0) { } // Construct with a specific option value. explicit multicast_hops(int v) { if (v < 0 || v > 255) { std::out_of_range ex("multicast hops value out of range"); boost::throw_exception(ex); } ipv4_value_ = (ipv4_value_type)v; ipv6_value_ = v; } // Set the value of the option. multicast_hops& operator=(int v) { if (v < 0 || v > 255) { std::out_of_range ex("multicast hops value out of range"); boost::throw_exception(ex); } ipv4_value_ = (ipv4_value_type)v; ipv6_value_ = v; return *this; } // Get the current value of the option. int value() const { return ipv6_value_; } // Get the level of the socket option. template int level(const Protocol& protocol) const { if (protocol.family() == PF_INET6) return IPv6_Level; return IPv4_Level; } // Get the name of the socket option. template int name(const Protocol& protocol) const { if (protocol.family() == PF_INET6) return IPv6_Name; return IPv4_Name; } // Get the address of the data. template void* data(const Protocol& protocol) { if (protocol.family() == PF_INET6) return &ipv6_value_; return &ipv4_value_; } // Get the address of the data. template const void* data(const Protocol& protocol) const { if (protocol.family() == PF_INET6) return &ipv6_value_; return &ipv4_value_; } // Get the size of the data. template std::size_t size(const Protocol& protocol) const { if (protocol.family() == PF_INET6) return sizeof(ipv6_value_); return sizeof(ipv4_value_); } // Set the size of the data. template void resize(const Protocol& protocol, std::size_t s) { if (protocol.family() == PF_INET6) { if (s != sizeof(ipv6_value_)) { std::length_error ex("multicast hops socket option resize"); boost::throw_exception(ex); } if (ipv6_value_ < 0) ipv4_value_ = 0; else if (ipv6_value_ > 255) ipv4_value_ = 255; else ipv4_value_ = (ipv4_value_type)ipv6_value_; } else { if (s != sizeof(ipv4_value_)) { std::length_error ex("multicast hops socket option resize"); boost::throw_exception(ex); } ipv6_value_ = ipv4_value_; } } private: ipv4_value_type ipv4_value_; ipv6_value_type ipv6_value_; }; // Helper template for implementing ip_mreq-based options. template class multicast_request { public: // Default constructor. multicast_request() { ipv4_value_.imr_multiaddr.s_addr = asio::detail::socket_ops::host_to_network_long( asio::ip::address_v4::any().to_ulong()); ipv4_value_.imr_interface.s_addr = asio::detail::socket_ops::host_to_network_long( asio::ip::address_v4::any().to_ulong()); asio::detail::in6_addr_type tmp_addr = IN6ADDR_ANY_INIT; ipv6_value_.ipv6mr_multiaddr = tmp_addr; ipv6_value_.ipv6mr_interface = 0; } // Construct with multicast address only. explicit multicast_request(const asio::ip::address& multicast_address) { if (multicast_address.is_v6()) { ipv4_value_.imr_multiaddr.s_addr = asio::detail::socket_ops::host_to_network_long( asio::ip::address_v4::any().to_ulong()); ipv4_value_.imr_interface.s_addr = asio::detail::socket_ops::host_to_network_long( asio::ip::address_v4::any().to_ulong()); using namespace std; // For memcpy. asio::ip::address_v6 ipv6_address = multicast_address.to_v6(); asio::ip::address_v6::bytes_type bytes = ipv6_address.to_bytes(); memcpy(ipv6_value_.ipv6mr_multiaddr.s6_addr, bytes.elems, 16); ipv6_value_.ipv6mr_interface = 0; } else { ipv4_value_.imr_multiaddr.s_addr = asio::detail::socket_ops::host_to_network_long( multicast_address.to_v4().to_ulong()); ipv4_value_.imr_interface.s_addr = asio::detail::socket_ops::host_to_network_long( asio::ip::address_v4::any().to_ulong()); asio::detail::in6_addr_type tmp_addr = IN6ADDR_ANY_INIT; ipv6_value_.ipv6mr_multiaddr = tmp_addr; ipv6_value_.ipv6mr_interface = 0; } } // Construct with multicast address and IPv4 address specifying an interface. explicit multicast_request( const asio::ip::address_v4& multicast_address, const asio::ip::address_v4& network_interface = asio::ip::address_v4::any()) { ipv4_value_.imr_multiaddr.s_addr = asio::detail::socket_ops::host_to_network_long( multicast_address.to_ulong()); ipv4_value_.imr_interface.s_addr = asio::detail::socket_ops::host_to_network_long( network_interface.to_ulong()); #if defined(__sun) asio::detail::in6_addr_type tmp_addr = {{IN6ADDR_ANY_INIT}}; #else asio::detail::in6_addr_type tmp_addr = IN6ADDR_ANY_INIT; #endif ipv6_value_.ipv6mr_multiaddr = tmp_addr; ipv6_value_.ipv6mr_interface = 0; } // Construct with multicast address and IPv6 network interface index. explicit multicast_request( const asio::ip::address_v6& multicast_address, unsigned long network_interface = 0) { ipv4_value_.imr_multiaddr.s_addr = asio::detail::socket_ops::host_to_network_long( asio::ip::address_v4::any().to_ulong()); ipv4_value_.imr_interface.s_addr = asio::detail::socket_ops::host_to_network_long( asio::ip::address_v4::any().to_ulong()); using namespace std; // For memcpy. asio::ip::address_v6::bytes_type bytes = multicast_address.to_bytes(); memcpy(ipv6_value_.ipv6mr_multiaddr.s6_addr, bytes.elems, 16); ipv6_value_.ipv6mr_interface = network_interface; } // Get the level of the socket option. template int level(const Protocol& protocol) const { if (protocol.family() == PF_INET6) return IPv6_Level; return IPv4_Level; } // Get the name of the socket option. template int name(const Protocol& protocol) const { if (protocol.family() == PF_INET6) return IPv6_Name; return IPv4_Name; } // Get the address of the option data. template const void* data(const Protocol& protocol) const { if (protocol.family() == PF_INET6) return &ipv6_value_; return &ipv4_value_; } // Get the size of the option data. template std::size_t size(const Protocol& protocol) const { if (protocol.family() == PF_INET6) return sizeof(ipv6_value_); return sizeof(ipv4_value_); } private: asio::detail::in4_mreq_type ipv4_value_; asio::detail::in6_mreq_type ipv6_value_; }; // Helper template for implementing options that specify a network interface. template class network_interface { public: // Default constructor. network_interface() { ipv4_value_.s_addr = asio::detail::socket_ops::host_to_network_long( asio::ip::address_v4::any().to_ulong()); ipv6_value_ = 0; } // Construct with IPv4 interface. explicit network_interface(const asio::ip::address_v4& ipv4_interface) { ipv4_value_.s_addr = asio::detail::socket_ops::host_to_network_long( ipv4_interface.to_ulong()); ipv6_value_ = 0; } // Construct with IPv6 interface. explicit network_interface(unsigned int ipv6_interface) { ipv4_value_.s_addr = asio::detail::socket_ops::host_to_network_long( asio::ip::address_v4::any().to_ulong()); ipv6_value_ = ipv6_interface; } // Get the level of the socket option. template int level(const Protocol& protocol) const { if (protocol.family() == PF_INET6) return IPv6_Level; return IPv4_Level; } // Get the name of the socket option. template int name(const Protocol& protocol) const { if (protocol.family() == PF_INET6) return IPv6_Name; return IPv4_Name; } // Get the address of the option data. template const void* data(const Protocol& protocol) const { if (protocol.family() == PF_INET6) return &ipv6_value_; return &ipv4_value_; } // Get the size of the option data. template std::size_t size(const Protocol& protocol) const { if (protocol.family() == PF_INET6) return sizeof(ipv6_value_); return sizeof(ipv4_value_); } private: asio::detail::in4_addr_type ipv4_value_; unsigned int ipv6_value_; }; } // namespace socket_option } // namespace detail } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IP_DETAIL_SOCKET_OPTION_HPP percona-xtradb-cluster-galera/asio/asio/ip/detail/impl/endpoint.ipp0000644000000000000000000001107712247075736025713 0ustar rootroot00000000000000// // ip/detail/impl/endpoint.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IP_DETAIL_IMPL_ENDPOINT_IPP #define ASIO_IP_DETAIL_IMPL_ENDPOINT_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #if !defined(BOOST_NO_IOSTREAM) # include #endif // !defined(BOOST_NO_IOSTREAM) #include "asio/detail/socket_ops.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/ip/detail/endpoint.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ip { namespace detail { endpoint::endpoint() : data_() { data_.v4.sin_family = AF_INET; data_.v4.sin_port = 0; data_.v4.sin_addr.s_addr = INADDR_ANY; } endpoint::endpoint(int family, unsigned short port_num) : data_() { using namespace std; // For memcpy. if (family == PF_INET) { data_.v4.sin_family = AF_INET; data_.v4.sin_port = asio::detail::socket_ops::host_to_network_short(port_num); data_.v4.sin_addr.s_addr = INADDR_ANY; } else { data_.v6.sin6_family = AF_INET6; data_.v6.sin6_port = asio::detail::socket_ops::host_to_network_short(port_num); data_.v6.sin6_flowinfo = 0; #if defined(__sun) asio::detail::in6_addr_type tmp_addr = {{IN6ADDR_ANY_INIT}}; #else asio::detail::in6_addr_type tmp_addr = IN6ADDR_ANY_INIT; #endif data_.v6.sin6_addr = tmp_addr; data_.v6.sin6_scope_id = 0; } } endpoint::endpoint(const asio::ip::address& addr, unsigned short port_num) : data_() { using namespace std; // For memcpy. if (addr.is_v4()) { data_.v4.sin_family = AF_INET; data_.v4.sin_port = asio::detail::socket_ops::host_to_network_short(port_num); data_.v4.sin_addr.s_addr = asio::detail::socket_ops::host_to_network_long( addr.to_v4().to_ulong()); } else { data_.v6.sin6_family = AF_INET6; data_.v6.sin6_port = asio::detail::socket_ops::host_to_network_short(port_num); data_.v6.sin6_flowinfo = 0; asio::ip::address_v6 v6_addr = addr.to_v6(); asio::ip::address_v6::bytes_type bytes = v6_addr.to_bytes(); memcpy(data_.v6.sin6_addr.s6_addr, bytes.elems, 16); data_.v6.sin6_scope_id = v6_addr.scope_id(); } } void endpoint::resize(std::size_t size) { if (size > sizeof(asio::detail::sockaddr_storage_type)) { asio::error_code ec(asio::error::invalid_argument); asio::detail::throw_error(ec); } } unsigned short endpoint::port() const { if (is_v4()) { return asio::detail::socket_ops::network_to_host_short( data_.v4.sin_port); } else { return asio::detail::socket_ops::network_to_host_short( data_.v6.sin6_port); } } void endpoint::port(unsigned short port_num) { if (is_v4()) { data_.v4.sin_port = asio::detail::socket_ops::host_to_network_short(port_num); } else { data_.v6.sin6_port = asio::detail::socket_ops::host_to_network_short(port_num); } } asio::ip::address endpoint::address() const { using namespace std; // For memcpy. if (is_v4()) { return asio::ip::address_v4( asio::detail::socket_ops::network_to_host_long( data_.v4.sin_addr.s_addr)); } else { asio::ip::address_v6::bytes_type bytes; memcpy(bytes.elems, data_.v6.sin6_addr.s6_addr, 16); return asio::ip::address_v6(bytes, data_.v6.sin6_scope_id); } } void endpoint::address(const asio::ip::address& addr) { endpoint tmp_endpoint(addr, port()); data_ = tmp_endpoint.data_; } bool operator==(const endpoint& e1, const endpoint& e2) { return e1.address() == e2.address() && e1.port() == e2.port(); } bool operator<(const endpoint& e1, const endpoint& e2) { if (e1.address() < e2.address()) return true; if (e1.address() != e2.address()) return false; return e1.port() < e2.port(); } #if !defined(BOOST_NO_IOSTREAM) std::string endpoint::to_string(asio::error_code& ec) const { std::string a = address().to_string(ec); if (ec) return std::string(); std::ostringstream tmp_os; tmp_os.imbue(std::locale::classic()); if (is_v4()) tmp_os << a; else tmp_os << '[' << a << ']'; tmp_os << ':' << port(); return tmp_os.str(); } #endif // !defined(BOOST_NO_IOSTREAM) } // namespace detail } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IP_DETAIL_IMPL_ENDPOINT_IPP percona-xtradb-cluster-galera/asio/asio/ip/impl/address.hpp0000644000000000000000000000241212247075736024246 0ustar rootroot00000000000000// // ip/impl/address.hpp // ~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IP_IMPL_ADDRESS_HPP #define ASIO_IP_IMPL_ADDRESS_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #if !defined(BOOST_NO_IOSTREAM) #include "asio/detail/throw_error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ip { template std::basic_ostream& operator<<( std::basic_ostream& os, const address& addr) { asio::error_code ec; std::string s = addr.to_string(ec); if (ec) { if (os.exceptions() & std::basic_ostream::failbit) asio::detail::throw_error(ec); else os.setstate(std::basic_ostream::failbit); } else for (std::string::iterator i = s.begin(); i != s.end(); ++i) os << os.widen(*i); return os; } } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" #endif // !defined(BOOST_NO_IOSTREAM) #endif // ASIO_IP_IMPL_ADDRESS_HPP percona-xtradb-cluster-galera/asio/asio/ip/impl/address.ipp0000644000000000000000000000747112247075736024261 0ustar rootroot00000000000000// // ip/impl/address.ipp // ~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IP_IMPL_ADDRESS_IPP #define ASIO_IP_IMPL_ADDRESS_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/ip/address.hpp" #include "asio/system_error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ip { address::address() : type_(ipv4), ipv4_address_(), ipv6_address_() { } address::address(const asio::ip::address_v4& ipv4_address) : type_(ipv4), ipv4_address_(ipv4_address), ipv6_address_() { } address::address(const asio::ip::address_v6& ipv6_address) : type_(ipv6), ipv4_address_(), ipv6_address_(ipv6_address) { } address::address(const address& other) : type_(other.type_), ipv4_address_(other.ipv4_address_), ipv6_address_(other.ipv6_address_) { } address& address::operator=(const address& other) { type_ = other.type_; ipv4_address_ = other.ipv4_address_; ipv6_address_ = other.ipv6_address_; return *this; } address& address::operator=(const asio::ip::address_v4& ipv4_address) { type_ = ipv4; ipv4_address_ = ipv4_address; ipv6_address_ = asio::ip::address_v6(); return *this; } address& address::operator=(const asio::ip::address_v6& ipv6_address) { type_ = ipv6; ipv4_address_ = asio::ip::address_v4(); ipv6_address_ = ipv6_address; return *this; } asio::ip::address_v4 address::to_v4() const { if (type_ != ipv4) { std::bad_cast ex; boost::throw_exception(ex); } return ipv4_address_; } asio::ip::address_v6 address::to_v6() const { if (type_ != ipv6) { std::bad_cast ex; boost::throw_exception(ex); } return ipv6_address_; } std::string address::to_string() const { if (type_ == ipv6) return ipv6_address_.to_string(); return ipv4_address_.to_string(); } std::string address::to_string(asio::error_code& ec) const { if (type_ == ipv6) return ipv6_address_.to_string(ec); return ipv4_address_.to_string(ec); } address address::from_string(const char* str) { asio::error_code ec; address addr = from_string(str, ec); asio::detail::throw_error(ec); return addr; } address address::from_string(const char* str, asio::error_code& ec) { asio::ip::address_v6 ipv6_address = asio::ip::address_v6::from_string(str, ec); if (!ec) { address tmp; tmp.type_ = ipv6; tmp.ipv6_address_ = ipv6_address; return tmp; } asio::ip::address_v4 ipv4_address = asio::ip::address_v4::from_string(str, ec); if (!ec) { address tmp; tmp.type_ = ipv4; tmp.ipv4_address_ = ipv4_address; return tmp; } return address(); } address address::from_string(const std::string& str) { return from_string(str.c_str()); } address address::from_string(const std::string& str, asio::error_code& ec) { return from_string(str.c_str(), ec); } bool operator==(const address& a1, const address& a2) { if (a1.type_ != a2.type_) return false; if (a1.type_ == address::ipv6) return a1.ipv6_address_ == a2.ipv6_address_; return a1.ipv4_address_ == a2.ipv4_address_; } bool operator<(const address& a1, const address& a2) { if (a1.type_ < a2.type_) return true; if (a1.type_ > a2.type_) return false; if (a1.type_ == address::ipv6) return a1.ipv6_address_ < a2.ipv6_address_; return a1.ipv4_address_ < a2.ipv4_address_; } } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IP_IMPL_ADDRESS_IPP percona-xtradb-cluster-galera/asio/asio/ip/impl/address_v4.hpp0000644000000000000000000000243412247075736024663 0ustar rootroot00000000000000// // ip/impl/address_v4.hpp // ~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IP_IMPL_ADDRESS_V4_HPP #define ASIO_IP_IMPL_ADDRESS_V4_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #if !defined(BOOST_NO_IOSTREAM) #include "asio/detail/throw_error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ip { template std::basic_ostream& operator<<( std::basic_ostream& os, const address_v4& addr) { asio::error_code ec; std::string s = addr.to_string(ec); if (ec) { if (os.exceptions() & std::basic_ostream::failbit) asio::detail::throw_error(ec); else os.setstate(std::basic_ostream::failbit); } else for (std::string::iterator i = s.begin(); i != s.end(); ++i) os << os.widen(*i); return os; } } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" #endif // !defined(BOOST_NO_IOSTREAM) #endif // ASIO_IP_IMPL_ADDRESS_V4_HPP percona-xtradb-cluster-galera/asio/asio/ip/impl/address_v4.ipp0000644000000000000000000000720412247075736024664 0ustar rootroot00000000000000// // ip/impl/address_v4.ipp // ~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IP_IMPL_ADDRESS_V4_IPP #define ASIO_IP_IMPL_ADDRESS_V4_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include #include "asio/error.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/detail/throw_error.hpp" #include "asio/ip/address_v4.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ip { address_v4::address_v4(const address_v4::bytes_type& bytes) { #if UCHAR_MAX > 0xFF if (bytes[0] > 0xFF || bytes[1] > 0xFF || bytes[2] > 0xFF || bytes[3] > 0xFF) { std::out_of_range ex("address_v4 from bytes_type"); boost::throw_exception(ex); } #endif // UCHAR_MAX > 0xFF using namespace std; // For memcpy. memcpy(&addr_.s_addr, bytes.elems, 4); } address_v4::address_v4(unsigned long addr) { #if ULONG_MAX > 0xFFFFFFFF if (addr > 0xFFFFFFFF) { std::out_of_range ex("address_v4 from unsigned long"); boost::throw_exception(ex); } #endif // ULONG_MAX > 0xFFFFFFFF addr_.s_addr = asio::detail::socket_ops::host_to_network_long(addr); } address_v4::bytes_type address_v4::to_bytes() const { using namespace std; // For memcpy. bytes_type bytes; memcpy(bytes.elems, &addr_.s_addr, 4); return bytes; } unsigned long address_v4::to_ulong() const { return asio::detail::socket_ops::network_to_host_long(addr_.s_addr); } std::string address_v4::to_string() const { asio::error_code ec; std::string addr = to_string(ec); asio::detail::throw_error(ec); return addr; } std::string address_v4::to_string(asio::error_code& ec) const { char addr_str[asio::detail::max_addr_v4_str_len]; const char* addr = asio::detail::socket_ops::inet_ntop(AF_INET, &addr_, addr_str, asio::detail::max_addr_v4_str_len, 0, ec); if (addr == 0) return std::string(); return addr; } address_v4 address_v4::from_string(const char* str) { asio::error_code ec; address_v4 addr = from_string(str, ec); asio::detail::throw_error(ec); return addr; } address_v4 address_v4::from_string( const char* str, asio::error_code& ec) { address_v4 tmp; if (asio::detail::socket_ops::inet_pton( AF_INET, str, &tmp.addr_, 0, ec) <= 0) return address_v4(); return tmp; } address_v4 address_v4::from_string(const std::string& str) { return from_string(str.c_str()); } address_v4 address_v4::from_string( const std::string& str, asio::error_code& ec) { return from_string(str.c_str(), ec); } bool address_v4::is_class_a() const { return IN_CLASSA(to_ulong()); } bool address_v4::is_class_b() const { return IN_CLASSB(to_ulong()); } bool address_v4::is_class_c() const { return IN_CLASSC(to_ulong()); } bool address_v4::is_multicast() const { return IN_MULTICAST(to_ulong()); } address_v4 address_v4::broadcast(const address_v4& addr, const address_v4& mask) { return address_v4(addr.to_ulong() | (mask.to_ulong() ^ 0xFFFFFFFF)); } address_v4 address_v4::netmask(const address_v4& addr) { if (addr.is_class_a()) return address_v4(0xFF000000); if (addr.is_class_b()) return address_v4(0xFFFF0000); if (addr.is_class_c()) return address_v4(0xFFFFFF00); return address_v4(0xFFFFFFFF); } } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IP_IMPL_ADDRESS_V4_IPP percona-xtradb-cluster-galera/asio/asio/ip/impl/address_v6.hpp0000644000000000000000000000243412247075736024665 0ustar rootroot00000000000000// // ip/impl/address_v6.hpp // ~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IP_IMPL_ADDRESS_V6_HPP #define ASIO_IP_IMPL_ADDRESS_V6_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #if !defined(BOOST_NO_IOSTREAM) #include "asio/detail/throw_error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ip { template std::basic_ostream& operator<<( std::basic_ostream& os, const address_v6& addr) { asio::error_code ec; std::string s = addr.to_string(ec); if (ec) { if (os.exceptions() & std::basic_ostream::failbit) asio::detail::throw_error(ec); else os.setstate(std::basic_ostream::failbit); } else for (std::string::iterator i = s.begin(); i != s.end(); ++i) os << os.widen(*i); return os; } } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" #endif // !defined(BOOST_NO_IOSTREAM) #endif // ASIO_IP_IMPL_ADDRESS_V6_HPP percona-xtradb-cluster-galera/asio/asio/ip/impl/address_v6.ipp0000644000000000000000000001627212247075736024673 0ustar rootroot00000000000000// // ip/impl/address_v6.ipp // ~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IP_IMPL_ADDRESS_V6_IPP #define ASIO_IP_IMPL_ADDRESS_V6_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include #include #include "asio/detail/socket_ops.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/ip/address_v6.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ip { address_v6::address_v6() : scope_id_(0) { #if defined(__sun) asio::detail::in6_addr_type tmp_addr = {{IN6ADDR_ANY_INIT}}; #else asio::detail::in6_addr_type tmp_addr = IN6ADDR_ANY_INIT; #endif addr_ = tmp_addr; } address_v6::address_v6(const address_v6::bytes_type& bytes, unsigned long scope_id) : scope_id_(scope_id) { #if UCHAR_MAX > 0xFF for (std::size_t i = 0; i < bytes.size(); ++i) { if (bytes[i] > 0xFF) { std::out_of_range ex("address_v6 from bytes_type"); boost::throw_exception(ex); } } #endif // UCHAR_MAX > 0xFF using namespace std; // For memcpy. memcpy(addr_.s6_addr, bytes.elems, 16); } address_v6::address_v6(const address_v6& other) : addr_(other.addr_), scope_id_(other.scope_id_) { } address_v6& address_v6::operator=(const address_v6& other) { addr_ = other.addr_; scope_id_ = other.scope_id_; return *this; } address_v6::bytes_type address_v6::to_bytes() const { using namespace std; // For memcpy. bytes_type bytes; memcpy(bytes.elems, addr_.s6_addr, 16); return bytes; } std::string address_v6::to_string() const { asio::error_code ec; std::string addr = to_string(ec); asio::detail::throw_error(ec); return addr; } std::string address_v6::to_string(asio::error_code& ec) const { char addr_str[asio::detail::max_addr_v6_str_len]; const char* addr = asio::detail::socket_ops::inet_ntop(AF_INET6, &addr_, addr_str, asio::detail::max_addr_v6_str_len, scope_id_, ec); if (addr == 0) return std::string(); return addr; } address_v6 address_v6::from_string(const char* str) { asio::error_code ec; address_v6 addr = from_string(str, ec); asio::detail::throw_error(ec); return addr; } address_v6 address_v6::from_string( const char* str, asio::error_code& ec) { address_v6 tmp; if (asio::detail::socket_ops::inet_pton( AF_INET6, str, &tmp.addr_, &tmp.scope_id_, ec) <= 0) return address_v6(); return tmp; } address_v6 address_v6::from_string(const std::string& str) { return from_string(str.c_str()); } address_v6 address_v6::from_string( const std::string& str, asio::error_code& ec) { return from_string(str.c_str(), ec); } address_v4 address_v6::to_v4() const { if (!is_v4_mapped() && !is_v4_compatible()) { std::bad_cast ex; boost::throw_exception(ex); } address_v4::bytes_type v4_bytes = { { addr_.s6_addr[12], addr_.s6_addr[13], addr_.s6_addr[14], addr_.s6_addr[15] } }; return address_v4(v4_bytes); } bool address_v6::is_loopback() const { #if defined(__BORLANDC__) return ((addr_.s6_addr[0] == 0) && (addr_.s6_addr[1] == 0) && (addr_.s6_addr[2] == 0) && (addr_.s6_addr[3] == 0) && (addr_.s6_addr[4] == 0) && (addr_.s6_addr[5] == 0) && (addr_.s6_addr[6] == 0) && (addr_.s6_addr[7] == 0) && (addr_.s6_addr[8] == 0) && (addr_.s6_addr[9] == 0) && (addr_.s6_addr[10] == 0) && (addr_.s6_addr[11] == 0) && (addr_.s6_addr[12] == 0) && (addr_.s6_addr[13] == 0) && (addr_.s6_addr[14] == 0) && (addr_.s6_addr[15] == 1)); #else using namespace asio::detail; return IN6_IS_ADDR_LOOPBACK(&addr_) != 0; #endif } bool address_v6::is_unspecified() const { #if defined(__BORLANDC__) return ((addr_.s6_addr[0] == 0) && (addr_.s6_addr[1] == 0) && (addr_.s6_addr[2] == 0) && (addr_.s6_addr[3] == 0) && (addr_.s6_addr[4] == 0) && (addr_.s6_addr[5] == 0) && (addr_.s6_addr[6] == 0) && (addr_.s6_addr[7] == 0) && (addr_.s6_addr[8] == 0) && (addr_.s6_addr[9] == 0) && (addr_.s6_addr[10] == 0) && (addr_.s6_addr[11] == 0) && (addr_.s6_addr[12] == 0) && (addr_.s6_addr[13] == 0) && (addr_.s6_addr[14] == 0) && (addr_.s6_addr[15] == 0)); #else using namespace asio::detail; return IN6_IS_ADDR_UNSPECIFIED(&addr_) != 0; #endif } bool address_v6::is_link_local() const { using namespace asio::detail; return IN6_IS_ADDR_LINKLOCAL(&addr_) != 0; } bool address_v6::is_site_local() const { using namespace asio::detail; return IN6_IS_ADDR_SITELOCAL(&addr_) != 0; } bool address_v6::is_v4_mapped() const { using namespace asio::detail; return IN6_IS_ADDR_V4MAPPED(&addr_) != 0; } bool address_v6::is_v4_compatible() const { using namespace asio::detail; return IN6_IS_ADDR_V4COMPAT(&addr_) != 0; } bool address_v6::is_multicast() const { using namespace asio::detail; return IN6_IS_ADDR_MULTICAST(&addr_) != 0; } bool address_v6::is_multicast_global() const { using namespace asio::detail; return IN6_IS_ADDR_MC_GLOBAL(&addr_) != 0; } bool address_v6::is_multicast_link_local() const { using namespace asio::detail; return IN6_IS_ADDR_MC_LINKLOCAL(&addr_) != 0; } bool address_v6::is_multicast_node_local() const { using namespace asio::detail; return IN6_IS_ADDR_MC_NODELOCAL(&addr_) != 0; } bool address_v6::is_multicast_org_local() const { using namespace asio::detail; return IN6_IS_ADDR_MC_ORGLOCAL(&addr_) != 0; } bool address_v6::is_multicast_site_local() const { using namespace asio::detail; return IN6_IS_ADDR_MC_SITELOCAL(&addr_) != 0; } bool operator==(const address_v6& a1, const address_v6& a2) { using namespace std; // For memcmp. return memcmp(&a1.addr_, &a2.addr_, sizeof(asio::detail::in6_addr_type)) == 0 && a1.scope_id_ == a2.scope_id_; } bool operator<(const address_v6& a1, const address_v6& a2) { using namespace std; // For memcmp. int memcmp_result = memcmp(&a1.addr_, &a2.addr_, sizeof(asio::detail::in6_addr_type)); if (memcmp_result < 0) return true; if (memcmp_result > 0) return false; return a1.scope_id_ < a2.scope_id_; } address_v6 address_v6::loopback() { address_v6 tmp; #if defined(__sun) asio::detail::in6_addr_type tmp_addr = {{IN6ADDR_LOOPBACK_INIT}}; #else asio::detail::in6_addr_type tmp_addr = IN6ADDR_LOOPBACK_INIT; #endif tmp.addr_ = tmp_addr; return tmp; } address_v6 address_v6::v4_mapped(const address_v4& addr) { address_v4::bytes_type v4_bytes = addr.to_bytes(); bytes_type v6_bytes = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, v4_bytes[0], v4_bytes[1], v4_bytes[2], v4_bytes[3] } }; return address_v6(v6_bytes); } address_v6 address_v6::v4_compatible(const address_v4& addr) { address_v4::bytes_type v4_bytes = addr.to_bytes(); bytes_type v6_bytes = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, v4_bytes[0], v4_bytes[1], v4_bytes[2], v4_bytes[3] } }; return address_v6(v6_bytes); } } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IP_IMPL_ADDRESS_V6_IPP percona-xtradb-cluster-galera/asio/asio/ip/impl/basic_endpoint.hpp0000644000000000000000000000266512247075736025614 0ustar rootroot00000000000000// // ip/impl/basic_endpoint.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IP_IMPL_BASIC_ENDPOINT_HPP #define ASIO_IP_IMPL_BASIC_ENDPOINT_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #if !defined(BOOST_NO_IOSTREAM) #include "asio/detail/throw_error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ip { template std::basic_ostream& operator<<( std::basic_ostream& os, const basic_endpoint& endpoint) { asio::ip::detail::endpoint tmp_ep(endpoint.address(), endpoint.port()); asio::error_code ec; std::string s = tmp_ep.to_string(ec); if (ec) { if (os.exceptions() & std::basic_ostream::failbit) asio::detail::throw_error(ec); else os.setstate(std::basic_ostream::failbit); } else for (std::string::iterator i = s.begin(); i != s.end(); ++i) os << os.widen(*i); return os; } } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" #endif // !defined(BOOST_NO_IOSTREAM) #endif // ASIO_IP_IMPL_BASIC_ENDPOINT_HPP percona-xtradb-cluster-galera/asio/asio/ip/impl/host_name.ipp0000644000000000000000000000241012247075736024575 0ustar rootroot00000000000000// // ip/impl/host_name.ipp // ~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IP_IMPL_HOST_NAME_IPP #define ASIO_IP_IMPL_HOST_NAME_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/detail/throw_error.hpp" #include "asio/detail/winsock_init.hpp" #include "asio/ip/host_name.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ip { std::string host_name() { char name[1024]; asio::error_code ec; if (asio::detail::socket_ops::gethostname(name, sizeof(name), ec) != 0) { asio::detail::throw_error(ec); return std::string(); } return std::string(name); } std::string host_name(asio::error_code& ec) { char name[1024]; if (asio::detail::socket_ops::gethostname(name, sizeof(name), ec) != 0) return std::string(); return std::string(name); } } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_IP_IMPL_HOST_NAME_IPP percona-xtradb-cluster-galera/asio/asio/local/basic_endpoint.hpp0000644000000000000000000001206112247075736025324 0ustar rootroot00000000000000// // local/basic_endpoint.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Derived from a public domain implementation written by Daniel Casimiro. // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_LOCAL_BASIC_ENDPOINT_HPP #define ASIO_LOCAL_BASIC_ENDPOINT_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_LOCAL_SOCKETS) \ || defined(GENERATING_DOCUMENTATION) #include "asio/local/detail/endpoint.hpp" #if !defined(BOOST_NO_IOSTREAM) # include #endif // !defined(BOOST_NO_IOSTREAM) #include "asio/detail/push_options.hpp" namespace asio { namespace local { /// Describes an endpoint for a UNIX socket. /** * The asio::local::basic_endpoint class template describes an endpoint * that may be associated with a particular UNIX socket. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. * * @par Concepts: * Endpoint. */ template class basic_endpoint { public: /// The protocol type associated with the endpoint. typedef Protocol protocol_type; /// The type of the endpoint structure. This type is dependent on the /// underlying implementation of the socket layer. #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined data_type; #else typedef asio::detail::socket_addr_type data_type; #endif /// Default constructor. basic_endpoint() { } /// Construct an endpoint using the specified path name. basic_endpoint(const char* path) : impl_(path) { } /// Construct an endpoint using the specified path name. basic_endpoint(const std::string& path) : impl_(path) { } /// Copy constructor. basic_endpoint(const basic_endpoint& other) : impl_(other.impl_) { } /// Assign from another endpoint. basic_endpoint& operator=(const basic_endpoint& other) { impl_ = other.impl_; return *this; } /// The protocol associated with the endpoint. protocol_type protocol() const { return protocol_type(); } /// Get the underlying endpoint in the native type. data_type* data() { return impl_.data(); } /// Get the underlying endpoint in the native type. const data_type* data() const { return impl_.data(); } /// Get the underlying size of the endpoint in the native type. std::size_t size() const { return impl_.size(); } /// Set the underlying size of the endpoint in the native type. void resize(std::size_t size) { impl_.resize(size); } /// Get the capacity of the endpoint in the native type. std::size_t capacity() const { return impl_.capacity(); } /// Get the path associated with the endpoint. std::string path() const { return impl_.path(); } /// Set the path associated with the endpoint. void path(const char* p) { impl_.path(p); } /// Set the path associated with the endpoint. void path(const std::string& p) { impl_.path(p); } /// Compare two endpoints for equality. friend bool operator==(const basic_endpoint& e1, const basic_endpoint& e2) { return e1.impl_ == e2.impl_; } /// Compare two endpoints for inequality. friend bool operator!=(const basic_endpoint& e1, const basic_endpoint& e2) { return !(e1.impl_ == e2.impl_); } /// Compare endpoints for ordering. friend bool operator<(const basic_endpoint& e1, const basic_endpoint& e2) { return e1.impl_ < e2.impl_; } /// Compare endpoints for ordering. friend bool operator>(const basic_endpoint& e1, const basic_endpoint& e2) { return e2.impl_ < e1.impl_; } /// Compare endpoints for ordering. friend bool operator<=(const basic_endpoint& e1, const basic_endpoint& e2) { return !(e2 < e1); } /// Compare endpoints for ordering. friend bool operator>=(const basic_endpoint& e1, const basic_endpoint& e2) { return !(e1 < e2); } private: // The underlying UNIX domain endpoint. asio::local::detail::endpoint impl_; }; /// Output an endpoint as a string. /** * Used to output a human-readable string for a specified endpoint. * * @param os The output stream to which the string will be written. * * @param endpoint The endpoint to be written. * * @return The output stream. * * @relates asio::local::basic_endpoint */ template std::basic_ostream& operator<<( std::basic_ostream& os, const basic_endpoint& endpoint) { os << endpoint.path(); return os; } } // namespace local } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_LOCAL_SOCKETS) // || defined(GENERATING_DOCUMENTATION) #endif // ASIO_LOCAL_BASIC_ENDPOINT_HPP percona-xtradb-cluster-galera/asio/asio/local/connect_pair.hpp0000644000000000000000000000606512247075736025016 0ustar rootroot00000000000000// // local/connect_pair.hpp // ~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_LOCAL_CONNECT_PAIR_HPP #define ASIO_LOCAL_CONNECT_PAIR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_LOCAL_SOCKETS) \ || defined(GENERATING_DOCUMENTATION) #include "asio/basic_socket.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/local/basic_endpoint.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace local { /// Create a pair of connected sockets. template void connect_pair( basic_socket& socket1, basic_socket& socket2); /// Create a pair of connected sockets. template asio::error_code connect_pair( basic_socket& socket1, basic_socket& socket2, asio::error_code& ec); template inline void connect_pair( basic_socket& socket1, basic_socket& socket2) { asio::error_code ec; connect_pair(socket1, socket2, ec); asio::detail::throw_error(ec); } template inline asio::error_code connect_pair( basic_socket& socket1, basic_socket& socket2, asio::error_code& ec) { // Check that this function is only being used with a UNIX domain socket. asio::local::basic_endpoint* tmp = static_cast(0); (void)tmp; Protocol protocol; asio::detail::socket_type sv[2]; if (asio::detail::socket_ops::socketpair(protocol.family(), protocol.type(), protocol.protocol(), sv, ec) == asio::detail::socket_error_retval) return ec; if (socket1.assign(protocol, sv[0], ec)) { asio::error_code temp_ec; asio::detail::socket_ops::state_type state[2] = { 0, 0 }; asio::detail::socket_ops::close(sv[0], state[0], true, temp_ec); asio::detail::socket_ops::close(sv[1], state[1], true, temp_ec); return ec; } if (socket2.assign(protocol, sv[1], ec)) { asio::error_code temp_ec; socket1.close(temp_ec); asio::detail::socket_ops::state_type state = 0; asio::detail::socket_ops::close(sv[1], state, true, temp_ec); return ec; } return ec; } } // namespace local } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_LOCAL_SOCKETS) // || defined(GENERATING_DOCUMENTATION) #endif // ASIO_LOCAL_CONNECT_PAIR_HPP percona-xtradb-cluster-galera/asio/asio/local/datagram_protocol.hpp0000644000000000000000000000352612247075736026052 0ustar rootroot00000000000000// // local/datagram_protocol.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_LOCAL_DATAGRAM_PROTOCOL_HPP #define ASIO_LOCAL_DATAGRAM_PROTOCOL_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_LOCAL_SOCKETS) \ || defined(GENERATING_DOCUMENTATION) #include "asio/basic_datagram_socket.hpp" #include "asio/detail/socket_types.hpp" #include "asio/local/basic_endpoint.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace local { /// Encapsulates the flags needed for datagram-oriented UNIX sockets. /** * The asio::local::datagram_protocol class contains flags necessary for * datagram-oriented UNIX domain sockets. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Safe. * * @par Concepts: * Protocol. */ class datagram_protocol { public: /// Obtain an identifier for the type of the protocol. int type() const { return SOCK_DGRAM; } /// Obtain an identifier for the protocol. int protocol() const { return 0; } /// Obtain an identifier for the protocol family. int family() const { return AF_UNIX; } /// The type of a UNIX domain endpoint. typedef basic_endpoint endpoint; /// The UNIX domain socket type. typedef basic_datagram_socket socket; }; } // namespace local } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_LOCAL_SOCKETS) // || defined(GENERATING_DOCUMENTATION) #endif // ASIO_LOCAL_DATAGRAM_PROTOCOL_HPP percona-xtradb-cluster-galera/asio/asio/local/detail/0000755000000000000000000000000012247075736023074 5ustar rootroot00000000000000percona-xtradb-cluster-galera/asio/asio/local/stream_protocol.hpp0000644000000000000000000000423112247075736025557 0ustar rootroot00000000000000// // local/stream_protocol.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_LOCAL_STREAM_PROTOCOL_HPP #define ASIO_LOCAL_STREAM_PROTOCOL_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_LOCAL_SOCKETS) \ || defined(GENERATING_DOCUMENTATION) #include "asio/basic_socket_acceptor.hpp" #include "asio/basic_socket_iostream.hpp" #include "asio/basic_stream_socket.hpp" #include "asio/detail/socket_types.hpp" #include "asio/local/basic_endpoint.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace local { /// Encapsulates the flags needed for stream-oriented UNIX sockets. /** * The asio::local::stream_protocol class contains flags necessary for * stream-oriented UNIX domain sockets. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Safe. * * @par Concepts: * Protocol. */ class stream_protocol { public: /// Obtain an identifier for the type of the protocol. int type() const { return SOCK_STREAM; } /// Obtain an identifier for the protocol. int protocol() const { return 0; } /// Obtain an identifier for the protocol family. int family() const { return AF_UNIX; } /// The type of a UNIX domain endpoint. typedef basic_endpoint endpoint; /// The UNIX domain socket type. typedef basic_stream_socket socket; /// The UNIX domain acceptor type. typedef basic_socket_acceptor acceptor; #if !defined(BOOST_NO_IOSTREAM) /// The UNIX domain iostream type. typedef basic_socket_iostream iostream; #endif // !defined(BOOST_NO_IOSTREAM) }; } // namespace local } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_LOCAL_SOCKETS) // || defined(GENERATING_DOCUMENTATION) #endif // ASIO_LOCAL_STREAM_PROTOCOL_HPP percona-xtradb-cluster-galera/asio/asio/local/detail/endpoint.hpp0000644000000000000000000000641512247075736025433 0ustar rootroot00000000000000// // local/detail/endpoint.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Derived from a public domain implementation written by Daniel Casimiro. // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_LOCAL_DETAIL_ENDPOINT_HPP #define ASIO_LOCAL_DETAIL_ENDPOINT_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_LOCAL_SOCKETS) #include #include #include "asio/detail/socket_types.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace local { namespace detail { // Helper class for implementing a UNIX domain endpoint. class endpoint { public: // Default constructor. ASIO_DECL endpoint(); // Construct an endpoint using the specified path name. ASIO_DECL endpoint(const char* path); // Construct an endpoint using the specified path name. ASIO_DECL endpoint(const std::string& path); // Copy constructor. endpoint(const endpoint& other) : data_(other.data_), path_length_(other.path_length_) { } // Assign from another endpoint. endpoint& operator=(const endpoint& other) { data_ = other.data_; path_length_ = other.path_length_; return *this; } // Get the underlying endpoint in the native type. asio::detail::socket_addr_type* data() { return &data_.base; } // Get the underlying endpoint in the native type. const asio::detail::socket_addr_type* data() const { return &data_.base; } // Get the underlying size of the endpoint in the native type. std::size_t size() const { return path_length_ + offsetof(asio::detail::sockaddr_un_type, sun_path); } // Set the underlying size of the endpoint in the native type. ASIO_DECL void resize(std::size_t size); // Get the capacity of the endpoint in the native type. std::size_t capacity() const { return sizeof(asio::detail::sockaddr_un_type); } // Get the path associated with the endpoint. ASIO_DECL std::string path() const; // Set the path associated with the endpoint. ASIO_DECL void path(const char* p); // Set the path associated with the endpoint. ASIO_DECL void path(const std::string& p); // Compare two endpoints for equality. ASIO_DECL friend bool operator==( const endpoint& e1, const endpoint& e2); // Compare endpoints for ordering. ASIO_DECL friend bool operator<( const endpoint& e1, const endpoint& e2); private: // The underlying UNIX socket address. union data_union { asio::detail::socket_addr_type base; asio::detail::sockaddr_un_type local; } data_; // The length of the path associated with the endpoint. std::size_t path_length_; // Initialise with a specified path. ASIO_DECL void init(const char* path, std::size_t path_length); }; } // namespace detail } // namespace local } // namespace asio #include "asio/detail/pop_options.hpp" #if defined(ASIO_HEADER_ONLY) # include "asio/local/detail/impl/endpoint.ipp" #endif // defined(ASIO_HEADER_ONLY) #endif // defined(ASIO_HAS_LOCAL_SOCKETS) #endif // ASIO_LOCAL_DETAIL_ENDPOINT_HPP percona-xtradb-cluster-galera/asio/asio/local/detail/impl/0000755000000000000000000000000012247075736024035 5ustar rootroot00000000000000percona-xtradb-cluster-galera/asio/asio/local/detail/impl/endpoint.ipp0000644000000000000000000000576512247075736026404 0ustar rootroot00000000000000// // local/detail/impl/endpoint.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Derived from a public domain implementation written by Daniel Casimiro. // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_LOCAL_DETAIL_IMPL_ENDPOINT_IPP #define ASIO_LOCAL_DETAIL_IMPL_ENDPOINT_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_LOCAL_SOCKETS) #include #include "asio/detail/socket_ops.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/local/detail/endpoint.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace local { namespace detail { endpoint::endpoint() { init("", 0); } endpoint::endpoint(const char* path) { using namespace std; // For strlen. init(path, strlen(path)); } endpoint::endpoint(const std::string& path) { init(path.data(), path.length()); } void endpoint::resize(std::size_t size) { if (size > sizeof(asio::detail::sockaddr_un_type)) { asio::error_code ec(asio::error::invalid_argument); asio::detail::throw_error(ec); } else if (size == 0) { path_length_ = 0; } else { path_length_ = size - offsetof(asio::detail::sockaddr_un_type, sun_path); // The path returned by the operating system may be NUL-terminated. if (path_length_ > 0 && data_.local.sun_path[path_length_ - 1] == 0) --path_length_; } } std::string endpoint::path() const { return std::string(data_.local.sun_path, path_length_); } void endpoint::path(const char* p) { using namespace std; // For strlen. init(p, strlen(p)); } void endpoint::path(const std::string& p) { init(p.data(), p.length()); } bool operator==(const endpoint& e1, const endpoint& e2) { return e1.path() == e2.path(); } bool operator<(const endpoint& e1, const endpoint& e2) { return e1.path() < e2.path(); } void endpoint::init(const char* path, std::size_t path_length) { if (path_length > sizeof(data_.local.sun_path) - 1) { // The buffer is not large enough to store this address. asio::error_code ec(asio::error::name_too_long); asio::detail::throw_error(ec); } using namespace std; // For memcpy. data_.local = asio::detail::sockaddr_un_type(); data_.local.sun_family = AF_UNIX; memcpy(data_.local.sun_path, path, path_length); path_length_ = path_length; // NUL-terminate normal path names. Names that start with a NUL are in the // UNIX domain protocol's "abstract namespace" and are not NUL-terminated. if (path_length > 0 && data_.local.sun_path[0] == 0) data_.local.sun_path[path_length] = 0; } } // namespace detail } // namespace local } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_LOCAL_SOCKETS) #endif // ASIO_LOCAL_DETAIL_IMPL_ENDPOINT_IPP percona-xtradb-cluster-galera/asio/asio/posix/basic_descriptor.hpp0000644000000000000000000002153712247075736025742 0ustar rootroot00000000000000// // posix/basic_descriptor.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_POSIX_BASIC_DESCRIPTOR_HPP #define ASIO_POSIX_BASIC_DESCRIPTOR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) \ || defined(GENERATING_DOCUMENTATION) #include "asio/basic_io_object.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/posix/descriptor_base.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace posix { /// Provides POSIX descriptor functionality. /** * The posix::basic_descriptor class template provides the ability to wrap a * POSIX descriptor. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. */ template class basic_descriptor : public basic_io_object, public descriptor_base { public: /// The native representation of a descriptor. typedef typename DescriptorService::native_type native_type; /// A basic_descriptor is always the lowest layer. typedef basic_descriptor lowest_layer_type; /// Construct a basic_descriptor without opening it. /** * This constructor creates a descriptor without opening it. * * @param io_service The io_service object that the descriptor will use to * dispatch handlers for any asynchronous operations performed on the * descriptor. */ explicit basic_descriptor(asio::io_service& io_service) : basic_io_object(io_service) { } /// Construct a basic_descriptor on an existing native descriptor. /** * This constructor creates a descriptor object to hold an existing native * descriptor. * * @param io_service The io_service object that the descriptor will use to * dispatch handlers for any asynchronous operations performed on the * descriptor. * * @param native_descriptor A native descriptor. * * @throws asio::system_error Thrown on failure. */ basic_descriptor(asio::io_service& io_service, const native_type& native_descriptor) : basic_io_object(io_service) { asio::error_code ec; this->service.assign(this->implementation, native_descriptor, ec); asio::detail::throw_error(ec); } /// Get a reference to the lowest layer. /** * This function returns a reference to the lowest layer in a stack of * layers. Since a basic_descriptor cannot contain any further layers, it * simply returns a reference to itself. * * @return A reference to the lowest layer in the stack of layers. Ownership * is not transferred to the caller. */ lowest_layer_type& lowest_layer() { return *this; } /// Get a const reference to the lowest layer. /** * This function returns a const reference to the lowest layer in a stack of * layers. Since a basic_descriptor cannot contain any further layers, it * simply returns a reference to itself. * * @return A const reference to the lowest layer in the stack of layers. * Ownership is not transferred to the caller. */ const lowest_layer_type& lowest_layer() const { return *this; } /// Assign an existing native descriptor to the descriptor. /* * This function opens the descriptor to hold an existing native descriptor. * * @param native_descriptor A native descriptor. * * @throws asio::system_error Thrown on failure. */ void assign(const native_type& native_descriptor) { asio::error_code ec; this->service.assign(this->implementation, native_descriptor, ec); asio::detail::throw_error(ec); } /// Assign an existing native descriptor to the descriptor. /* * This function opens the descriptor to hold an existing native descriptor. * * @param native_descriptor A native descriptor. * * @param ec Set to indicate what error occurred, if any. */ asio::error_code assign(const native_type& native_descriptor, asio::error_code& ec) { return this->service.assign(this->implementation, native_descriptor, ec); } /// Determine whether the descriptor is open. bool is_open() const { return this->service.is_open(this->implementation); } /// Close the descriptor. /** * This function is used to close the descriptor. Any asynchronous read or * write operations will be cancelled immediately, and will complete with the * asio::error::operation_aborted error. * * @throws asio::system_error Thrown on failure. */ void close() { asio::error_code ec; this->service.close(this->implementation, ec); asio::detail::throw_error(ec); } /// Close the descriptor. /** * This function is used to close the descriptor. Any asynchronous read or * write operations will be cancelled immediately, and will complete with the * asio::error::operation_aborted error. * * @param ec Set to indicate what error occurred, if any. */ asio::error_code close(asio::error_code& ec) { return this->service.close(this->implementation, ec); } /// Get the native descriptor representation. /** * This function may be used to obtain the underlying representation of the * descriptor. This is intended to allow access to native descriptor * functionality that is not otherwise provided. */ native_type native() { return this->service.native(this->implementation); } /// Cancel all asynchronous operations associated with the descriptor. /** * This function causes all outstanding asynchronous read or write operations * to finish immediately, and the handlers for cancelled operations will be * passed the asio::error::operation_aborted error. * * @throws asio::system_error Thrown on failure. */ void cancel() { asio::error_code ec; this->service.cancel(this->implementation, ec); asio::detail::throw_error(ec); } /// Cancel all asynchronous operations associated with the descriptor. /** * This function causes all outstanding asynchronous read or write operations * to finish immediately, and the handlers for cancelled operations will be * passed the asio::error::operation_aborted error. * * @param ec Set to indicate what error occurred, if any. */ asio::error_code cancel(asio::error_code& ec) { return this->service.cancel(this->implementation, ec); } /// Perform an IO control command on the descriptor. /** * This function is used to execute an IO control command on the descriptor. * * @param command The IO control command to be performed on the descriptor. * * @throws asio::system_error Thrown on failure. * * @sa IoControlCommand @n * asio::posix::descriptor_base::bytes_readable @n * asio::posix::descriptor_base::non_blocking_io * * @par Example * Getting the number of bytes ready to read: * @code * asio::posix::stream_descriptor descriptor(io_service); * ... * asio::posix::stream_descriptor::bytes_readable command; * descriptor.io_control(command); * std::size_t bytes_readable = command.get(); * @endcode */ template void io_control(IoControlCommand& command) { asio::error_code ec; this->service.io_control(this->implementation, command, ec); asio::detail::throw_error(ec); } /// Perform an IO control command on the descriptor. /** * This function is used to execute an IO control command on the descriptor. * * @param command The IO control command to be performed on the descriptor. * * @param ec Set to indicate what error occurred, if any. * * @sa IoControlCommand @n * asio::posix::descriptor_base::bytes_readable @n * asio::posix::descriptor_base::non_blocking_io * * @par Example * Getting the number of bytes ready to read: * @code * asio::posix::stream_descriptor descriptor(io_service); * ... * asio::posix::stream_descriptor::bytes_readable command; * asio::error_code ec; * descriptor.io_control(command, ec); * if (ec) * { * // An error occurred. * } * std::size_t bytes_readable = command.get(); * @endcode */ template asio::error_code io_control(IoControlCommand& command, asio::error_code& ec) { return this->service.io_control(this->implementation, command, ec); } protected: /// Protected destructor to prevent deletion through this type. ~basic_descriptor() { } }; } // namespace posix } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) // || defined(GENERATING_DOCUMENTATION) #endif // ASIO_POSIX_BASIC_DESCRIPTOR_HPP percona-xtradb-cluster-galera/asio/asio/posix/basic_stream_descriptor.hpp0000644000000000000000000002646512247075736027322 0ustar rootroot00000000000000// // posix/basic_stream_descriptor.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_POSIX_BASIC_STREAM_DESCRIPTOR_HPP #define ASIO_POSIX_BASIC_STREAM_DESCRIPTOR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) \ || defined(GENERATING_DOCUMENTATION) #include #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/posix/basic_descriptor.hpp" #include "asio/posix/stream_descriptor_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace posix { /// Provides stream-oriented descriptor functionality. /** * The posix::basic_stream_descriptor class template provides asynchronous and * blocking stream-oriented descriptor functionality. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. * * @par Concepts: * AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. */ template class basic_stream_descriptor : public basic_descriptor { public: /// The native representation of a descriptor. typedef typename StreamDescriptorService::native_type native_type; /// Construct a basic_stream_descriptor without opening it. /** * This constructor creates a stream descriptor without opening it. The * descriptor needs to be opened and then connected or accepted before data * can be sent or received on it. * * @param io_service The io_service object that the stream descriptor will * use to dispatch handlers for any asynchronous operations performed on the * descriptor. */ explicit basic_stream_descriptor(asio::io_service& io_service) : basic_descriptor(io_service) { } /// Construct a basic_stream_descriptor on an existing native descriptor. /** * This constructor creates a stream descriptor object to hold an existing * native descriptor. * * @param io_service The io_service object that the stream descriptor will * use to dispatch handlers for any asynchronous operations performed on the * descriptor. * * @param native_descriptor The new underlying descriptor implementation. * * @throws asio::system_error Thrown on failure. */ basic_stream_descriptor(asio::io_service& io_service, const native_type& native_descriptor) : basic_descriptor(io_service, native_descriptor) { } /// Write some data to the descriptor. /** * This function is used to write data to the stream descriptor. The function * call will block until one or more bytes of the data has been written * successfully, or until an error occurs. * * @param buffers One or more data buffers to be written to the descriptor. * * @returns The number of bytes written. * * @throws asio::system_error Thrown on failure. An error code of * asio::error::eof indicates that the connection was closed by the * peer. * * @note The write_some operation may not transmit all of the data to the * peer. Consider using the @ref write function if you need to ensure that * all data is written before the blocking operation completes. * * @par Example * To write a single data buffer use the @ref buffer function as follows: * @code * descriptor.write_some(asio::buffer(data, size)); * @endcode * See the @ref buffer documentation for information on writing multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template std::size_t write_some(const ConstBufferSequence& buffers) { asio::error_code ec; std::size_t s = this->service.write_some(this->implementation, buffers, ec); asio::detail::throw_error(ec); return s; } /// Write some data to the descriptor. /** * This function is used to write data to the stream descriptor. The function * call will block until one or more bytes of the data has been written * successfully, or until an error occurs. * * @param buffers One or more data buffers to be written to the descriptor. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes written. Returns 0 if an error occurred. * * @note The write_some operation may not transmit all of the data to the * peer. Consider using the @ref write function if you need to ensure that * all data is written before the blocking operation completes. */ template std::size_t write_some(const ConstBufferSequence& buffers, asio::error_code& ec) { return this->service.write_some(this->implementation, buffers, ec); } /// Start an asynchronous write. /** * This function is used to asynchronously write data to the stream * descriptor. The function call always returns immediately. * * @param buffers One or more data buffers to be written to the descriptor. * Although the buffers object may be copied as necessary, ownership of the * underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param handler The handler to be called when the write operation completes. * Copies will be made of the handler as required. The function signature of * the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes written. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note The write operation may not transmit all of the data to the peer. * Consider using the @ref async_write function if you need to ensure that all * data is written before the asynchronous operation completes. * * @par Example * To write a single data buffer use the @ref buffer function as follows: * @code * descriptor.async_write_some(asio::buffer(data, size), handler); * @endcode * See the @ref buffer documentation for information on writing multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template void async_write_some(const ConstBufferSequence& buffers, WriteHandler handler) { this->service.async_write_some(this->implementation, buffers, handler); } /// Read some data from the descriptor. /** * This function is used to read data from the stream descriptor. The function * call will block until one or more bytes of data has been read successfully, * or until an error occurs. * * @param buffers One or more buffers into which the data will be read. * * @returns The number of bytes read. * * @throws asio::system_error Thrown on failure. An error code of * asio::error::eof indicates that the connection was closed by the * peer. * * @note The read_some operation may not read all of the requested number of * bytes. Consider using the @ref read function if you need to ensure that * the requested amount of data is read before the blocking operation * completes. * * @par Example * To read into a single data buffer use the @ref buffer function as follows: * @code * descriptor.read_some(asio::buffer(data, size)); * @endcode * See the @ref buffer documentation for information on reading into multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template std::size_t read_some(const MutableBufferSequence& buffers) { asio::error_code ec; std::size_t s = this->service.read_some(this->implementation, buffers, ec); asio::detail::throw_error(ec); return s; } /// Read some data from the descriptor. /** * This function is used to read data from the stream descriptor. The function * call will block until one or more bytes of data has been read successfully, * or until an error occurs. * * @param buffers One or more buffers into which the data will be read. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes read. Returns 0 if an error occurred. * * @note The read_some operation may not read all of the requested number of * bytes. Consider using the @ref read function if you need to ensure that * the requested amount of data is read before the blocking operation * completes. */ template std::size_t read_some(const MutableBufferSequence& buffers, asio::error_code& ec) { return this->service.read_some(this->implementation, buffers, ec); } /// Start an asynchronous read. /** * This function is used to asynchronously read data from the stream * descriptor. The function call always returns immediately. * * @param buffers One or more buffers into which the data will be read. * Although the buffers object may be copied as necessary, ownership of the * underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param handler The handler to be called when the read operation completes. * Copies will be made of the handler as required. The function signature of * the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes read. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note The read operation may not read all of the requested number of bytes. * Consider using the @ref async_read function if you need to ensure that the * requested amount of data is read before the asynchronous operation * completes. * * @par Example * To read into a single data buffer use the @ref buffer function as follows: * @code * descriptor.async_read_some(asio::buffer(data, size), handler); * @endcode * See the @ref buffer documentation for information on reading into multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template void async_read_some(const MutableBufferSequence& buffers, ReadHandler handler) { this->service.async_read_some(this->implementation, buffers, handler); } }; } // namespace posix } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) // || defined(GENERATING_DOCUMENTATION) #endif // ASIO_POSIX_BASIC_STREAM_DESCRIPTOR_HPP percona-xtradb-cluster-galera/asio/asio/posix/descriptor_base.hpp0000644000000000000000000000471712247075736025574 0ustar rootroot00000000000000// // posix/descriptor_base.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_POSIX_DESCRIPTOR_BASE_HPP #define ASIO_POSIX_DESCRIPTOR_BASE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) \ || defined(GENERATING_DOCUMENTATION) #include "asio/detail/io_control.hpp" #include "asio/detail/socket_option.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace posix { /// The descriptor_base class is used as a base for the basic_stream_descriptor /// class template so that we have a common place to define the associated /// IO control commands. class descriptor_base { public: /// IO control command to set the blocking mode of the descriptor. /** * Implements the FIONBIO IO control command. * * @par Example * @code * asio::posix::stream_descriptor descriptor(io_service); * ... * asio::descriptor_base::non_blocking_io command(true); * descriptor.io_control(command); * @endcode * * @par Concepts: * IoControlCommand. */ #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined non_blocking_io; #else typedef asio::detail::io_control::non_blocking_io non_blocking_io; #endif /// IO control command to get the amount of data that can be read without /// blocking. /** * Implements the FIONREAD IO control command. * * @par Example * @code * asio::posix::stream_descriptor descriptor(io_service); * ... * asio::descriptor_base::bytes_readable command(true); * descriptor.io_control(command); * std::size_t bytes_readable = command.get(); * @endcode * * @par Concepts: * IoControlCommand. */ #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined bytes_readable; #else typedef asio::detail::io_control::bytes_readable bytes_readable; #endif protected: /// Protected destructor to prevent deletion through this type. ~descriptor_base() { } }; } // namespace posix } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) // || defined(GENERATING_DOCUMENTATION) #endif // ASIO_POSIX_DESCRIPTOR_BASE_HPP percona-xtradb-cluster-galera/asio/asio/posix/stream_descriptor.hpp0000644000000000000000000000177112247075736026152 0ustar rootroot00000000000000// // posix/stream_descriptor.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_POSIX_STREAM_DESCRIPTOR_HPP #define ASIO_POSIX_STREAM_DESCRIPTOR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) \ || defined(GENERATING_DOCUMENTATION) #include "asio/posix/basic_stream_descriptor.hpp" namespace asio { namespace posix { /// Typedef for the typical usage of a stream-oriented descriptor. typedef basic_stream_descriptor<> stream_descriptor; } // namespace posix } // namespace asio #endif // defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) // || defined(GENERATING_DOCUMENTATION) #endif // ASIO_POSIX_STREAM_DESCRIPTOR_HPP percona-xtradb-cluster-galera/asio/asio/posix/stream_descriptor_service.hpp0000644000000000000000000001177512247075736027677 0ustar rootroot00000000000000// // posix/stream_descriptor_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_POSIX_STREAM_DESCRIPTOR_SERVICE_HPP #define ASIO_POSIX_STREAM_DESCRIPTOR_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) \ || defined(GENERATING_DOCUMENTATION) #include #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/detail/reactive_descriptor_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace posix { /// Default service implementation for a stream descriptor. class stream_descriptor_service #if defined(GENERATING_DOCUMENTATION) : public asio::io_service::service #else : public asio::detail::service_base #endif { public: #if defined(GENERATING_DOCUMENTATION) /// The unique service identifier. static asio::io_service::id id; #endif private: // The type of the platform-specific implementation. typedef detail::reactive_descriptor_service service_impl_type; public: /// The type of a stream descriptor implementation. #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined implementation_type; #else typedef service_impl_type::implementation_type implementation_type; #endif /// The native descriptor type. #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined native_type; #else typedef service_impl_type::native_type native_type; #endif /// Construct a new stream descriptor service for the specified io_service. explicit stream_descriptor_service(asio::io_service& io_service) : asio::detail::service_base(io_service), service_impl_(io_service) { } /// Destroy all user-defined descriptorr objects owned by the service. void shutdown_service() { service_impl_.shutdown_service(); } /// Construct a new stream descriptor implementation. void construct(implementation_type& impl) { service_impl_.construct(impl); } /// Destroy a stream descriptor implementation. void destroy(implementation_type& impl) { service_impl_.destroy(impl); } /// Assign an existing native descriptor to a stream descriptor. asio::error_code assign(implementation_type& impl, const native_type& native_descriptor, asio::error_code& ec) { return service_impl_.assign(impl, native_descriptor, ec); } /// Determine whether the descriptor is open. bool is_open(const implementation_type& impl) const { return service_impl_.is_open(impl); } /// Close a stream descriptor implementation. asio::error_code close(implementation_type& impl, asio::error_code& ec) { return service_impl_.close(impl, ec); } /// Get the native descriptor implementation. native_type native(implementation_type& impl) { return service_impl_.native(impl); } /// Cancel all asynchronous operations associated with the descriptor. asio::error_code cancel(implementation_type& impl, asio::error_code& ec) { return service_impl_.cancel(impl, ec); } /// Perform an IO control command on the descriptor. template asio::error_code io_control(implementation_type& impl, IoControlCommand& command, asio::error_code& ec) { return service_impl_.io_control(impl, command, ec); } /// Write the given data to the stream. template std::size_t write_some(implementation_type& impl, const ConstBufferSequence& buffers, asio::error_code& ec) { return service_impl_.write_some(impl, buffers, ec); } /// Start an asynchronous write. template void async_write_some(implementation_type& impl, const ConstBufferSequence& buffers, WriteHandler descriptorr) { service_impl_.async_write_some(impl, buffers, descriptorr); } /// Read some data from the stream. template std::size_t read_some(implementation_type& impl, const MutableBufferSequence& buffers, asio::error_code& ec) { return service_impl_.read_some(impl, buffers, ec); } /// Start an asynchronous read. template void async_read_some(implementation_type& impl, const MutableBufferSequence& buffers, ReadHandler descriptorr) { service_impl_.async_read_some(impl, buffers, descriptorr); } private: // The platform-specific implementation. service_impl_type service_impl_; }; } // namespace posix } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) // || defined(GENERATING_DOCUMENTATION) #endif // ASIO_POSIX_STREAM_DESCRIPTOR_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/ssl/basic_context.hpp0000755000000000000000000003227112247075736024707 0ustar rootroot00000000000000// // ssl/basic_context.hpp // ~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com // Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_SSL_BASIC_CONTEXT_HPP #define ASIO_SSL_BASIC_CONTEXT_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/ssl/context_base.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ssl { /// SSL context. template class basic_context : public context_base, private boost::noncopyable { public: /// The type of the service that will be used to provide context operations. typedef Service service_type; /// The native implementation type of the locking dispatcher. typedef typename service_type::impl_type impl_type; /// Constructor. basic_context(asio::io_service& io_service, method m) : service_(asio::use_service(io_service)), impl_(service_.null()) { service_.create(impl_, m); } /// Destructor. ~basic_context() { service_.destroy(impl_); } /// Get the underlying implementation in the native type. /** * This function may be used to obtain the underlying implementation of the * context. This is intended to allow access to context functionality that is * not otherwise provided. */ impl_type impl() { return impl_; } /// Set options on the context. /** * This function may be used to configure the SSL options used by the context. * * @param o A bitmask of options. The available option values are defined in * the context_base class. The options are bitwise-ored with any existing * value for the options. * * @throws asio::system_error Thrown on failure. */ void set_options(options o) { asio::error_code ec; service_.set_options(impl_, o, ec); asio::detail::throw_error(ec); } /// Set options on the context. /** * This function may be used to configure the SSL options used by the context. * * @param o A bitmask of options. The available option values are defined in * the context_base class. The options are bitwise-ored with any existing * value for the options. * * @param ec Set to indicate what error occurred, if any. */ asio::error_code set_options(options o, asio::error_code& ec) { return service_.set_options(impl_, o, ec); } /// Set the peer verification mode. /** * This function may be used to configure the peer verification mode used by * the context. * * @param v A bitmask of peer verification modes. The available verify_mode * values are defined in the context_base class. * * @throws asio::system_error Thrown on failure. */ void set_verify_mode(verify_mode v) { asio::error_code ec; service_.set_verify_mode(impl_, v, ec); asio::detail::throw_error(ec); } /// Set the peer verification mode. /** * This function may be used to configure the peer verification mode used by * the context. * * @param v A bitmask of peer verification modes. The available verify_mode * values are defined in the context_base class. * * @param ec Set to indicate what error occurred, if any. */ asio::error_code set_verify_mode(verify_mode v, asio::error_code& ec) { return service_.set_verify_mode(impl_, v, ec); } /// Load a certification authority file for performing verification. /** * This function is used to load one or more trusted certification authorities * from a file. * * @param filename The name of a file containing certification authority * certificates in PEM format. * * @throws asio::system_error Thrown on failure. */ void load_verify_file(const std::string& filename) { asio::error_code ec; service_.load_verify_file(impl_, filename, ec); asio::detail::throw_error(ec); } /// Load a certification authority file for performing verification. /** * This function is used to load the certificates for one or more trusted * certification authorities from a file. * * @param filename The name of a file containing certification authority * certificates in PEM format. * * @param ec Set to indicate what error occurred, if any. */ asio::error_code load_verify_file(const std::string& filename, asio::error_code& ec) { return service_.load_verify_file(impl_, filename, ec); } /// Add a directory containing certificate authority files to be used for /// performing verification. /** * This function is used to specify the name of a directory containing * certification authority certificates. Each file in the directory must * contain a single certificate. The files must be named using the subject * name's hash and an extension of ".0". * * @param path The name of a directory containing the certificates. * * @throws asio::system_error Thrown on failure. */ void add_verify_path(const std::string& path) { asio::error_code ec; service_.add_verify_path(impl_, path, ec); asio::detail::throw_error(ec); } /// Add a directory containing certificate authority files to be used for /// performing verification. /** * This function is used to specify the name of a directory containing * certification authority certificates. Each file in the directory must * contain a single certificate. The files must be named using the subject * name's hash and an extension of ".0". * * @param path The name of a directory containing the certificates. * * @param ec Set to indicate what error occurred, if any. */ asio::error_code add_verify_path(const std::string& path, asio::error_code& ec) { return service_.add_verify_path(impl_, path, ec); } /// Use a certificate from a file. /** * This function is used to load a certificate into the context from a file. * * @param filename The name of the file containing the certificate. * * @param format The file format (ASN.1 or PEM). * * @throws asio::system_error Thrown on failure. */ void use_certificate_file(const std::string& filename, file_format format) { asio::error_code ec; service_.use_certificate_file(impl_, filename, format, ec); asio::detail::throw_error(ec); } /// Use a certificate from a file. /** * This function is used to load a certificate into the context from a file. * * @param filename The name of the file containing the certificate. * * @param format The file format (ASN.1 or PEM). * * @param ec Set to indicate what error occurred, if any. */ asio::error_code use_certificate_file(const std::string& filename, file_format format, asio::error_code& ec) { return service_.use_certificate_file(impl_, filename, format, ec); } /// Use a certificate chain from a file. /** * This function is used to load a certificate chain into the context from a * file. * * @param filename The name of the file containing the certificate. The file * must use the PEM format. * * @throws asio::system_error Thrown on failure. */ void use_certificate_chain_file(const std::string& filename) { asio::error_code ec; service_.use_certificate_chain_file(impl_, filename, ec); asio::detail::throw_error(ec); } /// Use a certificate chain from a file. /** * This function is used to load a certificate chain into the context from a * file. * * @param filename The name of the file containing the certificate. The file * must use the PEM format. * * @param ec Set to indicate what error occurred, if any. */ asio::error_code use_certificate_chain_file( const std::string& filename, asio::error_code& ec) { return service_.use_certificate_chain_file(impl_, filename, ec); } /// Use a private key from a file. /** * This function is used to load a private key into the context from a file. * * @param filename The name of the file containing the private key. * * @param format The file format (ASN.1 or PEM). * * @throws asio::system_error Thrown on failure. */ void use_private_key_file(const std::string& filename, file_format format) { asio::error_code ec; service_.use_private_key_file(impl_, filename, format, ec); asio::detail::throw_error(ec); } /// Use a private key from a file. /** * This function is used to load a private key into the context from a file. * * @param filename The name of the file containing the private key. * * @param format The file format (ASN.1 or PEM). * * @param ec Set to indicate what error occurred, if any. */ asio::error_code use_private_key_file(const std::string& filename, file_format format, asio::error_code& ec) { return service_.use_private_key_file(impl_, filename, format, ec); } /// Use an RSA private key from a file. /** * This function is used to load an RSA private key into the context from a * file. * * @param filename The name of the file containing the RSA private key. * * @param format The file format (ASN.1 or PEM). * * @throws asio::system_error Thrown on failure. */ void use_rsa_private_key_file(const std::string& filename, file_format format) { asio::error_code ec; service_.use_rsa_private_key_file(impl_, filename, format, ec); asio::detail::throw_error(ec); } /// Use an RSA private key from a file. /** * This function is used to load an RSA private key into the context from a * file. * * @param filename The name of the file containing the RSA private key. * * @param format The file format (ASN.1 or PEM). * * @param ec Set to indicate what error occurred, if any. */ asio::error_code use_rsa_private_key_file( const std::string& filename, file_format format, asio::error_code& ec) { return service_.use_rsa_private_key_file(impl_, filename, format, ec); } /// Use the specified file to obtain the temporary Diffie-Hellman parameters. /** * This function is used to load Diffie-Hellman parameters into the context * from a file. * * @param filename The name of the file containing the Diffie-Hellman * parameters. The file must use the PEM format. * * @throws asio::system_error Thrown on failure. */ void use_tmp_dh_file(const std::string& filename) { asio::error_code ec; service_.use_tmp_dh_file(impl_, filename, ec); asio::detail::throw_error(ec); } /// Use the specified file to obtain the temporary Diffie-Hellman parameters. /** * This function is used to load Diffie-Hellman parameters into the context * from a file. * * @param filename The name of the file containing the Diffie-Hellman * parameters. The file must use the PEM format. * * @param ec Set to indicate what error occurred, if any. */ asio::error_code use_tmp_dh_file(const std::string& filename, asio::error_code& ec) { return service_.use_tmp_dh_file(impl_, filename, ec); } /// Set the password callback. /** * This function is used to specify a callback function to obtain password * information about an encrypted key in PEM format. * * @param callback The function object to be used for obtaining the password. * The function signature of the handler must be: * @code std::string password_callback( * std::size_t max_length, // The maximum size for a password. * password_purpose purpose // Whether password is for reading or writing. * ); @endcode * The return value of the callback is a string containing the password. * * @throws asio::system_error Thrown on failure. */ template void set_password_callback(PasswordCallback callback) { asio::error_code ec; service_.set_password_callback(impl_, callback, ec); asio::detail::throw_error(ec); } /// Set the password callback. /** * This function is used to specify a callback function to obtain password * information about an encrypted key in PEM format. * * @param callback The function object to be used for obtaining the password. * The function signature of the handler must be: * @code std::string password_callback( * std::size_t max_length, // The maximum size for a password. * password_purpose purpose // Whether password is for reading or writing. * ); @endcode * The return value of the callback is a string containing the password. * * @param ec Set to indicate what error occurred, if any. */ template asio::error_code set_password_callback(PasswordCallback callback, asio::error_code& ec) { return service_.set_password_callback(impl_, callback, ec); } private: /// The backend service implementation. service_type& service_; /// The underlying native implementation. impl_type impl_; }; } // namespace ssl } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_SSL_BASIC_CONTEXT_HPP percona-xtradb-cluster-galera/asio/asio/ssl/context.hpp0000644000000000000000000000150312247075736023535 0ustar rootroot00000000000000// // ssl/context.hpp // ~~~~~~~~~~~~~~~ // // Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com // Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_SSL_CONTEXT_HPP #define ASIO_SSL_CONTEXT_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include "asio/ssl/basic_context.hpp" #include "asio/ssl/context_service.hpp" namespace asio { namespace ssl { /// Typedef for the typical usage of context. typedef basic_context context; } // namespace ssl } // namespace asio #endif // ASIO_SSL_CONTEXT_HPP percona-xtradb-cluster-galera/asio/asio/ssl/context_base.hpp0000755000000000000000000000756312247075736024546 0ustar rootroot00000000000000// // ssl/context_base.hpp // ~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_SSL_CONTEXT_BASE_HPP #define ASIO_SSL_CONTEXT_BASE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/ssl/detail/openssl_types.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ssl { /// The context_base class is used as a base for the basic_context class /// template so that we have a common place to define various enums. class context_base { public: /// Different methods supported by a context. enum method { /// Generic SSL version 2. sslv2, /// SSL version 2 client. sslv2_client, /// SSL version 2 server. sslv2_server, /// Generic SSL version 3. sslv3, /// SSL version 3 client. sslv3_client, /// SSL version 3 server. sslv3_server, /// Generic TLS version 1. tlsv1, /// TLS version 1 client. tlsv1_client, /// TLS version 1 server. tlsv1_server, /// Generic SSL/TLS. sslv23, /// SSL/TLS client. sslv23_client, /// SSL/TLS server. sslv23_server }; /// Bitmask type for SSL options. typedef int options; #if defined(GENERATING_DOCUMENTATION) /// Implement various bug workarounds. static const int default_workarounds = implementation_defined; /// Always create a new key when using tmp_dh parameters. static const int single_dh_use = implementation_defined; /// Disable SSL v2. static const int no_sslv2 = implementation_defined; /// Disable SSL v3. static const int no_sslv3 = implementation_defined; /// Disable TLS v1. static const int no_tlsv1 = implementation_defined; #else BOOST_STATIC_CONSTANT(long, default_workarounds = SSL_OP_ALL); BOOST_STATIC_CONSTANT(long, single_dh_use = SSL_OP_SINGLE_DH_USE); BOOST_STATIC_CONSTANT(long, no_sslv2 = SSL_OP_NO_SSLv2); BOOST_STATIC_CONSTANT(long, no_sslv3 = SSL_OP_NO_SSLv3); BOOST_STATIC_CONSTANT(long, no_tlsv1 = SSL_OP_NO_TLSv1); #endif /// File format types. enum file_format { /// ASN.1 file. asn1, /// PEM file. pem }; /// Bitmask type for peer verification. typedef int verify_mode; #if defined(GENERATING_DOCUMENTATION) /// No verification. static const int verify_none = implementation_defined; /// Verify the peer. static const int verify_peer = implementation_defined; /// Fail verification if the peer has no certificate. Ignored unless /// verify_peer is set. static const int verify_fail_if_no_peer_cert = implementation_defined; /// Do not request client certificate on renegotiation. Ignored unless /// verify_peer is set. static const int verify_client_once = implementation_defined; #else BOOST_STATIC_CONSTANT(int, verify_none = SSL_VERIFY_NONE); BOOST_STATIC_CONSTANT(int, verify_peer = SSL_VERIFY_PEER); BOOST_STATIC_CONSTANT(int, verify_fail_if_no_peer_cert = SSL_VERIFY_FAIL_IF_NO_PEER_CERT); BOOST_STATIC_CONSTANT(int, verify_client_once = SSL_VERIFY_CLIENT_ONCE); #endif /// Purpose of PEM password. enum password_purpose { /// The password is needed for reading/decryption. for_reading, /// The password is needed for writing/encryption. for_writing }; protected: /// Protected destructor to prevent deletion through this type. ~context_base() { } #if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) private: // Workaround to enable the empty base optimisation with Borland C++. char dummy_; #endif }; } // namespace ssl } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_SSL_CONTEXT_BASE_HPP percona-xtradb-cluster-galera/asio/asio/ssl/context_service.hpp0000755000000000000000000001156012247075736025264 0ustar rootroot00000000000000// // ssl/context_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com // Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_SSL_CONTEXT_SERVICE_HPP #define ASIO_SSL_CONTEXT_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/ssl/context_base.hpp" #include "asio/ssl/detail/openssl_context_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ssl { /// Default service implementation for a context. class context_service #if defined(GENERATING_DOCUMENTATION) : public asio::io_service::service #else : public asio::detail::service_base #endif { private: // The type of the platform-specific implementation. typedef detail::openssl_context_service service_impl_type; public: #if defined(GENERATING_DOCUMENTATION) /// The unique service identifier. static asio::io_service::id id; #endif /// The type of the context. #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined impl_type; #else typedef service_impl_type::impl_type impl_type; #endif /// Constructor. explicit context_service(asio::io_service& io_service) : asio::detail::service_base(io_service), service_impl_(asio::use_service(io_service)) { } /// Destroy all user-defined handler objects owned by the service. void shutdown_service() { } /// Return a null context implementation. impl_type null() const { return service_impl_.null(); } /// Create a new context implementation. void create(impl_type& impl, context_base::method m) { service_impl_.create(impl, m); } /// Destroy a context implementation. void destroy(impl_type& impl) { service_impl_.destroy(impl); } /// Set options on the context. asio::error_code set_options(impl_type& impl, context_base::options o, asio::error_code& ec) { return service_impl_.set_options(impl, o, ec); } /// Set peer verification mode. asio::error_code set_verify_mode(impl_type& impl, context_base::verify_mode v, asio::error_code& ec) { return service_impl_.set_verify_mode(impl, v, ec); } /// Load a certification authority file for performing verification. asio::error_code load_verify_file(impl_type& impl, const std::string& filename, asio::error_code& ec) { return service_impl_.load_verify_file(impl, filename, ec); } /// Add a directory containing certification authority files to be used for /// performing verification. asio::error_code add_verify_path(impl_type& impl, const std::string& path, asio::error_code& ec) { return service_impl_.add_verify_path(impl, path, ec); } /// Use a certificate from a file. asio::error_code use_certificate_file(impl_type& impl, const std::string& filename, context_base::file_format format, asio::error_code& ec) { return service_impl_.use_certificate_file(impl, filename, format, ec); } /// Use a certificate chain from a file. asio::error_code use_certificate_chain_file(impl_type& impl, const std::string& filename, asio::error_code& ec) { return service_impl_.use_certificate_chain_file(impl, filename, ec); } /// Use a private key from a file. asio::error_code use_private_key_file(impl_type& impl, const std::string& filename, context_base::file_format format, asio::error_code& ec) { return service_impl_.use_private_key_file(impl, filename, format, ec); } /// Use an RSA private key from a file. asio::error_code use_rsa_private_key_file(impl_type& impl, const std::string& filename, context_base::file_format format, asio::error_code& ec) { return service_impl_.use_rsa_private_key_file(impl, filename, format, ec); } /// Use the specified file to obtain the temporary Diffie-Hellman parameters. asio::error_code use_tmp_dh_file(impl_type& impl, const std::string& filename, asio::error_code& ec) { return service_impl_.use_tmp_dh_file(impl, filename, ec); } /// Set the password callback. template asio::error_code set_password_callback(impl_type& impl, PasswordCallback callback, asio::error_code& ec) { return service_impl_.set_password_callback(impl, callback, ec); } private: // The service that provides the platform-specific implementation. service_impl_type& service_impl_; }; } // namespace ssl } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_SSL_CONTEXT_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/ssl/detail/0000755000000000000000000000000012247075736022603 5ustar rootroot00000000000000percona-xtradb-cluster-galera/asio/asio/ssl/stream.hpp0000644000000000000000000004142112247075736023347 0ustar rootroot00000000000000// // ssl/stream.hpp // ~~~~~~~~~~~~~~ // // Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com // Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_SSL_STREAM_HPP #define ASIO_SSL_STREAM_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/ssl/basic_context.hpp" #include "asio/ssl/stream_base.hpp" #include "asio/ssl/stream_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ssl { /// Provides stream-oriented functionality using SSL. /** * The stream class template provides asynchronous and blocking stream-oriented * functionality using SSL. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. * * @par Example * To use the SSL stream template with an ip::tcp::socket, you would write: * @code * asio::io_service io_service; * asio::ssl::context context(io_service, asio::ssl::context::sslv23); * asio::ssl::stream sock(io_service, context); * @endcode * * @par Concepts: * AsyncReadStream, AsyncWriteStream, Stream, SyncRead_Stream, SyncWriteStream. */ template class stream : public stream_base, private boost::noncopyable { public: /// The type of the next layer. typedef typename boost::remove_reference::type next_layer_type; /// The type of the lowest layer. typedef typename next_layer_type::lowest_layer_type lowest_layer_type; /// The type of the service that will be used to provide stream operations. typedef Service service_type; /// The native implementation type of the stream. typedef typename service_type::impl_type impl_type; /// Construct a stream. /** * This constructor creates a stream and initialises the underlying stream * object. * * @param arg The argument to be passed to initialise the underlying stream. * * @param context The SSL context to be used for the stream. */ template explicit stream(Arg& arg, basic_context& context) : next_layer_(arg), service_(asio::use_service(next_layer_.get_io_service())), impl_(service_.null()) { service_.create(impl_, next_layer_, context); } /// Destructor. ~stream() { service_.destroy(impl_, next_layer_); } /// (Deprecated: use get_io_service().) Get the io_service associated with /// the object. /** * This function may be used to obtain the io_service object that the stream * uses to dispatch handlers for asynchronous operations. * * @return A reference to the io_service object that stream will use to * dispatch handlers. Ownership is not transferred to the caller. */ asio::io_service& io_service() { return next_layer_.get_io_service(); } /// Get the io_service associated with the object. /** * This function may be used to obtain the io_service object that the stream * uses to dispatch handlers for asynchronous operations. * * @return A reference to the io_service object that stream will use to * dispatch handlers. Ownership is not transferred to the caller. */ asio::io_service& get_io_service() { return next_layer_.get_io_service(); } /// Get a reference to the next layer. /** * This function returns a reference to the next layer in a stack of stream * layers. * * @return A reference to the next layer in the stack of stream layers. * Ownership is not transferred to the caller. */ next_layer_type& next_layer() { return next_layer_; } /// Get a reference to the lowest layer. /** * This function returns a reference to the lowest layer in a stack of * stream layers. * * @return A reference to the lowest layer in the stack of stream layers. * Ownership is not transferred to the caller. */ lowest_layer_type& lowest_layer() { return next_layer_.lowest_layer(); } /// Get a const reference to the lowest layer. /** * This function returns a const reference to the lowest layer in a stack of * stream layers. * * @return A const reference to the lowest layer in the stack of stream * layers. Ownership is not transferred to the caller. */ const lowest_layer_type& lowest_layer() const { return next_layer_.lowest_layer(); } /// Get the underlying implementation in the native type. /** * This function may be used to obtain the underlying implementation of the * context. This is intended to allow access to stream functionality that is * not otherwise provided. */ impl_type impl() { return impl_; } /// Perform SSL handshaking. /** * This function is used to perform SSL handshaking on the stream. The * function call will block until handshaking is complete or an error occurs. * * @param type The type of handshaking to be performed, i.e. as a client or as * a server. * * @throws asio::system_error Thrown on failure. */ void handshake(handshake_type type) { asio::error_code ec; service_.handshake(impl_, next_layer_, type, ec); asio::detail::throw_error(ec); } /// Perform SSL handshaking. /** * This function is used to perform SSL handshaking on the stream. The * function call will block until handshaking is complete or an error occurs. * * @param type The type of handshaking to be performed, i.e. as a client or as * a server. * * @param ec Set to indicate what error occurred, if any. */ asio::error_code handshake(handshake_type type, asio::error_code& ec) { return service_.handshake(impl_, next_layer_, type, ec); } /// Start an asynchronous SSL handshake. /** * This function is used to asynchronously perform an SSL handshake on the * stream. This function call always returns immediately. * * @param type The type of handshaking to be performed, i.e. as a client or as * a server. * * @param handler The handler to be called when the handshake operation * completes. Copies will be made of the handler as required. The equivalent * function signature of the handler must be: * @code void handler( * const asio::error_code& error // Result of operation. * ); @endcode */ template void async_handshake(handshake_type type, HandshakeHandler handler) { service_.async_handshake(impl_, next_layer_, type, handler); } /// Shut down SSL on the stream. /** * This function is used to shut down SSL on the stream. The function call * will block until SSL has been shut down or an error occurs. * * @throws asio::system_error Thrown on failure. */ void shutdown() { asio::error_code ec; service_.shutdown(impl_, next_layer_, ec); asio::detail::throw_error(ec); } /// Shut down SSL on the stream. /** * This function is used to shut down SSL on the stream. The function call * will block until SSL has been shut down or an error occurs. * * @param ec Set to indicate what error occurred, if any. */ asio::error_code shutdown(asio::error_code& ec) { return service_.shutdown(impl_, next_layer_, ec); } /// Asynchronously shut down SSL on the stream. /** * This function is used to asynchronously shut down SSL on the stream. This * function call always returns immediately. * * @param handler The handler to be called when the handshake operation * completes. Copies will be made of the handler as required. The equivalent * function signature of the handler must be: * @code void handler( * const asio::error_code& error // Result of operation. * ); @endcode */ template void async_shutdown(ShutdownHandler handler) { service_.async_shutdown(impl_, next_layer_, handler); } /// Write some data to the stream. /** * This function is used to write data on the stream. The function call will * block until one or more bytes of data has been written successfully, or * until an error occurs. * * @param buffers The data to be written. * * @returns The number of bytes written. * * @throws asio::system_error Thrown on failure. * * @note The write_some operation may not transmit all of the data to the * peer. Consider using the @ref write function if you need to ensure that all * data is written before the blocking operation completes. */ template std::size_t write_some(const ConstBufferSequence& buffers) { asio::error_code ec; std::size_t s = service_.write_some(impl_, next_layer_, buffers, ec); asio::detail::throw_error(ec); return s; } /// Write some data to the stream. /** * This function is used to write data on the stream. The function call will * block until one or more bytes of data has been written successfully, or * until an error occurs. * * @param buffers The data to be written to the stream. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes written. Returns 0 if an error occurred. * * @note The write_some operation may not transmit all of the data to the * peer. Consider using the @ref write function if you need to ensure that all * data is written before the blocking operation completes. */ template std::size_t write_some(const ConstBufferSequence& buffers, asio::error_code& ec) { return service_.write_some(impl_, next_layer_, buffers, ec); } /// Start an asynchronous write. /** * This function is used to asynchronously write one or more bytes of data to * the stream. The function call always returns immediately. * * @param buffers The data to be written to the stream. Although the buffers * object may be copied as necessary, ownership of the underlying buffers is * retained by the caller, which must guarantee that they remain valid until * the handler is called. * * @param handler The handler to be called when the write operation completes. * Copies will be made of the handler as required. The equivalent function * signature of the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes written. * ); @endcode * * @note The async_write_some operation may not transmit all of the data to * the peer. Consider using the @ref async_write function if you need to * ensure that all data is written before the blocking operation completes. */ template void async_write_some(const ConstBufferSequence& buffers, WriteHandler handler) { service_.async_write_some(impl_, next_layer_, buffers, handler); } /// Read some data from the stream. /** * This function is used to read data from the stream. The function call will * block until one or more bytes of data has been read successfully, or until * an error occurs. * * @param buffers The buffers into which the data will be read. * * @returns The number of bytes read. * * @throws asio::system_error Thrown on failure. * * @note The read_some operation may not read all of the requested number of * bytes. Consider using the @ref read function if you need to ensure that the * requested amount of data is read before the blocking operation completes. */ template std::size_t read_some(const MutableBufferSequence& buffers) { asio::error_code ec; std::size_t s = service_.read_some(impl_, next_layer_, buffers, ec); asio::detail::throw_error(ec); return s; } /// Read some data from the stream. /** * This function is used to read data from the stream. The function call will * block until one or more bytes of data has been read successfully, or until * an error occurs. * * @param buffers The buffers into which the data will be read. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes read. Returns 0 if an error occurred. * * @note The read_some operation may not read all of the requested number of * bytes. Consider using the @ref read function if you need to ensure that the * requested amount of data is read before the blocking operation completes. */ template std::size_t read_some(const MutableBufferSequence& buffers, asio::error_code& ec) { return service_.read_some(impl_, next_layer_, buffers, ec); } /// Start an asynchronous read. /** * This function is used to asynchronously read one or more bytes of data from * the stream. The function call always returns immediately. * * @param buffers The buffers into which the data will be read. Although the * buffers object may be copied as necessary, ownership of the underlying * buffers is retained by the caller, which must guarantee that they remain * valid until the handler is called. * * @param handler The handler to be called when the read operation completes. * Copies will be made of the handler as required. The equivalent function * signature of the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes read. * ); @endcode * * @note The async_read_some operation may not read all of the requested * number of bytes. Consider using the @ref async_read function if you need to * ensure that the requested amount of data is read before the asynchronous * operation completes. */ template void async_read_some(const MutableBufferSequence& buffers, ReadHandler handler) { service_.async_read_some(impl_, next_layer_, buffers, handler); } /// Peek at the incoming data on the stream. /** * This function is used to peek at the incoming data on the stream, without * removing it from the input queue. The function call will block until data * has been read successfully or an error occurs. * * @param buffers The buffers into which the data will be read. * * @returns The number of bytes read. * * @throws asio::system_error Thrown on failure. */ template std::size_t peek(const MutableBufferSequence& buffers) { asio::error_code ec; std::size_t s = service_.peek(impl_, next_layer_, buffers, ec); asio::detail::throw_error(ec); return s; } /// Peek at the incoming data on the stream. /** * This function is used to peek at the incoming data on the stream, withoutxi * removing it from the input queue. The function call will block until data * has been read successfully or an error occurs. * * @param buffers The buffers into which the data will be read. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes read. Returns 0 if an error occurred. */ template std::size_t peek(const MutableBufferSequence& buffers, asio::error_code& ec) { return service_.peek(impl_, next_layer_, buffers, ec); } /// Determine the amount of data that may be read without blocking. /** * This function is used to determine the amount of data, in bytes, that may * be read from the stream without blocking. * * @returns The number of bytes of data that can be read without blocking. * * @throws asio::system_error Thrown on failure. */ std::size_t in_avail() { asio::error_code ec; std::size_t s = service_.in_avail(impl_, next_layer_, ec); asio::detail::throw_error(ec); return s; } /// Determine the amount of data that may be read without blocking. /** * This function is used to determine the amount of data, in bytes, that may * be read from the stream without blocking. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes of data that can be read without blocking. */ std::size_t in_avail(asio::error_code& ec) { return service_.in_avail(impl_, next_layer_, ec); } private: /// The next layer. Stream next_layer_; /// The backend service implementation. service_type& service_; /// The underlying native implementation. impl_type impl_; }; } // namespace ssl } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_SSL_STREAM_HPP percona-xtradb-cluster-galera/asio/asio/ssl/stream_base.hpp0000755000000000000000000000250012247075736024337 0ustar rootroot00000000000000// // ssl/stream_base.hpp // ~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_SSL_STREAM_BASE_HPP #define ASIO_SSL_STREAM_BASE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/detail/push_options.hpp" namespace asio { namespace ssl { /// The stream_base class is used as a base for the asio::ssl::stream /// class template so that we have a common place to define various enums. class stream_base { public: /// Different handshake types. enum handshake_type { /// Perform handshaking as a client. client, /// Perform handshaking as a server. server }; protected: /// Protected destructor to prevent deletion through this type. ~stream_base() { } #if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) private: // Workaround to enable the empty base optimisation with Borland C++. char dummy_; #endif }; } // namespace ssl } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_SSL_STREAM_BASE_HPP percona-xtradb-cluster-galera/asio/asio/ssl/stream_service.hpp0000644000000000000000000001264112247075736025071 0ustar rootroot00000000000000// // ssl/stream_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com // Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_SSL_STREAM_SERVICE_HPP #define ASIO_SSL_STREAM_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include "asio/io_service.hpp" #include "asio/ssl/basic_context.hpp" #include "asio/ssl/detail/openssl_stream_service.hpp" #include "asio/ssl/stream_base.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ssl { /// Default service implementation for an SSL stream. class stream_service #if defined(GENERATING_DOCUMENTATION) : public asio::io_service::service #else : public asio::detail::service_base #endif { private: // The type of the platform-specific implementation. typedef detail::openssl_stream_service service_impl_type; public: #if defined(GENERATING_DOCUMENTATION) /// The unique service identifier. static asio::io_service::id id; #endif /// The type of a stream implementation. #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined impl_type; #else typedef service_impl_type::impl_type impl_type; #endif /// Construct a new stream service for the specified io_service. explicit stream_service(asio::io_service& io_service) : asio::detail::service_base(io_service), service_impl_(asio::use_service(io_service)) { } /// Destroy all user-defined handler objects owned by the service. void shutdown_service() { } /// Return a null stream implementation. impl_type null() const { return service_impl_.null(); } /// Create a new stream implementation. template void create(impl_type& impl, Stream& next_layer, basic_context& context) { service_impl_.create(impl, next_layer, context); } /// Destroy a stream implementation. template void destroy(impl_type& impl, Stream& next_layer) { service_impl_.destroy(impl, next_layer); } /// Perform SSL handshaking. template asio::error_code handshake(impl_type& impl, Stream& next_layer, stream_base::handshake_type type, asio::error_code& ec) { return service_impl_.handshake(impl, next_layer, type, ec); } /// Start an asynchronous SSL handshake. template void async_handshake(impl_type& impl, Stream& next_layer, stream_base::handshake_type type, HandshakeHandler handler) { service_impl_.async_handshake(impl, next_layer, type, handler); } /// Shut down SSL on the stream. template asio::error_code shutdown(impl_type& impl, Stream& next_layer, asio::error_code& ec) { return service_impl_.shutdown(impl, next_layer, ec); } /// Asynchronously shut down SSL on the stream. template void async_shutdown(impl_type& impl, Stream& next_layer, ShutdownHandler handler) { service_impl_.async_shutdown(impl, next_layer, handler); } /// Write some data to the stream. template std::size_t write_some(impl_type& impl, Stream& next_layer, const ConstBufferSequence& buffers, asio::error_code& ec) { return service_impl_.write_some(impl, next_layer, buffers, ec); } /// Start an asynchronous write. template void async_write_some(impl_type& impl, Stream& next_layer, const ConstBufferSequence& buffers, WriteHandler handler) { service_impl_.async_write_some(impl, next_layer, buffers, handler); } /// Read some data from the stream. template std::size_t read_some(impl_type& impl, Stream& next_layer, const MutableBufferSequence& buffers, asio::error_code& ec) { return service_impl_.read_some(impl, next_layer, buffers, ec); } /// Start an asynchronous read. template void async_read_some(impl_type& impl, Stream& next_layer, const MutableBufferSequence& buffers, ReadHandler handler) { service_impl_.async_read_some(impl, next_layer, buffers, handler); } /// Peek at the incoming data on the stream. template std::size_t peek(impl_type& impl, Stream& next_layer, const MutableBufferSequence& buffers, asio::error_code& ec) { return service_impl_.peek(impl, next_layer, buffers, ec); } /// Determine the amount of data that may be read without blocking. template std::size_t in_avail(impl_type& impl, Stream& next_layer, asio::error_code& ec) { return service_impl_.in_avail(impl, next_layer, ec); } private: // The service that provides the platform-specific implementation. service_impl_type& service_impl_; }; } // namespace ssl } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_SSL_STREAM_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/ssl/detail/openssl_context_service.hpp0000755000000000000000000002256512247075736030300 0ustar rootroot00000000000000// // ssl/detail/openssl_context_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com // Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_SSL_DETAIL_OPENSSL_CONTEXT_SERVICE_HPP #define ASIO_SSL_DETAIL_OPENSSL_CONTEXT_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/ssl/context_base.hpp" #include "asio/ssl/detail/openssl_init.hpp" #include "asio/ssl/detail/openssl_types.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ssl { namespace detail { class openssl_context_service : public asio::detail::service_base { public: // The native type of the context. typedef ::SSL_CTX* impl_type; // The type for the password callback function object. typedef boost::function password_callback_type; // Constructor. openssl_context_service(asio::io_service& io_service) : asio::detail::service_base(io_service) { } // Destroy all user-defined handler objects owned by the service. void shutdown_service() { } // Return a null context implementation. static impl_type null() { return 0; } // Create a new context implementation. void create(impl_type& impl, context_base::method m) { switch (m) { /* Not supported in libssl 1.0.0 case context_base::sslv2: impl = ::SSL_CTX_new(::SSLv2_method()); break; case context_base::sslv2_client: impl = ::SSL_CTX_new(::SSLv2_client_method()); break; case context_base::sslv2_server: impl = ::SSL_CTX_new(::SSLv2_server_method()); break; */ case context_base::sslv3: impl = ::SSL_CTX_new(::SSLv3_method()); break; case context_base::sslv3_client: impl = ::SSL_CTX_new(::SSLv3_client_method()); break; case context_base::sslv3_server: impl = ::SSL_CTX_new(::SSLv3_server_method()); break; case context_base::tlsv1: impl = ::SSL_CTX_new(::TLSv1_method()); break; case context_base::tlsv1_client: impl = ::SSL_CTX_new(::TLSv1_client_method()); break; case context_base::tlsv1_server: impl = ::SSL_CTX_new(::TLSv1_server_method()); break; case context_base::sslv23: impl = ::SSL_CTX_new(::SSLv23_method()); break; case context_base::sslv23_client: impl = ::SSL_CTX_new(::SSLv23_client_method()); break; case context_base::sslv23_server: impl = ::SSL_CTX_new(::SSLv23_server_method()); break; default: impl = ::SSL_CTX_new(0); break; } } // Destroy a context implementation. void destroy(impl_type& impl) { if (impl != null()) { if (impl->default_passwd_callback_userdata) { password_callback_type* callback = static_cast( impl->default_passwd_callback_userdata); delete callback; impl->default_passwd_callback_userdata = 0; } ::SSL_CTX_free(impl); impl = null(); } } // Set options on the context. asio::error_code set_options(impl_type& impl, context_base::options o, asio::error_code& ec) { ::SSL_CTX_set_options(impl, o); ec = asio::error_code(); return ec; } // Set peer verification mode. asio::error_code set_verify_mode(impl_type& impl, context_base::verify_mode v, asio::error_code& ec) { ::SSL_CTX_set_verify(impl, v, 0); ec = asio::error_code(); return ec; } // Load a certification authority file for performing verification. asio::error_code load_verify_file(impl_type& impl, const std::string& filename, asio::error_code& ec) { if (::SSL_CTX_load_verify_locations(impl, filename.c_str(), 0) != 1) { ec = asio::error::invalid_argument; return ec; } ec = asio::error_code(); return ec; } // Add a directory containing certification authority files to be used for // performing verification. asio::error_code add_verify_path(impl_type& impl, const std::string& path, asio::error_code& ec) { if (::SSL_CTX_load_verify_locations(impl, 0, path.c_str()) != 1) { ec = asio::error::invalid_argument; return ec; } ec = asio::error_code(); return ec; } // Use a certificate from a file. asio::error_code use_certificate_file(impl_type& impl, const std::string& filename, context_base::file_format format, asio::error_code& ec) { int file_type; switch (format) { case context_base::asn1: file_type = SSL_FILETYPE_ASN1; break; case context_base::pem: file_type = SSL_FILETYPE_PEM; break; default: { ec = asio::error::invalid_argument; return ec; } } if (::SSL_CTX_use_certificate_file(impl, filename.c_str(), file_type) != 1) { ec = asio::error::invalid_argument; return ec; } ec = asio::error_code(); return ec; } // Use a certificate chain from a file. asio::error_code use_certificate_chain_file(impl_type& impl, const std::string& filename, asio::error_code& ec) { if (::SSL_CTX_use_certificate_chain_file(impl, filename.c_str()) != 1) { ec = asio::error::invalid_argument; return ec; } ec = asio::error_code(); return ec; } // Use a private key from a file. asio::error_code use_private_key_file(impl_type& impl, const std::string& filename, context_base::file_format format, asio::error_code& ec) { int file_type; switch (format) { case context_base::asn1: file_type = SSL_FILETYPE_ASN1; break; case context_base::pem: file_type = SSL_FILETYPE_PEM; break; default: { ec = asio::error::invalid_argument; return ec; } } if (::SSL_CTX_use_PrivateKey_file(impl, filename.c_str(), file_type) != 1) { ec = asio::error::invalid_argument; return ec; } ec = asio::error_code(); return ec; } // Use an RSA private key from a file. asio::error_code use_rsa_private_key_file(impl_type& impl, const std::string& filename, context_base::file_format format, asio::error_code& ec) { int file_type; switch (format) { case context_base::asn1: file_type = SSL_FILETYPE_ASN1; break; case context_base::pem: file_type = SSL_FILETYPE_PEM; break; default: { ec = asio::error::invalid_argument; return ec; } } if (::SSL_CTX_use_RSAPrivateKey_file( impl, filename.c_str(), file_type) != 1) { ec = asio::error::invalid_argument; return ec; } ec = asio::error_code(); return ec; } // Use the specified file to obtain the temporary Diffie-Hellman parameters. asio::error_code use_tmp_dh_file(impl_type& impl, const std::string& filename, asio::error_code& ec) { ::BIO* bio = ::BIO_new_file(filename.c_str(), "r"); if (!bio) { ec = asio::error::invalid_argument; return ec; } ::DH* dh = ::PEM_read_bio_DHparams(bio, 0, 0, 0); if (!dh) { ::BIO_free(bio); ec = asio::error::invalid_argument; return ec; } ::BIO_free(bio); int result = ::SSL_CTX_set_tmp_dh(impl, dh); ::DH_free(dh); if (result != 1) { ec = asio::error::invalid_argument; return ec; } ec = asio::error_code(); return ec; } static int password_callback(char* buf, int size, int purpose, void* data) { using namespace std; // For strncat and strlen. if (data) { password_callback_type* callback = static_cast(data); std::string passwd = (*callback)(static_cast(size), purpose ? context_base::for_writing : context_base::for_reading); *buf = '\0'; strncat(buf, passwd.c_str(), size); return strlen(buf); } return 0; } // Set the password callback. template asio::error_code set_password_callback(impl_type& impl, Password_Callback callback, asio::error_code& ec) { // Allocate callback function object if not already present. if (impl->default_passwd_callback_userdata) { password_callback_type* callback_function = static_cast( impl->default_passwd_callback_userdata); *callback_function = callback; } else { password_callback_type* callback_function = new password_callback_type(callback); impl->default_passwd_callback_userdata = callback_function; } // Set the password callback. SSL_CTX_set_default_passwd_cb(impl, &openssl_context_service::password_callback); ec = asio::error_code(); return ec; } private: // Ensure openssl is initialised. openssl_init<> init_; }; } // namespace detail } // namespace ssl } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_SSL_DETAIL_OPENSSL_CONTEXT_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/ssl/detail/openssl_init.hpp0000755000000000000000000001024512247075736026027 0ustar rootroot00000000000000// // ssl/detail/openssl_init.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com // Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_SSL_DETAIL_OPENSSL_INIT_HPP #define ASIO_SSL_DETAIL_OPENSSL_INIT_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include #include #include "asio/detail/mutex.hpp" #include "asio/detail/tss_ptr.hpp" #include "asio/ssl/detail/openssl_types.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ssl { namespace detail { template class openssl_init : private boost::noncopyable { private: // Structure to perform the actual initialisation. class do_init { public: do_init() { if (Do_Init) { ::SSL_library_init(); ::SSL_load_error_strings(); ::OpenSSL_add_ssl_algorithms(); mutexes_.resize(::CRYPTO_num_locks()); for (size_t i = 0; i < mutexes_.size(); ++i) mutexes_[i].reset(new asio::detail::mutex); ::CRYPTO_set_locking_callback(&do_init::openssl_locking_func); ::CRYPTO_set_id_callback(&do_init::openssl_id_func); } } ~do_init() { if (Do_Init) { ::CRYPTO_set_id_callback(0); ::CRYPTO_set_locking_callback(0); ::ERR_free_strings(); ::ERR_remove_state(0); ::EVP_cleanup(); ::CRYPTO_cleanup_all_ex_data(); ::CONF_modules_unload(1); ::ENGINE_cleanup(); } } // Helper function to manage a do_init singleton. The static instance of the // openssl_init object ensures that this function is always called before // main, and therefore before any other threads can get started. The do_init // instance must be static in this function to ensure that it gets // initialised before any other global objects try to use it. static boost::shared_ptr instance() { static boost::shared_ptr init(new do_init); return init; } private: static unsigned long openssl_id_func() { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) return ::GetCurrentThreadId(); #else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) void* id = instance()->thread_id_; if (id == 0) instance()->thread_id_ = id = &id; // Ugh. BOOST_ASSERT(sizeof(unsigned long) >= sizeof(void*)); return reinterpret_cast(id); #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) } static void openssl_locking_func(int mode, int n, const char* /*file*/, int /*line*/) { if (mode & CRYPTO_LOCK) instance()->mutexes_[n]->lock(); else instance()->mutexes_[n]->unlock(); } // Mutexes to be used in locking callbacks. std::vector > mutexes_; #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) // The thread identifiers to be used by openssl. asio::detail::tss_ptr thread_id_; #endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) }; public: // Constructor. openssl_init() : ref_(do_init::instance()) { using namespace std; // For memmove. // Ensure openssl_init::instance_ is linked in. openssl_init* tmp = &instance_; memmove(&tmp, &tmp, sizeof(openssl_init*)); } // Destructor. ~openssl_init() { } private: // Instance to force initialisation of openssl at global scope. static openssl_init instance_; // Reference to singleton do_init object to ensure that openssl does not get // cleaned up until the last user has finished with it. boost::shared_ptr ref_; }; template openssl_init openssl_init::instance_; } // namespace detail } // namespace ssl } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_SSL_DETAIL_OPENSSL_INIT_HPP percona-xtradb-cluster-galera/asio/asio/ssl/detail/openssl_operation.hpp0000755000000000000000000003234412247075736027070 0ustar rootroot00000000000000// // ssl/detail/openssl_operation.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_SSL_DETAIL_OPENSSL_OPERATION_HPP #define ASIO_SSL_DETAIL_OPENSSL_OPERATION_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include #include "asio/buffer.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/placeholders.hpp" #include "asio/ssl/detail/openssl_types.hpp" #include "asio/strand.hpp" #include "asio/system_error.hpp" #include "asio/write.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ssl { namespace detail { typedef boost::function ssl_primitive_func; typedef boost::function user_handler_func; // Network send_/recv buffer implementation // // class net_buffer { static const int NET_BUF_SIZE = 16*1024 + 256; // SSL record size + spare unsigned char buf_[NET_BUF_SIZE]; unsigned char* data_start_; unsigned char* data_end_; public: net_buffer() { data_start_ = data_end_ = buf_; } unsigned char* get_unused_start() { return data_end_; } unsigned char* get_data_start() { return data_start_; } size_t get_unused_len() { return (NET_BUF_SIZE - (data_end_ - buf_)); } size_t get_data_len() { return (data_end_ - data_start_); } void data_added(size_t count) { data_end_ += count; data_end_ = data_end_ > (buf_ + NET_BUF_SIZE)? (buf_ + NET_BUF_SIZE): data_end_; } void data_removed(size_t count) { data_start_ += count; if (data_start_ >= data_end_) reset(); } void reset() { data_start_ = buf_; data_end_ = buf_; } bool has_data() { return (data_start_ < data_end_); } }; // class net_buffer // // Operation class // // template class openssl_operation { public: // Constructor for asynchronous operations openssl_operation(ssl_primitive_func primitive, Stream& socket, net_buffer& recv_buf, SSL* session, BIO* ssl_bio, user_handler_func handler, asio::io_service::strand& strand ) : primitive_(primitive) , user_handler_(handler) , strand_(&strand) , recv_buf_(recv_buf) , socket_(socket) , ssl_bio_(ssl_bio) , session_(session) { write_ = boost::bind( &openssl_operation::do_async_write, this, boost::arg<1>(), boost::arg<2>() ); read_ = boost::bind( &openssl_operation::do_async_read, this ); handler_= boost::bind( &openssl_operation::async_user_handler, this, boost::arg<1>(), boost::arg<2>() ); } // Constructor for synchronous operations openssl_operation(ssl_primitive_func primitive, Stream& socket, net_buffer& recv_buf, SSL* session, BIO* ssl_bio) : primitive_(primitive) , strand_(0) , recv_buf_(recv_buf) , socket_(socket) , ssl_bio_(ssl_bio) , session_(session) { write_ = boost::bind( &openssl_operation::do_sync_write, this, boost::arg<1>(), boost::arg<2>() ); read_ = boost::bind( &openssl_operation::do_sync_read, this ); handler_ = boost::bind( &openssl_operation::sync_user_handler, this, boost::arg<1>(), boost::arg<2>() ); } // Start operation // In case of asynchronous it returns 0, in sync mode returns success code // or throws an error... int start() { int rc = primitive_( session_ ); bool is_operation_done = (rc > 0); // For connect/accept/shutdown, the operation // is done, when return code is 1 // for write, it is done, when is retcode > 0 // for read, is is done when retcode > 0 int error_code = !is_operation_done ? ::SSL_get_error( session_, rc ) : 0; int sys_error_code = ERR_get_error(); if (error_code == SSL_ERROR_SSL) return handler_(asio::error_code( sys_error_code, asio::error::get_ssl_category()), rc); bool is_read_needed = (error_code == SSL_ERROR_WANT_READ); bool is_write_needed = (error_code == SSL_ERROR_WANT_WRITE || ::BIO_ctrl_pending( ssl_bio_ )); bool is_shut_down_received = ((::SSL_get_shutdown( session_ ) & SSL_RECEIVED_SHUTDOWN) == SSL_RECEIVED_SHUTDOWN); bool is_shut_down_sent = ((::SSL_get_shutdown( session_ ) & SSL_SENT_SHUTDOWN) == SSL_SENT_SHUTDOWN); if (is_shut_down_sent && is_shut_down_received && is_operation_done && !is_write_needed) // SSL connection is shut down cleanly return handler_(asio::error_code(), 1); if (is_shut_down_received && !is_operation_done) // Shutdown has been requested, while we were reading or writing... // abort our action... return handler_(asio::error::shut_down, 0); if (!is_operation_done && !is_read_needed && !is_write_needed && !is_shut_down_sent) { // The operation has failed... It is not completed and does // not want network communication nor does want to send shutdown out... if (error_code == SSL_ERROR_SYSCALL) { return handler_(asio::error_code( sys_error_code, asio::error::system_category), rc); } else { return handler_(asio::error_code( sys_error_code, asio::error::get_ssl_category()), rc); } } if (!is_operation_done && !is_write_needed) { // We may have left over data that we can pass to SSL immediately if (recv_buf_.get_data_len() > 0) { // Pass the buffered data to SSL int written = ::BIO_write ( ssl_bio_, recv_buf_.get_data_start(), recv_buf_.get_data_len() ); if (written > 0) { recv_buf_.data_removed(written); } else if (written < 0) { if (!BIO_should_retry(ssl_bio_)) { // Some serios error with BIO.... return handler_(asio::error::no_recovery, 0); } } return start(); } else if (is_read_needed || (is_shut_down_sent && !is_shut_down_received)) { return read_(); } } // Continue with operation, flush any SSL data out to network... return write_(is_operation_done, rc); } // Private implementation private: typedef boost::function int_handler_func; typedef boost::function write_func; typedef boost::function read_func; ssl_primitive_func primitive_; user_handler_func user_handler_; asio::io_service::strand* strand_; write_func write_; read_func read_; int_handler_func handler_; net_buffer send_buf_; // buffers for network IO // The recv buffer is owned by the stream, not the operation, since there can // be left over bytes after passing the data up to the application, and these // bytes need to be kept around for the next read operation issued by the // application. net_buffer& recv_buf_; Stream& socket_; BIO* ssl_bio_; SSL* session_; // int sync_user_handler(const asio::error_code& error, int rc) { if (!error) return rc; throw asio::system_error(error); } int async_user_handler(asio::error_code error, int rc) { if (rc < 0) { if (!error) error = asio::error::no_recovery; rc = 0; } user_handler_(error, rc); return 0; } // Writes bytes asynchronously from SSL to NET int do_async_write(bool is_operation_done, int rc) { int len = ::BIO_ctrl_pending( ssl_bio_ ); if ( len ) { // There is something to write into net, do it... len = (int)send_buf_.get_unused_len() > len? len: send_buf_.get_unused_len(); if (len == 0) { // In case our send buffer is full, we have just to wait until // previous send to complete... return 0; } // Read outgoing data from bio len = ::BIO_read( ssl_bio_, send_buf_.get_unused_start(), len); if (len > 0) { unsigned char *data_start = send_buf_.get_unused_start(); send_buf_.data_added(len); BOOST_ASSERT(strand_); asio::async_write ( socket_, asio::buffer(data_start, len), strand_->wrap ( boost::bind ( &openssl_operation::async_write_handler, this, is_operation_done, rc, asio::placeholders::error, asio::placeholders::bytes_transferred ) ) ); return 0; } else if (!BIO_should_retry(ssl_bio_)) { // Seems like fatal error // reading from SSL BIO has failed... handler_(asio::error::no_recovery, 0); return 0; } } if (is_operation_done) { // Finish the operation, with success handler_(asio::error_code(), rc); return 0; } // OPeration is not done and writing to net has been made... // start operation again start(); return 0; } void async_write_handler(bool is_operation_done, int rc, const asio::error_code& error, size_t bytes_sent) { if (!error) { // Remove data from send buffer send_buf_.data_removed(bytes_sent); if (is_operation_done) handler_(asio::error_code(), rc); else // Since the operation was not completed, try it again... start(); } else handler_(error, rc); } int do_async_read() { // Wait for new data BOOST_ASSERT(strand_); socket_.async_read_some ( asio::buffer(recv_buf_.get_unused_start(), recv_buf_.get_unused_len()), strand_->wrap ( boost::bind ( &openssl_operation::async_read_handler, this, asio::placeholders::error, asio::placeholders::bytes_transferred ) ) ); return 0; } void async_read_handler(const asio::error_code& error, size_t bytes_recvd) { if (!error) { recv_buf_.data_added(bytes_recvd); // Pass the received data to SSL int written = ::BIO_write ( ssl_bio_, recv_buf_.get_data_start(), recv_buf_.get_data_len() ); if (written > 0) { recv_buf_.data_removed(written); } else if (written < 0) { if (!BIO_should_retry(ssl_bio_)) { // Some serios error with BIO.... handler_(asio::error::no_recovery, 0); return; } } // and try the SSL primitive again start(); } else { // Error in network level... // SSL can't continue either... handler_(error, 0); } } // Syncronous functions... int do_sync_write(bool is_operation_done, int rc) { int len = ::BIO_ctrl_pending( ssl_bio_ ); if ( len ) { // There is something to write into net, do it... len = (int)send_buf_.get_unused_len() > len? len: send_buf_.get_unused_len(); // Read outgoing data from bio len = ::BIO_read( ssl_bio_, send_buf_.get_unused_start(), len); if (len > 0) { size_t sent_len = asio::write( socket_, asio::buffer(send_buf_.get_unused_start(), len) ); send_buf_.data_added(len); send_buf_.data_removed(sent_len); } else if (!BIO_should_retry(ssl_bio_)) { // Seems like fatal error // reading from SSL BIO has failed... throw asio::system_error(asio::error::no_recovery); } } if (is_operation_done) // Finish the operation, with success return rc; // Operation is not finished, start again. return start(); } int do_sync_read() { size_t len = socket_.read_some ( asio::buffer(recv_buf_.get_unused_start(), recv_buf_.get_unused_len()) ); // Write data to ssl recv_buf_.data_added(len); // Pass the received data to SSL int written = ::BIO_write ( ssl_bio_, recv_buf_.get_data_start(), recv_buf_.get_data_len() ); if (written > 0) { recv_buf_.data_removed(written); } else if (written < 0) { if (!BIO_should_retry(ssl_bio_)) { // Some serios error with BIO.... throw asio::system_error(asio::error::no_recovery); } } // Try the operation again return start(); } }; // class openssl_operation } // namespace detail } // namespace ssl } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_SSL_DETAIL_OPENSSL_OPERATION_HPP percona-xtradb-cluster-galera/asio/asio/ssl/detail/openssl_stream_service.hpp0000644000000000000000000003556312247075736030106 0ustar rootroot00000000000000// // ssl/detail/stream_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com // Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_SSL_DETAIL_OPENSSL_STREAM_SERVICE_HPP #define ASIO_SSL_DETAIL_OPENSSL_STREAM_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include #include #include #include #include #include "asio/detail/buffer_sequence_adapter.hpp" #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/ssl/basic_context.hpp" #include "asio/ssl/stream_base.hpp" #include "asio/ssl/detail/openssl_operation.hpp" #include "asio/ssl/detail/openssl_types.hpp" #include "asio/strand.hpp" #include "asio/system_error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace ssl { namespace detail { class openssl_stream_service : public asio::detail::service_base { private: enum { max_buffer_size = INT_MAX }; //Base handler for asyncrhonous operations template class base_handler { public: typedef boost::function< void (const asio::error_code&, size_t)> func_t; base_handler(asio::io_service& io_service) : op_(NULL) , io_service_(io_service) , work_(io_service) {} void do_func(const asio::error_code& error, size_t size) { func_(error, size); } void set_operation(openssl_operation* op) { op_ = op; } void set_func(func_t func) { func_ = func; } ~base_handler() { delete op_; } private: func_t func_; openssl_operation* op_; asio::io_service& io_service_; asio::io_service::work work_; }; // class base_handler // Handler for asynchronous IO (write/read) operations template class io_handler : public base_handler { public: io_handler(Handler handler, asio::io_service& io_service) : base_handler(io_service) , handler_(handler) { this->set_func(boost::bind( &io_handler::handler_impl, this, boost::arg<1>(), boost::arg<2>() )); } private: Handler handler_; void handler_impl(const asio::error_code& error, size_t size) { std::auto_ptr > this_ptr(this); handler_(error, size); } }; // class io_handler // Handler for asyncrhonous handshake (connect, accept) functions template class handshake_handler : public base_handler { public: handshake_handler(Handler handler, asio::io_service& io_service) : base_handler(io_service) , handler_(handler) { this->set_func(boost::bind( &handshake_handler::handler_impl, this, boost::arg<1>(), boost::arg<2>() )); } private: Handler handler_; void handler_impl(const asio::error_code& error, size_t) { std::auto_ptr > this_ptr(this); handler_(error); } }; // class handshake_handler // Handler for asyncrhonous shutdown template class shutdown_handler : public base_handler { public: shutdown_handler(Handler handler, asio::io_service& io_service) : base_handler(io_service), handler_(handler) { this->set_func(boost::bind( &shutdown_handler::handler_impl, this, boost::arg<1>(), boost::arg<2>() )); } private: Handler handler_; void handler_impl(const asio::error_code& error, size_t) { std::auto_ptr > this_ptr(this); handler_(error); } }; // class shutdown_handler public: // The implementation type. typedef struct impl_struct { ::SSL* ssl; ::BIO* ext_bio; net_buffer recv_buf; } * impl_type; // Construct a new stream socket service for the specified io_service. explicit openssl_stream_service(asio::io_service& io_service) : asio::detail::service_base(io_service), strand_(io_service) { } // Destroy all user-defined handler objects owned by the service. void shutdown_service() { } // Return a null stream implementation. impl_type null() const { return 0; } // Create a new stream implementation. template void create(impl_type& impl, Stream& /*next_layer*/, basic_context& context) { impl = new impl_struct; impl->ssl = ::SSL_new(context.impl()); ::SSL_set_mode(impl->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); ::SSL_set_mode(impl->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); ::BIO* int_bio = 0; impl->ext_bio = 0; ::BIO_new_bio_pair(&int_bio, 8192, &impl->ext_bio, 8192); ::SSL_set_bio(impl->ssl, int_bio, int_bio); } // Destroy a stream implementation. template void destroy(impl_type& impl, Stream& /*next_layer*/) { if (impl != 0) { ::BIO_free(impl->ext_bio); ::SSL_free(impl->ssl); delete impl; impl = 0; } } // Perform SSL handshaking. template asio::error_code handshake(impl_type& impl, Stream& next_layer, stream_base::handshake_type type, asio::error_code& ec) { try { openssl_operation op( type == stream_base::client ? &ssl_wrap::SSL_connect: &ssl_wrap::SSL_accept, next_layer, impl->recv_buf, impl->ssl, impl->ext_bio); op.start(); } catch (asio::system_error& e) { ec = e.code(); return ec; } ec = asio::error_code(); return ec; } // Start an asynchronous SSL handshake. template void async_handshake(impl_type& impl, Stream& next_layer, stream_base::handshake_type type, Handler handler) { typedef handshake_handler connect_handler; connect_handler* local_handler = new connect_handler(handler, get_io_service()); openssl_operation* op = new openssl_operation ( type == stream_base::client ? &ssl_wrap::SSL_connect: &ssl_wrap::SSL_accept, next_layer, impl->recv_buf, impl->ssl, impl->ext_bio, boost::bind ( &base_handler::do_func, local_handler, boost::arg<1>(), boost::arg<2>() ), strand_ ); local_handler->set_operation(op); strand_.post(boost::bind(&openssl_operation::start, op)); } // Shut down SSL on the stream. template asio::error_code shutdown(impl_type& impl, Stream& next_layer, asio::error_code& ec) { try { openssl_operation op( &ssl_wrap::SSL_shutdown, next_layer, impl->recv_buf, impl->ssl, impl->ext_bio); op.start(); } catch (asio::system_error& e) { ec = e.code(); return ec; } ec = asio::error_code(); return ec; } // Asynchronously shut down SSL on the stream. template void async_shutdown(impl_type& impl, Stream& next_layer, Handler handler) { typedef shutdown_handler disconnect_handler; disconnect_handler* local_handler = new disconnect_handler(handler, get_io_service()); openssl_operation* op = new openssl_operation ( &ssl_wrap::SSL_shutdown, next_layer, impl->recv_buf, impl->ssl, impl->ext_bio, boost::bind ( &base_handler::do_func, local_handler, boost::arg<1>(), boost::arg<2>() ), strand_ ); local_handler->set_operation(op); strand_.post(boost::bind(&openssl_operation::start, op)); } // Write some data to the stream. template std::size_t write_some(impl_type& impl, Stream& next_layer, const Const_Buffers& buffers, asio::error_code& ec) { size_t bytes_transferred = 0; try { asio::const_buffer buffer = asio::detail::buffer_sequence_adapter< asio::const_buffer, Const_Buffers>::first(buffers); std::size_t buffer_size = asio::buffer_size(buffer); if (buffer_size > max_buffer_size) buffer_size = max_buffer_size; else if (buffer_size == 0) { ec = asio::error_code(); return 0; } boost::function send_func = boost::bind(boost::type(), &::SSL_write, boost::arg<1>(), asio::buffer_cast(buffer), static_cast(buffer_size)); openssl_operation op( send_func, next_layer, impl->recv_buf, impl->ssl, impl->ext_bio ); bytes_transferred = static_cast(op.start()); } catch (asio::system_error& e) { ec = e.code(); return 0; } ec = asio::error_code(); return bytes_transferred; } // Start an asynchronous write. template void async_write_some(impl_type& impl, Stream& next_layer, const Const_Buffers& buffers, Handler handler) { typedef io_handler send_handler; asio::const_buffer buffer = asio::detail::buffer_sequence_adapter< asio::const_buffer, Const_Buffers>::first(buffers); std::size_t buffer_size = asio::buffer_size(buffer); if (buffer_size > max_buffer_size) buffer_size = max_buffer_size; else if (buffer_size == 0) { get_io_service().post(asio::detail::bind_handler( handler, asio::error_code(), 0)); return; } send_handler* local_handler = new send_handler(handler, get_io_service()); boost::function send_func = boost::bind(boost::type(), &::SSL_write, boost::arg<1>(), asio::buffer_cast(buffer), static_cast(buffer_size)); openssl_operation* op = new openssl_operation ( send_func, next_layer, impl->recv_buf, impl->ssl, impl->ext_bio, boost::bind ( &base_handler::do_func, local_handler, boost::arg<1>(), boost::arg<2>() ), strand_ ); local_handler->set_operation(op); strand_.post(boost::bind(&openssl_operation::start, op)); } // Read some data from the stream. template std::size_t read_some(impl_type& impl, Stream& next_layer, const Mutable_Buffers& buffers, asio::error_code& ec) { size_t bytes_transferred = 0; try { asio::mutable_buffer buffer = asio::detail::buffer_sequence_adapter< asio::mutable_buffer, Mutable_Buffers>::first(buffers); std::size_t buffer_size = asio::buffer_size(buffer); if (buffer_size > max_buffer_size) buffer_size = max_buffer_size; else if (buffer_size == 0) { ec = asio::error_code(); return 0; } boost::function recv_func = boost::bind(boost::type(), &::SSL_read, boost::arg<1>(), asio::buffer_cast(buffer), static_cast(buffer_size)); openssl_operation op(recv_func, next_layer, impl->recv_buf, impl->ssl, impl->ext_bio ); bytes_transferred = static_cast(op.start()); } catch (asio::system_error& e) { ec = e.code(); return 0; } ec = asio::error_code(); return bytes_transferred; } // Start an asynchronous read. template void async_read_some(impl_type& impl, Stream& next_layer, const Mutable_Buffers& buffers, Handler handler) { typedef io_handler recv_handler; asio::mutable_buffer buffer = asio::detail::buffer_sequence_adapter< asio::mutable_buffer, Mutable_Buffers>::first(buffers); std::size_t buffer_size = asio::buffer_size(buffer); if (buffer_size > max_buffer_size) buffer_size = max_buffer_size; else if (buffer_size == 0) { get_io_service().post(asio::detail::bind_handler( handler, asio::error_code(), 0)); return; } recv_handler* local_handler = new recv_handler(handler, get_io_service()); boost::function recv_func = boost::bind(boost::type(), &::SSL_read, boost::arg<1>(), asio::buffer_cast(buffer), static_cast(buffer_size)); openssl_operation* op = new openssl_operation ( recv_func, next_layer, impl->recv_buf, impl->ssl, impl->ext_bio, boost::bind ( &base_handler::do_func, local_handler, boost::arg<1>(), boost::arg<2>() ), strand_ ); local_handler->set_operation(op); strand_.post(boost::bind(&openssl_operation::start, op)); } // Peek at the incoming data on the stream. template std::size_t peek(impl_type& /*impl*/, Stream& /*next_layer*/, const Mutable_Buffers& /*buffers*/, asio::error_code& ec) { ec = asio::error_code(); return 0; } // Determine the amount of data that may be read without blocking. template std::size_t in_avail(impl_type& /*impl*/, Stream& /*next_layer*/, asio::error_code& ec) { ec = asio::error_code(); return 0; } private: asio::io_service::strand strand_; typedef asio::detail::mutex mutex_type; template struct ssl_wrap { static Mutex ssl_mutex_; static int SSL_accept(SSL *ssl) { typename Mutex::scoped_lock lock(ssl_mutex_); return ::SSL_accept(ssl); } static int SSL_connect(SSL *ssl) { typename Mutex::scoped_lock lock(ssl_mutex_); return ::SSL_connect(ssl); } static int SSL_shutdown(SSL *ssl) { typename Mutex::scoped_lock lock(ssl_mutex_); return ::SSL_shutdown(ssl); } }; }; template Mutex openssl_stream_service::ssl_wrap::ssl_mutex_; } // namespace detail } // namespace ssl } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_SSL_DETAIL_OPENSSL_STREAM_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/ssl/detail/openssl_types.hpp0000755000000000000000000000132612247075736026230 0ustar rootroot00000000000000// // ssl/detail/openssl_types.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_SSL_DETAIL_OPENSSL_TYPES_HPP #define ASIO_SSL_DETAIL_OPENSSL_TYPES_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include #include #include "asio/detail/socket_types.hpp" #endif // ASIO_SSL_DETAIL_OPENSSL_TYPES_HPP percona-xtradb-cluster-galera/asio/asio/windows/basic_handle.hpp0000644000000000000000000001531612247075736025345 0ustar rootroot00000000000000// // windows/basic_handle.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_WINDOWS_BASIC_HANDLE_HPP #define ASIO_WINDOWS_BASIC_HANDLE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) \ || defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) \ || defined(GENERATING_DOCUMENTATION) #include "asio/basic_io_object.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace windows { /// Provides Windows handle functionality. /** * The windows::basic_handle class template provides the ability to wrap a * Windows handle. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. */ template class basic_handle : public basic_io_object { public: /// The native representation of a handle. typedef typename HandleService::native_type native_type; /// A basic_handle is always the lowest layer. typedef basic_handle lowest_layer_type; /// Construct a basic_handle without opening it. /** * This constructor creates a handle without opening it. * * @param io_service The io_service object that the handle will use to * dispatch handlers for any asynchronous operations performed on the handle. */ explicit basic_handle(asio::io_service& io_service) : basic_io_object(io_service) { } /// Construct a basic_handle on an existing native handle. /** * This constructor creates a handle object to hold an existing native handle. * * @param io_service The io_service object that the handle will use to * dispatch handlers for any asynchronous operations performed on the handle. * * @param native_handle A native handle. * * @throws asio::system_error Thrown on failure. */ basic_handle(asio::io_service& io_service, const native_type& native_handle) : basic_io_object(io_service) { asio::error_code ec; this->service.assign(this->implementation, native_handle, ec); asio::detail::throw_error(ec); } /// Get a reference to the lowest layer. /** * This function returns a reference to the lowest layer in a stack of * layers. Since a basic_handle cannot contain any further layers, it simply * returns a reference to itself. * * @return A reference to the lowest layer in the stack of layers. Ownership * is not transferred to the caller. */ lowest_layer_type& lowest_layer() { return *this; } /// Get a const reference to the lowest layer. /** * This function returns a const reference to the lowest layer in a stack of * layers. Since a basic_handle cannot contain any further layers, it simply * returns a reference to itself. * * @return A const reference to the lowest layer in the stack of layers. * Ownership is not transferred to the caller. */ const lowest_layer_type& lowest_layer() const { return *this; } /// Assign an existing native handle to the handle. /* * This function opens the handle to hold an existing native handle. * * @param native_handle A native handle. * * @throws asio::system_error Thrown on failure. */ void assign(const native_type& native_handle) { asio::error_code ec; this->service.assign(this->implementation, native_handle, ec); asio::detail::throw_error(ec); } /// Assign an existing native handle to the handle. /* * This function opens the handle to hold an existing native handle. * * @param native_handle A native handle. * * @param ec Set to indicate what error occurred, if any. */ asio::error_code assign(const native_type& native_handle, asio::error_code& ec) { return this->service.assign(this->implementation, native_handle, ec); } /// Determine whether the handle is open. bool is_open() const { return this->service.is_open(this->implementation); } /// Close the handle. /** * This function is used to close the handle. Any asynchronous read or write * operations will be cancelled immediately, and will complete with the * asio::error::operation_aborted error. * * @throws asio::system_error Thrown on failure. */ void close() { asio::error_code ec; this->service.close(this->implementation, ec); asio::detail::throw_error(ec); } /// Close the handle. /** * This function is used to close the handle. Any asynchronous read or write * operations will be cancelled immediately, and will complete with the * asio::error::operation_aborted error. * * @param ec Set to indicate what error occurred, if any. */ asio::error_code close(asio::error_code& ec) { return this->service.close(this->implementation, ec); } /// Get the native handle representation. /** * This function may be used to obtain the underlying representation of the * handle. This is intended to allow access to native handle functionality * that is not otherwise provided. */ native_type native() { return this->service.native(this->implementation); } /// Cancel all asynchronous operations associated with the handle. /** * This function causes all outstanding asynchronous read or write operations * to finish immediately, and the handlers for cancelled operations will be * passed the asio::error::operation_aborted error. * * @throws asio::system_error Thrown on failure. */ void cancel() { asio::error_code ec; this->service.cancel(this->implementation, ec); asio::detail::throw_error(ec); } /// Cancel all asynchronous operations associated with the handle. /** * This function causes all outstanding asynchronous read or write operations * to finish immediately, and the handlers for cancelled operations will be * passed the asio::error::operation_aborted error. * * @param ec Set to indicate what error occurred, if any. */ asio::error_code cancel(asio::error_code& ec) { return this->service.cancel(this->implementation, ec); } protected: /// Protected destructor to prevent deletion through this type. ~basic_handle() { } }; } // namespace windows } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) // || defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) // || defined(GENERATING_DOCUMENTATION) #endif // ASIO_WINDOWS_BASIC_HANDLE_HPP percona-xtradb-cluster-galera/asio/asio/windows/basic_random_access_handle.hpp0000644000000000000000000003004412247075736030221 0ustar rootroot00000000000000// // windows/basic_random_access_handle.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_WINDOWS_BASIC_RANDOM_ACCESS_HANDLE_HPP #define ASIO_WINDOWS_BASIC_RANDOM_ACCESS_HANDLE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) \ || defined(GENERATING_DOCUMENTATION) #include #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/windows/basic_handle.hpp" #include "asio/windows/random_access_handle_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace windows { /// Provides random-access handle functionality. /** * The windows::basic_random_access_handle class template provides asynchronous * and blocking random-access handle functionality. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. */ template class basic_random_access_handle : public basic_handle { public: /// The native representation of a handle. typedef typename RandomAccessHandleService::native_type native_type; /// Construct a basic_random_access_handle without opening it. /** * This constructor creates a random-access handle without opening it. The * handle needs to be opened before data can be written to or or read from it. * * @param io_service The io_service object that the random-access handle will * use to dispatch handlers for any asynchronous operations performed on the * handle. */ explicit basic_random_access_handle(asio::io_service& io_service) : basic_handle(io_service) { } /// Construct a basic_random_access_handle on an existing native handle. /** * This constructor creates a random-access handle object to hold an existing * native handle. * * @param io_service The io_service object that the random-access handle will * use to dispatch handlers for any asynchronous operations performed on the * handle. * * @param native_handle The new underlying handle implementation. * * @throws asio::system_error Thrown on failure. */ basic_random_access_handle(asio::io_service& io_service, const native_type& native_handle) : basic_handle(io_service, native_handle) { } /// Write some data to the handle at the specified offset. /** * This function is used to write data to the random-access handle. The * function call will block until one or more bytes of the data has been * written successfully, or until an error occurs. * * @param offset The offset at which the data will be written. * * @param buffers One or more data buffers to be written to the handle. * * @returns The number of bytes written. * * @throws asio::system_error Thrown on failure. An error code of * asio::error::eof indicates that the connection was closed by the * peer. * * @note The write_some_at operation may not write all of the data. Consider * using the @ref write_at function if you need to ensure that all data is * written before the blocking operation completes. * * @par Example * To write a single data buffer use the @ref buffer function as follows: * @code * handle.write_some_at(42, asio::buffer(data, size)); * @endcode * See the @ref buffer documentation for information on writing multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template std::size_t write_some_at(boost::uint64_t offset, const ConstBufferSequence& buffers) { asio::error_code ec; std::size_t s = this->service.write_some_at( this->implementation, offset, buffers, ec); asio::detail::throw_error(ec); return s; } /// Write some data to the handle at the specified offset. /** * This function is used to write data to the random-access handle. The * function call will block until one or more bytes of the data has been * written successfully, or until an error occurs. * * @param offset The offset at which the data will be written. * * @param buffers One or more data buffers to be written to the handle. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes written. Returns 0 if an error occurred. * * @note The write_some operation may not transmit all of the data to the * peer. Consider using the @ref write_at function if you need to ensure that * all data is written before the blocking operation completes. */ template std::size_t write_some_at(boost::uint64_t offset, const ConstBufferSequence& buffers, asio::error_code& ec) { return this->service.write_some_at( this->implementation, offset, buffers, ec); } /// Start an asynchronous write at the specified offset. /** * This function is used to asynchronously write data to the random-access * handle. The function call always returns immediately. * * @param offset The offset at which the data will be written. * * @param buffers One or more data buffers to be written to the handle. * Although the buffers object may be copied as necessary, ownership of the * underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param handler The handler to be called when the write operation completes. * Copies will be made of the handler as required. The function signature of * the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes written. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note The write operation may not transmit all of the data to the peer. * Consider using the @ref async_write_at function if you need to ensure that * all data is written before the asynchronous operation completes. * * @par Example * To write a single data buffer use the @ref buffer function as follows: * @code * handle.async_write_some_at(42, asio::buffer(data, size), handler); * @endcode * See the @ref buffer documentation for information on writing multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template void async_write_some_at(boost::uint64_t offset, const ConstBufferSequence& buffers, WriteHandler handler) { this->service.async_write_some_at( this->implementation, offset, buffers, handler); } /// Read some data from the handle at the specified offset. /** * This function is used to read data from the random-access handle. The * function call will block until one or more bytes of data has been read * successfully, or until an error occurs. * * @param offset The offset at which the data will be read. * * @param buffers One or more buffers into which the data will be read. * * @returns The number of bytes read. * * @throws asio::system_error Thrown on failure. An error code of * asio::error::eof indicates that the connection was closed by the * peer. * * @note The read_some operation may not read all of the requested number of * bytes. Consider using the @ref read_at function if you need to ensure that * the requested amount of data is read before the blocking operation * completes. * * @par Example * To read into a single data buffer use the @ref buffer function as follows: * @code * handle.read_some_at(42, asio::buffer(data, size)); * @endcode * See the @ref buffer documentation for information on reading into multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template std::size_t read_some_at(boost::uint64_t offset, const MutableBufferSequence& buffers) { asio::error_code ec; std::size_t s = this->service.read_some_at( this->implementation, offset, buffers, ec); asio::detail::throw_error(ec); return s; } /// Read some data from the handle at the specified offset. /** * This function is used to read data from the random-access handle. The * function call will block until one or more bytes of data has been read * successfully, or until an error occurs. * * @param offset The offset at which the data will be read. * * @param buffers One or more buffers into which the data will be read. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes read. Returns 0 if an error occurred. * * @note The read_some operation may not read all of the requested number of * bytes. Consider using the @ref read_at function if you need to ensure that * the requested amount of data is read before the blocking operation * completes. */ template std::size_t read_some_at(boost::uint64_t offset, const MutableBufferSequence& buffers, asio::error_code& ec) { return this->service.read_some_at( this->implementation, offset, buffers, ec); } /// Start an asynchronous read at the specified offset. /** * This function is used to asynchronously read data from the random-access * handle. The function call always returns immediately. * * @param offset The offset at which the data will be read. * * @param buffers One or more buffers into which the data will be read. * Although the buffers object may be copied as necessary, ownership of the * underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param handler The handler to be called when the read operation completes. * Copies will be made of the handler as required. The function signature of * the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes read. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note The read operation may not read all of the requested number of bytes. * Consider using the @ref async_read_at function if you need to ensure that * the requested amount of data is read before the asynchronous operation * completes. * * @par Example * To read into a single data buffer use the @ref buffer function as follows: * @code * handle.async_read_some_at(42, asio::buffer(data, size), handler); * @endcode * See the @ref buffer documentation for information on reading into multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template void async_read_some_at(boost::uint64_t offset, const MutableBufferSequence& buffers, ReadHandler handler) { this->service.async_read_some_at( this->implementation, offset, buffers, handler); } }; } // namespace windows } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) // || defined(GENERATING_DOCUMENTATION) #endif // ASIO_WINDOWS_BASIC_RANDOM_ACCESS_HANDLE_HPP percona-xtradb-cluster-galera/asio/asio/windows/basic_stream_handle.hpp0000644000000000000000000002613712247075736026723 0ustar rootroot00000000000000// // windows/basic_stream_handle.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_WINDOWS_BASIC_STREAM_HANDLE_HPP #define ASIO_WINDOWS_BASIC_STREAM_HANDLE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) \ || defined(GENERATING_DOCUMENTATION) #include #include "asio/error.hpp" #include "asio/windows/basic_handle.hpp" #include "asio/windows/stream_handle_service.hpp" #include "asio/detail/throw_error.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace windows { /// Provides stream-oriented handle functionality. /** * The windows::basic_stream_handle class template provides asynchronous and * blocking stream-oriented handle functionality. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. * * @par Concepts: * AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. */ template class basic_stream_handle : public basic_handle { public: /// The native representation of a handle. typedef typename StreamHandleService::native_type native_type; /// Construct a basic_stream_handle without opening it. /** * This constructor creates a stream handle without opening it. The handle * needs to be opened and then connected or accepted before data can be sent * or received on it. * * @param io_service The io_service object that the stream handle will use to * dispatch handlers for any asynchronous operations performed on the handle. */ explicit basic_stream_handle(asio::io_service& io_service) : basic_handle(io_service) { } /// Construct a basic_stream_handle on an existing native handle. /** * This constructor creates a stream handle object to hold an existing native * handle. * * @param io_service The io_service object that the stream handle will use to * dispatch handlers for any asynchronous operations performed on the handle. * * @param native_handle The new underlying handle implementation. * * @throws asio::system_error Thrown on failure. */ basic_stream_handle(asio::io_service& io_service, const native_type& native_handle) : basic_handle(io_service, native_handle) { } /// Write some data to the handle. /** * This function is used to write data to the stream handle. The function call * will block until one or more bytes of the data has been written * successfully, or until an error occurs. * * @param buffers One or more data buffers to be written to the handle. * * @returns The number of bytes written. * * @throws asio::system_error Thrown on failure. An error code of * asio::error::eof indicates that the connection was closed by the * peer. * * @note The write_some operation may not transmit all of the data to the * peer. Consider using the @ref write function if you need to ensure that * all data is written before the blocking operation completes. * * @par Example * To write a single data buffer use the @ref buffer function as follows: * @code * handle.write_some(asio::buffer(data, size)); * @endcode * See the @ref buffer documentation for information on writing multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template std::size_t write_some(const ConstBufferSequence& buffers) { asio::error_code ec; std::size_t s = this->service.write_some(this->implementation, buffers, ec); asio::detail::throw_error(ec); return s; } /// Write some data to the handle. /** * This function is used to write data to the stream handle. The function call * will block until one or more bytes of the data has been written * successfully, or until an error occurs. * * @param buffers One or more data buffers to be written to the handle. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes written. Returns 0 if an error occurred. * * @note The write_some operation may not transmit all of the data to the * peer. Consider using the @ref write function if you need to ensure that * all data is written before the blocking operation completes. */ template std::size_t write_some(const ConstBufferSequence& buffers, asio::error_code& ec) { return this->service.write_some(this->implementation, buffers, ec); } /// Start an asynchronous write. /** * This function is used to asynchronously write data to the stream handle. * The function call always returns immediately. * * @param buffers One or more data buffers to be written to the handle. * Although the buffers object may be copied as necessary, ownership of the * underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param handler The handler to be called when the write operation completes. * Copies will be made of the handler as required. The function signature of * the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes written. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note The write operation may not transmit all of the data to the peer. * Consider using the @ref async_write function if you need to ensure that all * data is written before the asynchronous operation completes. * * @par Example * To write a single data buffer use the @ref buffer function as follows: * @code * handle.async_write_some(asio::buffer(data, size), handler); * @endcode * See the @ref buffer documentation for information on writing multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template void async_write_some(const ConstBufferSequence& buffers, WriteHandler handler) { this->service.async_write_some(this->implementation, buffers, handler); } /// Read some data from the handle. /** * This function is used to read data from the stream handle. The function * call will block until one or more bytes of data has been read successfully, * or until an error occurs. * * @param buffers One or more buffers into which the data will be read. * * @returns The number of bytes read. * * @throws asio::system_error Thrown on failure. An error code of * asio::error::eof indicates that the connection was closed by the * peer. * * @note The read_some operation may not read all of the requested number of * bytes. Consider using the @ref read function if you need to ensure that * the requested amount of data is read before the blocking operation * completes. * * @par Example * To read into a single data buffer use the @ref buffer function as follows: * @code * handle.read_some(asio::buffer(data, size)); * @endcode * See the @ref buffer documentation for information on reading into multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template std::size_t read_some(const MutableBufferSequence& buffers) { asio::error_code ec; std::size_t s = this->service.read_some(this->implementation, buffers, ec); asio::detail::throw_error(ec); return s; } /// Read some data from the handle. /** * This function is used to read data from the stream handle. The function * call will block until one or more bytes of data has been read successfully, * or until an error occurs. * * @param buffers One or more buffers into which the data will be read. * * @param ec Set to indicate what error occurred, if any. * * @returns The number of bytes read. Returns 0 if an error occurred. * * @note The read_some operation may not read all of the requested number of * bytes. Consider using the @ref read function if you need to ensure that * the requested amount of data is read before the blocking operation * completes. */ template std::size_t read_some(const MutableBufferSequence& buffers, asio::error_code& ec) { return this->service.read_some(this->implementation, buffers, ec); } /// Start an asynchronous read. /** * This function is used to asynchronously read data from the stream handle. * The function call always returns immediately. * * @param buffers One or more buffers into which the data will be read. * Although the buffers object may be copied as necessary, ownership of the * underlying memory blocks is retained by the caller, which must guarantee * that they remain valid until the handler is called. * * @param handler The handler to be called when the read operation completes. * Copies will be made of the handler as required. The function signature of * the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. * std::size_t bytes_transferred // Number of bytes read. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation * of the handler will be performed in a manner equivalent to using * asio::io_service::post(). * * @note The read operation may not read all of the requested number of bytes. * Consider using the @ref async_read function if you need to ensure that the * requested amount of data is read before the asynchronous operation * completes. * * @par Example * To read into a single data buffer use the @ref buffer function as follows: * @code * handle.async_read_some(asio::buffer(data, size), handler); * @endcode * See the @ref buffer documentation for information on reading into multiple * buffers in one go, and how to use it with arrays, boost::array or * std::vector. */ template void async_read_some(const MutableBufferSequence& buffers, ReadHandler handler) { this->service.async_read_some(this->implementation, buffers, handler); } }; } // namespace windows } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) // || defined(GENERATING_DOCUMENTATION) #endif // ASIO_WINDOWS_BASIC_STREAM_HANDLE_HPP percona-xtradb-cluster-galera/asio/asio/windows/overlapped_ptr.hpp0000644000000000000000000000523312247075736025774 0ustar rootroot00000000000000// // windows/overlapped_ptr.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_WINDOWS_OVERLAPPED_PTR_HPP #define ASIO_WINDOWS_OVERLAPPED_PTR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_WINDOWS_OVERLAPPED_PTR) \ || defined(GENERATING_DOCUMENTATION) #include "asio/detail/noncopyable.hpp" #include "asio/detail/win_iocp_overlapped_ptr.hpp" #include "asio/io_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace windows { /// Wraps a handler to create an OVERLAPPED object for use with overlapped I/O. /** * A special-purpose smart pointer used to wrap an application handler so that * it can be passed as the LPOVERLAPPED argument to overlapped I/O functions. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. */ class overlapped_ptr : private noncopyable { public: /// Construct an empty overlapped_ptr. overlapped_ptr() : impl_() { } /// Construct an overlapped_ptr to contain the specified handler. template explicit overlapped_ptr(asio::io_service& io_service, Handler handler) : impl_(io_service, handler) { } /// Destructor automatically frees the OVERLAPPED object unless released. ~overlapped_ptr() { } /// Reset to empty. void reset() { impl_.reset(); } /// Reset to contain the specified handler, freeing any current OVERLAPPED /// object. template void reset(asio::io_service& io_service, Handler handler) { impl_.reset(io_service, handler); } /// Get the contained OVERLAPPED object. OVERLAPPED* get() { return impl_.get(); } /// Get the contained OVERLAPPED object. const OVERLAPPED* get() const { return impl_.get(); } /// Release ownership of the OVERLAPPED object. OVERLAPPED* release() { return impl_.release(); } /// Post completion notification for overlapped operation. Releases ownership. void complete(const asio::error_code& ec, std::size_t bytes_transferred) { impl_.complete(ec, bytes_transferred); } private: detail::win_iocp_overlapped_ptr impl_; }; } // namespace windows } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_WINDOWS_OVERLAPPED_PTR) // || defined(GENERATING_DOCUMENTATION) #endif // ASIO_WINDOWS_OVERLAPPED_PTR_HPP percona-xtradb-cluster-galera/asio/asio/windows/random_access_handle.hpp0000644000000000000000000000204512247075736027060 0ustar rootroot00000000000000// // windows/random_access_handle.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_WINDOWS_RANDOM_ACCESS_HANDLE_HPP #define ASIO_WINDOWS_RANDOM_ACCESS_HANDLE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) \ || defined(GENERATING_DOCUMENTATION) #include "asio/windows/basic_random_access_handle.hpp" namespace asio { namespace windows { /// Typedef for the typical usage of a random-access handle. typedef basic_random_access_handle<> random_access_handle; } // namespace windows } // namespace asio #endif // defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) // || defined(GENERATING_DOCUMENTATION) #endif // ASIO_WINDOWS_RANDOM_ACCESS_HANDLE_HPP percona-xtradb-cluster-galera/asio/asio/windows/random_access_handle_service.hpp0000644000000000000000000001203412247075736030577 0ustar rootroot00000000000000// // windows/random_access_handle_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_WINDOWS_RANDOM_ACCESS_HANDLE_SERVICE_HPP #define ASIO_WINDOWS_RANDOM_ACCESS_HANDLE_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) \ || defined(GENERATING_DOCUMENTATION) #include #include #include #include "asio/detail/win_iocp_handle_service.hpp" #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace windows { /// Default service implementation for a random-access handle. class random_access_handle_service #if defined(GENERATING_DOCUMENTATION) : public asio::io_service::service #else : public asio::detail::service_base #endif { public: #if defined(GENERATING_DOCUMENTATION) /// The unique service identifier. static asio::io_service::id id; #endif private: // The type of the platform-specific implementation. typedef detail::win_iocp_handle_service service_impl_type; public: /// The type of a random-access handle implementation. #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined implementation_type; #else typedef service_impl_type::implementation_type implementation_type; #endif /// The native handle type. #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined native_type; #else typedef service_impl_type::native_type native_type; #endif /// Construct a new random-access handle service for the specified io_service. explicit random_access_handle_service(asio::io_service& io_service) : asio::detail::service_base< random_access_handle_service>(io_service), service_impl_(io_service) { } /// Destroy all user-defined handler objects owned by the service. void shutdown_service() { service_impl_.shutdown_service(); } /// Construct a new random-access handle implementation. void construct(implementation_type& impl) { service_impl_.construct(impl); } /// Destroy a random-access handle implementation. void destroy(implementation_type& impl) { service_impl_.destroy(impl); } /// Assign an existing native handle to a random-access handle. asio::error_code assign(implementation_type& impl, const native_type& native_handle, asio::error_code& ec) { return service_impl_.assign(impl, native_handle, ec); } /// Determine whether the handle is open. bool is_open(const implementation_type& impl) const { return service_impl_.is_open(impl); } /// Close a random-access handle implementation. asio::error_code close(implementation_type& impl, asio::error_code& ec) { return service_impl_.close(impl, ec); } /// Get the native handle implementation. native_type native(implementation_type& impl) { return service_impl_.native(impl); } /// Cancel all asynchronous operations associated with the handle. asio::error_code cancel(implementation_type& impl, asio::error_code& ec) { return service_impl_.cancel(impl, ec); } /// Write the given data at the specified offset. template std::size_t write_some_at(implementation_type& impl, boost::uint64_t offset, const ConstBufferSequence& buffers, asio::error_code& ec) { return service_impl_.write_some_at(impl, offset, buffers, ec); } /// Start an asynchronous write at the specified offset. template void async_write_some_at(implementation_type& impl, boost::uint64_t offset, const ConstBufferSequence& buffers, WriteHandler handler) { service_impl_.async_write_some_at(impl, offset, buffers, handler); } /// Read some data from the specified offset. template std::size_t read_some_at(implementation_type& impl, boost::uint64_t offset, const MutableBufferSequence& buffers, asio::error_code& ec) { return service_impl_.read_some_at(impl, offset, buffers, ec); } /// Start an asynchronous read at the specified offset. template void async_read_some_at(implementation_type& impl, boost::uint64_t offset, const MutableBufferSequence& buffers, ReadHandler handler) { service_impl_.async_read_some_at(impl, offset, buffers, handler); } private: // The platform-specific implementation. service_impl_type service_impl_; }; } // namespace windows } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) // || defined(GENERATING_DOCUMENTATION) #endif // ASIO_WINDOWS_RANDOM_ACCESS_HANDLE_SERVICE_HPP percona-xtradb-cluster-galera/asio/asio/windows/stream_handle.hpp0000644000000000000000000000174112247075736025554 0ustar rootroot00000000000000// // windows/stream_handle.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_WINDOWS_STREAM_HANDLE_HPP #define ASIO_WINDOWS_STREAM_HANDLE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) \ || defined(GENERATING_DOCUMENTATION) #include "asio/windows/basic_stream_handle.hpp" namespace asio { namespace windows { /// Typedef for the typical usage of a stream-oriented handle. typedef basic_stream_handle<> stream_handle; } // namespace windows } // namespace asio #endif // defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) // || defined(GENERATING_DOCUMENTATION) #endif // ASIO_WINDOWS_STREAM_HANDLE_HPP percona-xtradb-cluster-galera/asio/asio/windows/stream_handle_service.hpp0000644000000000000000000001120012247075736027263 0ustar rootroot00000000000000// // windows/stream_handle_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_WINDOWS_STREAM_HANDLE_SERVICE_HPP #define ASIO_WINDOWS_STREAM_HANDLE_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #if defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) \ || defined(GENERATING_DOCUMENTATION) #include #include "asio/detail/win_iocp_handle_service.hpp" #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { namespace windows { /// Default service implementation for a stream handle. class stream_handle_service #if defined(GENERATING_DOCUMENTATION) : public asio::io_service::service #else : public asio::detail::service_base #endif { public: #if defined(GENERATING_DOCUMENTATION) /// The unique service identifier. static asio::io_service::id id; #endif private: // The type of the platform-specific implementation. typedef detail::win_iocp_handle_service service_impl_type; public: /// The type of a stream handle implementation. #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined implementation_type; #else typedef service_impl_type::implementation_type implementation_type; #endif /// The native handle type. #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined native_type; #else typedef service_impl_type::native_type native_type; #endif /// Construct a new stream handle service for the specified io_service. explicit stream_handle_service(asio::io_service& io_service) : asio::detail::service_base(io_service), service_impl_(io_service) { } /// Destroy all user-defined handler objects owned by the service. void shutdown_service() { service_impl_.shutdown_service(); } /// Construct a new stream handle implementation. void construct(implementation_type& impl) { service_impl_.construct(impl); } /// Destroy a stream handle implementation. void destroy(implementation_type& impl) { service_impl_.destroy(impl); } /// Assign an existing native handle to a stream handle. asio::error_code assign(implementation_type& impl, const native_type& native_handle, asio::error_code& ec) { return service_impl_.assign(impl, native_handle, ec); } /// Determine whether the handle is open. bool is_open(const implementation_type& impl) const { return service_impl_.is_open(impl); } /// Close a stream handle implementation. asio::error_code close(implementation_type& impl, asio::error_code& ec) { return service_impl_.close(impl, ec); } /// Get the native handle implementation. native_type native(implementation_type& impl) { return service_impl_.native(impl); } /// Cancel all asynchronous operations associated with the handle. asio::error_code cancel(implementation_type& impl, asio::error_code& ec) { return service_impl_.cancel(impl, ec); } /// Write the given data to the stream. template std::size_t write_some(implementation_type& impl, const ConstBufferSequence& buffers, asio::error_code& ec) { return service_impl_.write_some(impl, buffers, ec); } /// Start an asynchronous write. template void async_write_some(implementation_type& impl, const ConstBufferSequence& buffers, WriteHandler handler) { service_impl_.async_write_some(impl, buffers, handler); } /// Read some data from the stream. template std::size_t read_some(implementation_type& impl, const MutableBufferSequence& buffers, asio::error_code& ec) { return service_impl_.read_some(impl, buffers, ec); } /// Start an asynchronous read. template void async_read_some(implementation_type& impl, const MutableBufferSequence& buffers, ReadHandler handler) { service_impl_.async_read_some(impl, buffers, handler); } private: // The platform-specific implementation. service_impl_type service_impl_; }; } // namespace windows } // namespace asio #include "asio/detail/pop_options.hpp" #endif // defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) // || defined(GENERATING_DOCUMENTATION) #endif // ASIO_WINDOWS_STREAM_HANDLE_SERVICE_HPP percona-xtradb-cluster-galera/common/common.h0000644000000000000000000000102212247075736021576 0ustar rootroot00000000000000/* *Copyright (C) 2012 Codership Oy */ /*! @file Stores some common definitions to be known throughout the modules */ #ifndef COMMON_DEFS_H #define COMMON_DEFS_H #define COMMON_TCP_SCHEME "tcp" #define COMMON_UDP_SCHEME "udp" #define COMMON_SSL_SCHEME "ssl" #define COMMON_DEFAULT_SCHEME COMMON_TCP_SCHEME #define COMMON_BASE_HOST_KEY "base_host" #define COMMON_BASE_PORT_KEY "base_port" #define COMMON_BASE_PORT_DEFAULT "4567" #define COMMON_STATE_FILE "grastate.dat" #endif // COMMON_DEFS_H percona-xtradb-cluster-galera/debian/README.Debian0000644000000000000000000000033012247075736022131 0ustar rootroot00000000000000percona-xtradb-cluster-galera for Debian ---------------------------------------- -- Ignacio Nin Tue, 06 Dec 2011 12:19:07 -0500 percona-xtradb-cluster-galera/debian/README.source0000644000000000000000000000035512247075736022256 0ustar rootroot00000000000000percona-xtradb-cluster-galera for Debian ---------------------------------------- percona-xtradb-cluster-galera/debian/changelog0000644000000000000000000000053612247075736021752 0ustar rootroot00000000000000percona-xtradb-cluster-galera-2.x (0.2-1) unstable; urgency=low * Change package name to include 2.x in its name -- Ignacio Nin Tue, 13 Mar 2012 13:14:50 -0300 percona-xtradb-cluster-galera (0.1-1) unstable; urgency=low * Initial release -- Ignacio Nin Tue, 06 Dec 2011 12:19:07 -0500 percona-xtradb-cluster-galera/debian/compat0000644000000000000000000000000212247075736021272 0ustar rootroot000000000000007 percona-xtradb-cluster-galera/debian/control0000644000000000000000000000123312247075736021476 0ustar rootroot00000000000000Source: percona-xtradb-cluster-galera-2.x Section: database Priority: extra Maintainer: Ignacio Nin Build-Depends: debhelper (>= 7.0.50~), scons, libboost-dev (>= 1.41), libssl-dev, check, libboost-program-options-dev (>= 1.41) Standards-Version: 3.8.4 Package: percona-xtradb-cluster-galera-2.x Architecture: any Provides: percona-xtradb-cluster-galera-25 Conflicts: galera, percona-xtradb-cluster-galera Replaces: galera, percona-xtradb-cluster-galera Depends: ${shlibs:Depends}, ${misc:Depends} Description: Galera components of Percona XtraDB Cluster This package contains the Galera components required by Percona XtraDB Cluster. percona-xtradb-cluster-galera/debian/copyright0000644000000000000000000000124312247075736022027 0ustar rootroot00000000000000This work was packaged for Debian by: Ignacio Nin on Tue, 06 Dec 2011 12:19:07 -0500 It was downloaded from: lp:galera/2.x Upstream Author(s): Codership Oy http://www.codership.com Copyright: This is Galera - Codership's implementation of wsrep interface (https://launchpad.net/wsrep/). The software and other files in this directory unless otherwise noted are distributed under GPLv3, see COPYING for details. The Debian packaging is: Copyright (C) 2011 Ignacio Nin and is licensed under the GPL version 3, see "/usr/share/common-licenses/GPL-3". percona-xtradb-cluster-galera/debian/docs0000644000000000000000000000003412247075736020744 0ustar rootroot00000000000000README README-MySQL COPYING percona-xtradb-cluster-galera/debian/percona-xtradb-cluster-galera-2.x.install0000644000000000000000000000005412247075736027711 0ustar rootroot00000000000000garb/garbd usr/bin libgalera_smm.so usr/lib percona-xtradb-cluster-galera/debian/rules0000755000000000000000000000102612247075736021153 0ustar rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 build: scons revno=$(GALERA_REVNO) boost_pool=0 garb/garbd libgalera_smm.so $(SCONS_ARGS) %: dh $@ percona-xtradb-cluster-galera/debian/source/0000755000000000000000000000000012247075736021374 5ustar rootroot00000000000000percona-xtradb-cluster-galera/debian/source/format0000644000000000000000000000000412247075736022601 0ustar rootroot000000000000001.0 percona-xtradb-cluster-galera/galera/SConscript0000644000000000000000000000006412247075736022117 0ustar rootroot00000000000000 SConscript(['src/SConscript', 'tests/SConscript']) percona-xtradb-cluster-galera/galera/src/0000755000000000000000000000000012247075736020674 5ustar rootroot00000000000000percona-xtradb-cluster-galera/galera/tests/0000755000000000000000000000000012247075736021247 5ustar rootroot00000000000000percona-xtradb-cluster-galera/galera/src/SConscript0000644000000000000000000000254312247075736022712 0ustar rootroot00000000000000 Import('env') libgaleraxx_env = env.Clone() libgaleraxx_srcs = [ 'mapped_buffer.cpp', 'write_set.cpp', 'trx_handle.cpp', 'key_entry.cpp', 'wsdb.cpp', 'certification.cpp', 'galera_service_thd.cpp', 'wsrep_params.cpp', 'gcs_action_source.cpp', 'galera_info.c', 'replicator.cpp', 'ist.cpp', 'gcs_dummy.cpp', 'saved_state.cpp' ] libgaleraxx_env.StaticLibrary('galera++', libgaleraxx_srcs) # Environment for multimaster library build mmlib_env = libgaleraxx_env.Clone() mmlib_env.Append(CPPFLAGS = ' -DGALERA_MULTIMASTER') mmlib_env.Replace(SHOBJPREFIX = 'libmmgalera++-') # Environment to compile provider unit (part of multimaster library) # This is needed to hardcode version and revision mmprovider_env = mmlib_env.Clone() Import ('GALERA_VER', 'GALERA_REV') mmprovider_env.Append(CPPFLAGS = ' -DGALERA_VER=\\"' + GALERA_VER + '\\"') mmprovider_env.Append(CPPFLAGS = ' -DGALERA_REV=\\"' + GALERA_REV + '\\"') # env.Append(LIBGALERA_OBJS = libgaleraxx_env.SharedObject(libgaleraxx_srcs)) env.Append(LIBMMGALERA_OBJS = mmlib_env.SharedObject([ 'replicator_smm.cpp', 'replicator_str.cpp', 'replicator_smm_stats.cpp', 'replicator_smm_params.cpp', ])) env.Append(LIBMMGALERA_OBJS = mmprovider_env.SharedObject([ 'wsrep_provider.cpp' ])) percona-xtradb-cluster-galera/galera/src/action_source.hpp0000644000000000000000000000056012247075736024243 0ustar rootroot00000000000000// // Copyright (C) 2010-2013 Codership Oy // #ifndef GALERA_ACTION_SOURCE_HPP #define GALERA_ACTION_SOURCE_HPP namespace galera { class ActionSource { public: ActionSource() { } virtual ~ActionSource() { } virtual ssize_t process(void* ctx, bool& exit_loop) = 0; }; } #endif // GALERA_ACTION_SOURCE_HPP percona-xtradb-cluster-galera/galera/src/certification.cpp0000644000000000000000000005443012247075736024231 0ustar rootroot00000000000000// // Copyright (C) 2010-2012 Codership Oy // #include "certification.hpp" #include "uuid.hpp" #include "gu_lock.hpp" #include "gu_throw.hpp" #include static const bool cert_debug_on(false); #define cert_debug \ if (cert_debug_on == false) { } \ else log_info << "cert debug: " std::string const galera::Certification::Param::log_conflicts = CERTIFICATION_PARAM_LOG_CONFLICTS_STR; std::string const galera::Certification::Defaults::log_conflicts = CERTIFICATION_DEFAULTS_LOG_CONFLICTS_STR; /*** It is EXTREMELY important that these constants are the same on all nodes. *** Don't change them ever!!! ***/ long const galera::Certification::max_length_default = 16384; unsigned long const galera::Certification::max_length_check_default = 127; void galera::Certification::purge_for_trx(TrxHandle* trx) { TrxHandle::CertKeySet& refs(trx->cert_keys_); for (TrxHandle::CertKeySet::iterator i = refs.begin(); i != refs.end(); ++i) { // Unref all referenced and remove if was referenced by us KeyEntry* const kel(i->first); const bool full_key(i->second.first); const bool shared(i->second.second); CertIndex::iterator ci(cert_index_.find(kel)); assert(ci != cert_index_.end()); KeyEntry* const ke(*ci); if (shared == false && (ke->ref_trx() == trx || ke->ref_full_trx() == trx)) { ke->unref(trx, full_key); } if (shared == true && (ke->ref_shared_trx() == trx || ke->ref_full_shared_trx() == trx)) { ke->unref_shared(trx, full_key); } if (ke->ref_trx() == 0 && ke->ref_shared_trx() == 0) { assert(ke->ref_full_trx() == 0); assert(ke->ref_full_shared_trx() == 0); delete ke; cert_index_.erase(ci); } if (kel != ke) delete kel; } } /*! for convenience returns true if conflict and false if not */ static inline bool certify_and_depend_v1to2(const galera::KeyEntry* const match, galera::TrxHandle* const trx, bool const full_key, bool const exclusive_key, bool const log_conflict) { // 1) if the key is full, match for any trx // 2) if the key is partial, match for trx with full key const galera::TrxHandle* const ref_trx(full_key == true ? match->ref_trx() : match->ref_full_trx()); if (cert_debug_on && ref_trx) { cert_debug << "exclusive match (" << (full_key == true ? "full" : "partial") << ") " << *trx << " <-----> " << *ref_trx; } wsrep_seqno_t const ref_seqno(ref_trx ? ref_trx->global_seqno() : -1); // trx should not have any references in index at this point assert(ref_trx != trx); if (gu_likely(0 != ref_trx)) { // cert conflict takes place if // 1) write sets originated from different nodes, are within cert range // 2) ref_trx is in isolation mode, write sets are within cert range if ((trx->source_id() != ref_trx->source_id() || (ref_trx->flags() & galera::TrxHandle::F_ISOLATION) != 0) && ref_seqno > trx->last_seen_seqno()) { if (gu_unlikely(log_conflict == true)) { log_info << "trx conflict for key " << match->get_key(ref_trx->version()) << ": " << *trx << " <--X--> " << *ref_trx; } return true; } } wsrep_seqno_t depends_seqno(ref_seqno); if (exclusive_key) // exclusive keys must depend on shared refs as well { const galera::TrxHandle* const ref_shared_trx(full_key == true ? match->ref_shared_trx() : match->ref_full_shared_trx()); assert(ref_shared_trx != trx); if (ref_shared_trx) { cert_debug << "shared match (" << (full_key == true ? "full" : "partial") << ") " << *trx << " <-----> " << *ref_shared_trx; depends_seqno = std::max(ref_shared_trx->global_seqno(), depends_seqno); } } trx->set_depends_seqno(std::max(trx->depends_seqno(), depends_seqno)); return false; } static bool certify_v1to2(galera::TrxHandle* trx, galera::Certification::CertIndex& cert_index, const galera::Key& key, galera::TrxHandle::CertKeySet& key_list, bool const store_keys, bool const log_conflicts) { typedef std::list KPS; KPS key_parts(key.key_parts()); KPS::const_iterator begin(key_parts.begin()), end; bool full_key(false); for (end = begin; full_key == false; end != key_parts.end() ? ++end : end) { full_key = (end == key_parts.end()); galera::Certification::CertIndex::iterator ci; galera::KeyEntry ke(key.version(), begin, end, key.flags()); cert_debug << "key: " << ke.get_key() << " (" << (full_key == true ? "full" : "partial") << ")"; bool const shared_key(ke.get_key().flags() & galera::Key::F_SHARED); if (store_keys && (key_list.find(&ke) != key_list.end())) { // avoid certification for duplicates // should be removed once we can eleminate dups on deserialization continue; } galera::KeyEntry* kep; if ((ci = cert_index.find(&ke)) == cert_index.end()) { if (store_keys) { kep = new galera::KeyEntry(ke); ci = cert_index.insert(kep).first; cert_debug << "created new entry"; } } else { cert_debug << "found existing entry"; // Note: For we skip certification for isolated trxs, only // cert index and key_list is populated. if ((trx->flags() & galera::TrxHandle::F_ISOLATION) == 0 && certify_and_depend_v1to2(*ci, trx, full_key, !shared_key, log_conflicts)) { return false; } if (store_keys) { if (gu_likely( true == ke.get_key().equal_all((*ci)->get_key()))) { kep = *ci; } else { // duplicate with different flags - need to store a copy kep = new galera::KeyEntry(ke); } } } if (store_keys) { key_list.insert(std::make_pair(kep, std::make_pair(full_key, shared_key))); } } return true; } galera::Certification::TestResult galera::Certification::do_test_v1to2(TrxHandle* trx, bool store_keys) { cert_debug << "BEGIN CERTIFICATION: " << *trx; galera::TrxHandle::CertKeySet& key_list(trx->cert_keys_); long key_count(0); gu::Lock lock(mutex_); if ((trx->flags() & (TrxHandle::F_ISOLATION | TrxHandle::F_PA_UNSAFE)) || trx_map_.empty()) { trx->set_depends_seqno(trx->global_seqno() - 1); } else { trx->set_depends_seqno( trx_map_.begin()->second->global_seqno() - 1); } #ifndef NDEBUG // to check that cleanup after cert failure returns cert_index_ // to original size size_t prev_cert_index_size(cert_index_.size()); #endif // NDEBUG /* Scan over write sets */ size_t offset(0); const gu::byte_t* buf(trx->write_set_buffer().first); const size_t buf_len(trx->write_set_buffer().second); while (offset < buf_len) { std::pair k(WriteSet::segment(buf, buf_len, offset)); // Scan over all keys offset = k.first; while (offset < k.first + k.second) { Key key(trx->version()); offset = unserialize(buf, buf_len, offset, key); if (certify_v1to2(trx, cert_index_, key, key_list, store_keys, log_conflicts_) == false) { goto cert_fail; } ++key_count; } // Skip data part std::pair d(WriteSet::segment(buf, buf_len, offset)); offset = d.first + d.second; } trx->set_depends_seqno(std::max(trx->depends_seqno(), last_pa_unsafe_)); if (store_keys == true) { for (TrxHandle::CertKeySet::iterator i(key_list.begin()); i != key_list.end();) { KeyEntry* const kel(i->first); CertIndex::const_iterator ci(cert_index_.find(kel)); if (ci == cert_index_.end()) { gu_throw_fatal << "could not find key '" << kel->get_key() << "' from cert index"; } KeyEntry* const ke(*ci); const bool full_key(i->second.first); const bool shared_key(i->second.second); bool keep(false); if (shared_key == false) { if ((full_key == false && ke->ref_trx() != trx) || (full_key == true && ke->ref_full_trx() != trx)) { ke->ref(trx, full_key); keep = true; } } else { if ((full_key == false && ke->ref_shared_trx() != trx) || (full_key == true && ke->ref_full_shared_trx() != trx)) { ke->ref_shared(trx, full_key); keep = true; } } if (keep) { ++i; } else { // this should not happen with Map, but with List is possible i = key_list.erase(i); if (kel != ke) delete kel; } } if (trx->pa_unsafe()) last_pa_unsafe_ = trx->global_seqno(); key_count_ += key_count; } cert_debug << "END CERTIFICATION (success): " << *trx; return TEST_OK; cert_fail: cert_debug << "END CERTIFICATION (failed): " << *trx; if (store_keys == true) { // Clean up key entries allocated for this trx for (TrxHandle::CertKeySet::iterator i(key_list.begin()); i != key_list.end(); ++i) { KeyEntry* const kel(i->first); // Clean up cert_index_ from entries which were added by this trx CertIndex::iterator ci(cert_index_.find(kel)); if (ci != cert_index_.end()) { KeyEntry* ke(*ci); if (ke->ref_trx() == 0 && ke->ref_shared_trx() == 0) { // kel was added to cert_index_ by this trx - // remove from cert_index_ and fall through to delete if (ke->get_key().flags() != kel->get_key().flags()) { // two copies of keys in key list, shared and exclusive, // skip the one which was not used to create key entry assert(key_list.find(ke) != key_list.end()); continue; } assert(ke->ref_full_trx() == 0); assert(ke->ref_full_shared_trx() == 0); assert(kel == ke); cert_index_.erase(ci); } else if (ke == kel) { // kel was added and is referenced by another trx - skip it continue; } // else kel != ke : kel is a duplicate of ke with different // flags, fall through to delete } else { assert(0); // we actually should never be here, the key should // be either added to cert_index_ or be there already log_warn << "could not find key '" << kel->get_key() << "' from cert index"; } assert(kel->ref_trx() == 0); assert(kel->ref_shared_trx() == 0); assert(kel->ref_full_trx() == 0); assert(kel->ref_full_shared_trx() == 0); delete kel; } assert(cert_index_.size() == prev_cert_index_size); } return TEST_FAILED; } galera::Certification::TestResult galera::Certification::do_test(TrxHandle* trx, bool store_keys) { if (trx->version() != version_) { log_info << "trx protocol version: " << trx->version() << " does not match certification protocol version: " << version_; return TEST_FAILED; } if (gu_unlikely(trx->last_seen_seqno() < initial_position_ || trx->global_seqno() - trx->last_seen_seqno() > max_length_)) { if (trx->last_seen_seqno() < initial_position_) { if (cert_index_.empty() == false) { log_warn << "last seen seqno below limit for trx " << *trx; } else { log_debug << "last seen seqno below limit for trx " << *trx; } } if (trx->global_seqno() - trx->last_seen_seqno() > max_length_) { log_warn << "certification interval for trx " << *trx << " exceeds the limit of " << max_length_; } return TEST_FAILED; } TestResult res(TEST_FAILED); switch (version_) { case 1: case 2: res = do_test_v1to2(trx, store_keys); break; default: gu_throw_fatal << "certification test for version " << version_ << " not implemented"; } if (store_keys == true && res == TEST_OK) { ++n_certified_; deps_dist_ += (trx->global_seqno() - trx->depends_seqno()); } return res; } galera::Certification::Certification(gu::Config& conf) : version_ (-1), trx_map_ (), cert_index_ (), deps_set_ (), mutex_ (), trx_size_warn_count_ (0), initial_position_ (-1), position_ (-1), safe_to_discard_seqno_ (-1), last_pa_unsafe_ (-1), n_certified_ (0), deps_dist_ (0), /* The defaults below are deliberately not reflected in conf: people * should not know about these dangerous setting unless they read RTFM. */ max_length_ (conf.get("cert.max_length", max_length_default)), max_length_check_ (conf.get("cert.max_length_check", max_length_check_default)), log_conflicts_ (false), key_count_ (0) { try // this is for unit tests where conf may lack some parameters { log_conflicts_ = conf.get(Param::log_conflicts); } catch (gu::NotFound& e) { conf.set(Param::log_conflicts, Defaults::log_conflicts); log_conflicts_ = conf.get(Param::log_conflicts); } } galera::Certification::~Certification() { log_info << "cert index usage at exit " << cert_index_.size(); log_info << "cert trx map usage at exit " << trx_map_.size(); log_info << "deps set usage at exit " << deps_set_.size(); log_info << "avg deps dist " << get_avg_deps_dist(); for_each(trx_map_.begin(), trx_map_.end(), PurgeAndDiscard(*this)); } void galera::Certification::assign_initial_position(wsrep_seqno_t seqno, int version) { switch (version) { // value -1 used in initialization when trx protocol version is not // available case -1: case 1: case 2: break; default: gu_throw_fatal << "certification/trx version " << version << " not supported"; } if (seqno >= position_) { std::for_each(trx_map_.begin(), trx_map_.end(), PurgeAndDiscard(*this)); assert(cert_index_.size() == 0); trx_map_.clear(); } else { log_warn << "moving position backwards: " << position_ << " -> " << seqno; std::for_each(cert_index_.begin(), cert_index_.end(), gu::DeleteObject()); std::for_each(trx_map_.begin(), trx_map_.end(), Unref2nd()); cert_index_.clear(); trx_map_.clear(); } log_info << "Assign initial position for certification: " << seqno << ", protocol version: " << version; gu::Lock lock(mutex_); initial_position_ = seqno; position_ = seqno; safe_to_discard_seqno_ = seqno; last_pa_unsafe_ = seqno; version_ = version; } galera::Certification::TestResult galera::Certification::append_trx(TrxHandle* trx) { // todo: enable when source id bug is fixed assert(trx->source_id() != WSREP_UUID_UNDEFINED); assert(trx->global_seqno() >= 0 && trx->local_seqno() >= 0); assert(trx->global_seqno() > position_); trx->ref(); { gu::Lock lock(mutex_); if (gu_unlikely(trx->global_seqno() != position_ + 1)) { // this is perfectly normal if trx is rolled back just after // replication, keeping the log though log_debug << "seqno gap, position: " << position_ << " trx seqno " << trx->global_seqno(); } if (gu_unlikely((trx->last_seen_seqno() + 1) < trx_map_.begin()->first)) { /* See #733 - for now it is false positive */ cert_debug << "WARNING: last_seen_seqno is below certification index: " << trx_map_.begin()->first << " > " << trx->last_seen_seqno(); } position_ = trx->global_seqno(); if (gu_unlikely(!(position_ & max_length_check_) && (trx_map_.size() > static_cast(max_length_)))) { log_debug << "trx map size: " << trx_map_.size() << " - check if status.last_committed is incrementing"; const wsrep_seqno_t trim_seqno(position_ - max_length_); const wsrep_seqno_t stds(get_safe_to_discard_seqno_()); if (trim_seqno > stds) { log_warn << "Attempt to trim certification index at " << trim_seqno << ", above safe-to-discard: " << stds; purge_trxs_upto_(stds); } else { cert_debug << "purging index up to " << trim_seqno; purge_trxs_upto_(trim_seqno); } } } const TestResult retval(test(trx)); gu::Lock lock(mutex_); if (trx_map_.insert( std::make_pair(trx->global_seqno(), trx)).second == false) gu_throw_fatal << "duplicate trx entry " << *trx; deps_set_.insert(trx->last_seen_seqno()); assert(deps_set_.size() <= trx_map_.size()); trx->mark_certified(); return retval; } galera::Certification::TestResult galera::Certification::test(TrxHandle* trx, bool bval) { assert(trx->global_seqno() >= 0 && trx->local_seqno() >= 0); const TestResult ret(do_test(trx, bval)); if (gu_unlikely(ret != TEST_OK)) { // make sure that last depends seqno is -1 for trxs that failed // certification trx->set_depends_seqno(WSREP_SEQNO_UNDEFINED); } return ret; } wsrep_seqno_t galera::Certification::get_safe_to_discard_seqno_() const { wsrep_seqno_t retval; if (deps_set_.empty() == true) { retval = safe_to_discard_seqno_; } else { retval = (*deps_set_.begin()) - 1; } return retval; } void galera::Certification::purge_trxs_upto_(wsrep_seqno_t seqno) { TrxMap::iterator lower_bound(trx_map_.lower_bound(seqno)); cert_debug << "purging index up to " << lower_bound->first; for_each(trx_map_.begin(), lower_bound, PurgeAndDiscard(*this)); trx_map_.erase(trx_map_.begin(), lower_bound); if (0 == ((trx_map_.size() + 1) % 10000)) { log_debug << "trx map after purge: length: " << trx_map_.size() << ", purge seqno " << seqno; } } wsrep_seqno_t galera::Certification::set_trx_committed(TrxHandle* trx) { assert(trx->global_seqno() >= 0 && trx->local_seqno() >= 0 && trx->is_committed() == false); wsrep_seqno_t ret(-1); { gu::Lock lock(mutex_); if (trx->is_certified() == true) { // trxs with depends_seqno == -1 haven't gone through // append_trx DepsSet::iterator i(deps_set_.find(trx->last_seen_seqno())); assert(i != deps_set_.end()); if (deps_set_.size() == 1) safe_to_discard_seqno_ = *i; deps_set_.erase(i); } if (gu_unlikely(index_purge_required())) { ret = get_safe_to_discard_seqno_(); } } trx->mark_committed(); trx->clear(); return ret; } galera::TrxHandle* galera::Certification::get_trx(wsrep_seqno_t seqno) { gu::Lock lock(mutex_); TrxMap::iterator i(trx_map_.find(seqno)); if (i == trx_map_.end()) return 0; i->second->ref(); return i->second; } void galera::Certification::set_log_conflicts(const std::string& str) { try { bool const old(log_conflicts_); log_conflicts_ = gu::from_string(str); if (old != log_conflicts_) { log_info << (log_conflicts_ ? "Enabled" : "Disabled") << " logging of certification conflicts."; } } catch (gu::NotFound& e) { gu_throw_error(EINVAL) << "Bad value '" << str << "' for boolean parameter '" << Param::log_conflicts << '\''; } } percona-xtradb-cluster-galera/galera/src/certification.hpp0000644000000000000000000001321212247075736024227 0ustar rootroot00000000000000// // Copyright (C) 2010-2012 Codership Oy // #ifndef GALERA_CERTIFICATION_HPP #define GALERA_CERTIFICATION_HPP #include "trx_handle.hpp" #include "gu_unordered.hpp" #include "gu_lock.hpp" #include "gu_config.hpp" #include #include #include namespace galera { class Certification { public: struct Param { #define CERTIFICATION_PARAM_LOG_CONFLICTS_STR "cert.log_conflicts" static const std::string log_conflicts; }; struct Defaults { #define CERTIFICATION_DEFAULTS_LOG_CONFLICTS_STR "no" static const std::string log_conflicts; }; typedef gu::UnorderedSet CertIndex; private: typedef std::multiset DepsSet; typedef std::map TrxMap; public: typedef enum { TEST_OK, TEST_FAILED } TestResult; Certification(gu::Config& conf); ~Certification(); void assign_initial_position(wsrep_seqno_t seqno, int versiono); TestResult append_trx(TrxHandle*); TestResult test(TrxHandle*, bool = true); wsrep_seqno_t position() const { return position_; } wsrep_seqno_t get_safe_to_discard_seqno() const { gu::Lock lock(mutex_); return get_safe_to_discard_seqno_(); } void purge_trxs_upto(wsrep_seqno_t seqno) { gu::Lock lock(mutex_); const wsrep_seqno_t stds(get_safe_to_discard_seqno_()); // assert(seqno <= get_safe_to_discard_seqno()); // Note: setting trx committed is not done in total order so // safe to discard seqno may decrease. Enable assertion above when // this issue is fixed. purge_trxs_upto_(std::min(seqno, stds)); } // Set trx corresponding to handle committed. Return purge seqno if // index purge is required, -1 otherwise. wsrep_seqno_t set_trx_committed(TrxHandle*); TrxHandle* get_trx(wsrep_seqno_t); // statistics section double get_avg_deps_dist() const { gu::Lock lock(mutex_); return (n_certified_ == 0 ? 0 : double(deps_dist_)/n_certified_); } size_t index_size() const { gu::Lock lock(mutex_); return cert_index_.size(); } bool index_purge_required() { register long const count(key_count_.fetch_and_zero()); return ((count > Certification::purge_interval_) || (key_count_ += count /* restore count */, false)); } void set_log_conflicts(const std::string& str); private: TestResult do_test(TrxHandle*, bool); TestResult do_test_v0(TrxHandle*, bool); TestResult do_test_v1to2(TrxHandle*, bool); void purge_for_trx(TrxHandle*); // unprotected variants for internal use wsrep_seqno_t get_safe_to_discard_seqno_() const; void purge_trxs_upto_(wsrep_seqno_t); class PurgeAndDiscard { public: PurgeAndDiscard(Certification& cert) : cert_(cert) { } void operator()(TrxMap::value_type& vt) const { { TrxHandle* trx(vt.second); TrxHandleLock lock(*trx); if (trx->is_committed() == false) { log_warn << "trx not committed in purge and discard: " << *trx; } if (trx->depends_seqno() > -1) { cert_.purge_for_trx(trx); cert_.n_certified_--; cert_.deps_dist_ -= (trx->global_seqno() - trx->depends_seqno()); } if (trx->refcnt() > 1) { log_debug << "trx " << trx->trx_id() << " refcnt " << trx->refcnt(); } } vt.second->unref(); } PurgeAndDiscard(const PurgeAndDiscard& other) : cert_(other.cert_) { } private: void operator=(const PurgeAndDiscard&); Certification& cert_; }; int version_; TrxMap trx_map_; CertIndex cert_index_; DepsSet deps_set_; gu::Mutex mutex_; size_t trx_size_warn_count_; wsrep_seqno_t initial_position_; wsrep_seqno_t position_; wsrep_seqno_t safe_to_discard_seqno_; wsrep_seqno_t last_pa_unsafe_; size_t n_certified_; wsrep_seqno_t deps_dist_; /* The only reason those are not static constants is because * there might be a need to thange them without recompilation. * see #454 */ long const max_length_; /* Purge trx_map_ when it exceeds this * NOTE: this effectively sets a limit * on trx certification interval */ static long const max_length_default; unsigned long const max_length_check_; /* Mask how often to check */ static unsigned long const max_length_check_default; bool log_conflicts_; static long const purge_interval_ = (1UL<<10); gu::Atomic key_count_; }; } #endif // GALERA_CERTIFICATION_HPP percona-xtradb-cluster-galera/galera/src/fsm.hpp0000644000000000000000000001337712247075736022205 0ustar rootroot00000000000000// // Copyright (C) 2010 Codership Oy // #ifndef GALERA_FSM_HPP #define GALERA_FSM_HPP #include "gu_unordered.hpp" #include "gu_throw.hpp" #include #include namespace galera { class EmptyGuard { public: bool operator()() const { return true; } }; class EmptyAction { public: void operator()() { } }; template class FSM { public: class TransAttr { public: TransAttr() : pre_guard_(0), post_guard_(0), pre_action_(0), post_action_(0) { } std::list pre_guard_; std::list post_guard_; std::list pre_action_; std::list post_action_; }; typedef gu::UnorderedMap TransMap; FSM(State const initial_state) : delete_(true), trans_map_(new TransMap), state_(initial_state), state_hist_() { } FSM(TransMap* const trans_map, State const initial_state) : delete_(false), trans_map_(trans_map), state_(initial_state), state_hist_() { } ~FSM() { if (delete_ == true) delete trans_map_; } void shift_to(State const state) { typename TransMap::iterator i(trans_map_->find(Transition(state_, state))); if (i == trans_map_->end()) { log_fatal << "FSM: no such a transition " << state_ << " -> " << state; // gu_throw_fatal << "FSM: no such a transition " // << state_ << " -> " << state; abort(); // we want to catch it in the stack } typename std::list::const_iterator gi; for (gi = i->second.pre_guard_.begin(); gi != i->second.pre_guard_.end(); ++gi) { if ((*gi)() == false) { log_fatal << "FSM: pre guard failed for " << state_ << " -> " << state; gu_throw_fatal << "FSM: pre guard failed for " << state_ << " -> " << state; } } typename std::list::iterator ai; for (ai = i->second.pre_action_.begin(); ai != i->second.pre_action_.end(); ++ai) { (*ai)(); } state_hist_.push_back(state_); state_ = state; for (ai = i->second.post_action_.begin(); ai != i->second.post_action_.end(); ++ai) { (*ai)(); } for (gi = i->second.post_guard_.begin(); gi != i->second.post_guard_.end(); ++gi) { if ((*gi)() == false) { log_fatal << "FSM: post guard failed for " << state_ << " -> " << state; gu_throw_fatal << "FSM: post guard failed for " << state_ << " -> " << state; } } } const State& operator()() const { return state_; } void add_transition(Transition const& trans) { if (trans_map_->insert( std::make_pair(trans, TransAttr())).second == false) { gu_throw_fatal << "transition " << trans.from() << " -> " << trans.to() << " already exists"; } } void add_pre_guard(Transition const& trans, Guard const& guard) { typename TransMap::iterator i(trans_map_->find(trans)); if (i == trans_map_->end()) { gu_throw_fatal << "no such a transition " << trans.from() << " -> " << trans.to(); } i->second.pre_guard_.push_back(guard); } void add_post_guard(Transition const& trans, Guard const& guard) { typename TransMap::iterator i(trans_map_->find(trans)); if (i == trans_map_->end()) { gu_throw_fatal << "no such a transition " << trans.from() << " -> " << trans.to(); } i->second.post_guard_.push_back(guard); } void add_pre_action(Transition const& trans, Action const& action) { typename TransMap::iterator i(trans_map_->find(trans)); if (i == trans_map_->end()) { gu_throw_fatal << "no such a transition " << trans.from() << " -> " << trans.to(); } i->second.pre_action_.push_back(action); } void add_post_action(Transition const& trans, Action const& action) { typename TransMap::iterator i(trans_map_->find(trans)); if (i == trans_map_->end()) { gu_throw_fatal << "no such a transition " << trans.from() << " -> " << trans.to(); } i->second.post_action_.push_back(action); } private: FSM(const FSM&); void operator=(const FSM&); bool delete_; TransMap* const trans_map_; State state_; std::vector state_hist_; }; } #endif // GALERA_FSM_HPP percona-xtradb-cluster-galera/galera/src/galera_common.hpp0000644000000000000000000000227012247075736024211 0ustar rootroot00000000000000/* * Copyright (C) 2012 Codership Oy */ /*! * @file common.hpp * * @brief Imports definitions from the global common.h */ #ifndef GALERA_COMMON_HPP #define GALERA_COMMON_HPP #if defined(HAVE_COMMON_H) #include #endif #include namespace galera { #if defined(HAVE_COMMON_H) static std::string const TCP_SCHEME(COMMON_TCP_SCHEME); static std::string const UDP_SCHEME(COMMON_UDP_SCHEME); static std::string const SSL_SCHEME(COMMON_SSL_SCHEME); static std::string const BASE_PORT_KEY(COMMON_BASE_PORT_KEY); static std::string const BASE_PORT_DEFAULT(COMMON_BASE_PORT_DEFAULT); static std::string const BASE_HOST_KEY(COMMON_BASE_HOST_KEY); static std::string const GALERA_STATE_FILE(COMMON_STATE_FILE); #else static std::string const TCP_SCHEME("tcp"); static std::string const UDP_SCHEME("udp"); static std::string const SSL_SCHEME("ssl"); static std::string const BASE_PORT_KEY("base_port"); static std::string const BASE_PORT_DEFAULT("4567"); static std::string const BASE_HOST_KEY("base_host"); static std::string const GALERA_STATE_FILE("grastate.dat"); #endif } #endif /* GALERA_COMMON_HPP */ percona-xtradb-cluster-galera/galera/src/galera_exception.hpp0000644000000000000000000000312012247075736024712 0ustar rootroot00000000000000// // Copyright (C) 2010 Codership Oy // #ifndef GALERA_EXCEPTION_HPP #define GALERA_EXCEPTION_HPP #include "galerautils.hpp" #include "wsrep_api.h" namespace galera { /*! * An exception to handle applier errors and avoid confusing wsrep error codes * with the standard ones */ class ApplyException : public gu::Exception { public: ApplyException (const std::string& msg, int err) : gu::Exception (msg, err) { if (err < 0) // sanity check { log_fatal << "Attempt to throw exception with a " << err << " code"; abort(); } } /* this is just int because we must handle any positive value */ int status () { return get_errno(); } }; static inline const char* wsrep_status_str(wsrep_status_t& status) { switch (status) { case WSREP_OK: return "WSREP_OK"; case WSREP_WARNING: return "WSREP_WARNING"; case WSREP_TRX_MISSING: return "WSREP_TRX_MISSING"; case WSREP_TRX_FAIL: return "WSREP_TRX_FAIL"; case WSREP_BF_ABORT: return "WSREP_BF_ABORT"; case WSREP_CONN_FAIL: return "WSREP_CONN_FAIL"; case WSREP_NODE_FAIL: return "WSREP_NODE_FAIL"; case WSREP_FATAL: return "WSREP_FATAL"; case WSREP_NOT_IMPLEMENTED: return "WSREP_NOT_IMPLEMENTED"; default: return "(unknown code)"; } } /*! * And exception to handle replication errors */ class ReplException : public gu::Exception { public: ReplException (const std::string& msg, int err) : gu::Exception (msg, err) {} }; } #endif /* GALERA_EXCEPTION_HPP */ percona-xtradb-cluster-galera/galera/src/galera_info.c0000644000000000000000000000407712247075736023316 0ustar rootroot00000000000000// Copyright (C) 2009 Codership Oy #include "galera_info.h" #include #include static size_t view_info_size (int members) { return (sizeof(wsrep_view_info_t) + members * sizeof(wsrep_member_info_t)); } /* create view info out of configuration message */ wsrep_view_info_t* galera_view_info_create (const gcs_act_conf_t* conf, bool st_required) { wsrep_view_info_t* ret = malloc(view_info_size(conf->memb_num)); if (ret) { const char* str = conf->data; int m; wsrep_uuid_t uuid = *(wsrep_uuid_t*)&conf->uuid; wsrep_seqno_t seqno = conf->seqno != GCS_SEQNO_ILL ? conf->seqno : WSREP_SEQNO_UNDEFINED; wsrep_gtid_t gtid = { uuid, seqno }; ret->state_id = gtid; ret->view = conf->conf_id; ret->status = conf->conf_id != -1 ? WSREP_VIEW_PRIMARY : WSREP_VIEW_NON_PRIMARY; ret->state_gap = st_required; ret->my_idx = conf->my_idx; ret->memb_num = conf->memb_num; ret->proto_ver = conf->appl_proto_ver; for (m = 0; m < ret->memb_num; m++) { wsrep_member_info_t* member = &ret->members[m]; size_t id_len = strlen(str); gu_uuid_scan (str, id_len, (gu_uuid_t*)&member->id); str = str + id_len + 1; strncpy(member->name, str, sizeof(member->name) - 1); member->name[sizeof(member->name) - 1] = '\0'; str = str + strlen(str) + 1; strncpy(member->incoming, str, sizeof(member->incoming) - 1); member->incoming[sizeof(member->incoming) - 1] = '\0'; str = str + strlen(str) + 1; } } return ret; } /* make a copy of view info object */ wsrep_view_info_t* galera_view_info_copy (const wsrep_view_info_t* vi) { size_t ret_size = view_info_size (vi->memb_num); wsrep_view_info_t* ret = malloc (ret_size); if (ret) { memcpy (ret, vi, ret_size); } return ret; } percona-xtradb-cluster-galera/galera/src/galera_info.h0000644000000000000000000000070012247075736023310 0ustar rootroot00000000000000// Copyright (C) 2009 Codership Oy #ifndef __GALERA_INFO_H__ #define __GALERA_INFO_H__ #include #include "wsrep_api.h" /* create view info out of configuration message */ extern wsrep_view_info_t* galera_view_info_create (const gcs_act_conf_t* conf, bool st_required); /* make a copy of view info object */ extern wsrep_view_info_t* galera_view_info_copy (const wsrep_view_info_t* vi); #endif // __GALERA_INFO_H__ percona-xtradb-cluster-galera/galera/src/galera_service_thd.cpp0000644000000000000000000000427312247075736025220 0ustar rootroot00000000000000/* * Copyright (C) 2010 Codership Oy */ #include "galera_service_thd.hpp" const uint32_t galera::ServiceThd::A_NONE = 0; static const uint32_t A_LAST_COMMITTED = 1 << 0; static const uint32_t A_EXIT = 1 << 31; void* galera::ServiceThd::thd_func (void* arg) { galera::ServiceThd* st = reinterpret_cast(arg); bool exit = false; while (!exit) { galera::ServiceThd::Data data; { gu::Lock lock(st->mtx_); if (A_NONE == st->data_.act_) lock.wait(st->cond_); data = st->data_; st->data_.act_ = A_NONE; // clear pending actions } exit = ((data.act_ & A_EXIT)); if (!exit) { if (data.act_ & A_LAST_COMMITTED) { ssize_t const ret(st->gcs_.set_last_applied(data.last_committed_)); if (gu_unlikely(ret < 0)) { log_warn << "Failed to report last committed " << data.last_committed_ << ", " << ret << " (" << strerror (-ret) << ')'; // @todo: figure out what to do in this case } else { log_debug << "Reported last committed: " << data.last_committed_; } } } } return 0; } galera::ServiceThd::ServiceThd (GcsI& gcs) : gcs_ (gcs), thd_ (), mtx_ (), cond_ (), data_ () { gu_thread_create (&thd_, NULL, thd_func, this); } galera::ServiceThd::~ServiceThd () { { gu::Lock lock(mtx_); data_.act_ |= A_EXIT; cond_.signal(); } gu_thread_join(thd_, NULL); } void galera::ServiceThd::report_last_committed(gcs_seqno_t seqno) { gu::Lock lock(mtx_); if (data_.last_committed_ < seqno) { data_.last_committed_ = seqno; if (!(data_.act_ & A_LAST_COMMITTED)) { data_.act_ |= A_LAST_COMMITTED; cond_.signal(); } } } void galera::ServiceThd::reset() { gu::Lock lock(mtx_); data_.act_ = A_NONE; data_.last_committed_ = 0; } percona-xtradb-cluster-galera/galera/src/galera_service_thd.hpp0000644000000000000000000000171512247075736025223 0ustar rootroot00000000000000/* * Copyright (C) 2010 Codership Oy */ #ifndef GALERA_SERVICE_THD_HPP #define GALERA_SERVICE_THD_HPP #include #include #include "gcs.hpp" namespace galera { class ServiceThd { public: ServiceThd (GcsI& gcs); ~ServiceThd (); void report_last_committed (gcs_seqno_t seqno); void reset(); // reset to initial state before gcs (re)connect private: GcsI& gcs_; gu_thread_t thd_; gu::Mutex mtx_; gu::Cond cond_; static const uint32_t A_NONE; struct Data { uint32_t act_; gcs_seqno_t last_committed_; Data() : act_(A_NONE), last_committed_(0) {} }; Data data_; static void* thd_func (void*); ServiceThd (const ServiceThd&); ServiceThd& operator= (const ServiceThd&); }; } #endif /* GALERA_SERVICE_THD_HPP */ percona-xtradb-cluster-galera/galera/src/gcs.hpp0000644000000000000000000002366412247075736022174 0ustar rootroot00000000000000// // Copyright (C) 2010-2013 Codership Oy // #ifndef GALERA_GCS_HPP #define GALERA_GCS_HPP #include "gu_atomic.hpp" #include "gu_throw.hpp" #include "gu_config.hpp" #include "gcs.h" #include "wsrep_api.h" #include #include #define GCS_IMPL Gcs namespace galera { class GcsI { public: GcsI() {} virtual ~GcsI() {} virtual ssize_t connect(const std::string& cluster_name, const std::string& cluster_url, bool bootstrap) = 0; virtual ssize_t set_initial_position(const wsrep_uuid_t& uuid, gcs_seqno_t seqno) = 0; virtual void close() = 0; virtual ssize_t recv(gcs_action& act) = 0; virtual ssize_t send(const void*, size_t, gcs_act_type_t, bool) = 0; virtual ssize_t repl(gcs_action& act, bool) = 0; virtual gcs_seqno_t caused() = 0; virtual ssize_t schedule() = 0; virtual ssize_t interrupt(ssize_t) = 0; virtual ssize_t resume_recv() = 0; virtual ssize_t set_last_applied(gcs_seqno_t) = 0; virtual ssize_t request_state_transfer(const void* req, ssize_t req_len, const std::string& sst_donor, gcs_seqno_t* seqno_l) = 0; virtual ssize_t desync(gcs_seqno_t* seqno_l) = 0; virtual void join(gcs_seqno_t seqno) = 0; virtual void get_stats(gcs_stats*) const = 0; /*! @throws NotFound */ virtual void param_set (const std::string& key, const std::string& value) = 0; /*! @throws NotFound */ virtual char* param_get (const std::string& key) const = 0; virtual size_t max_action_size() const = 0; }; class Gcs : public GcsI { public: Gcs(gu::Config& config, gcache::GCache& cache, int repl_proto_ver = 0, int appl_proto_ver = 0, const char* node_name = 0, const char* node_incoming = 0) : conn_(gcs_create(reinterpret_cast(&config), reinterpret_cast(&cache), node_name, node_incoming, repl_proto_ver, appl_proto_ver)) { log_info << "Passing config to GCS: " << config; if (conn_ == 0) gu_throw_fatal << "could not create gcs connection"; } ~Gcs() { gcs_destroy(conn_); } ssize_t connect(const std::string& cluster_name, const std::string& cluster_url, bool const bootstrap) { return gcs_open(conn_, cluster_name.c_str(), cluster_url.c_str(), bootstrap); } ssize_t set_initial_position(const wsrep_uuid_t& uuid, gcs_seqno_t seqno) { return gcs_init(conn_, seqno, uuid.data); } void close() { gcs_close(conn_); } ssize_t recv(struct gcs_action& act) { return gcs_recv(conn_, &act); } ssize_t send(const void* act, size_t act_len, gcs_act_type_t act_type, bool scheduled) { return gcs_send(conn_, act, act_len, act_type, scheduled); } ssize_t repl(struct gcs_action& act, bool scheduled) { return gcs_repl(conn_, &act, scheduled); } gcs_seqno_t caused() { return gcs_caused(conn_); } ssize_t schedule() { return gcs_schedule(conn_); } ssize_t interrupt(ssize_t handle) { return gcs_interrupt(conn_, handle); } ssize_t resume_recv() { return gcs_resume_recv(conn_); } ssize_t set_last_applied(gcs_seqno_t last_applied) { return gcs_set_last_applied(conn_, last_applied); } ssize_t request_state_transfer(const void* req, ssize_t req_len, const std::string& sst_donor, gcs_seqno_t* seqno_l) { return gcs_request_state_transfer(conn_, req, req_len, sst_donor.c_str(), seqno_l); } ssize_t desync (gcs_seqno_t* seqno_l) { return gcs_desync(conn_, seqno_l); } void join (gcs_seqno_t seqno) { long const err(gcs_join(conn_, seqno)); if (err < 0) { gu_throw_error (-err) << "gcs_join(" << seqno << ") failed"; } } void get_stats(gcs_stats* stats) const { return gcs_get_stats(conn_, stats); } void param_set (const std::string& key, const std::string& value) { long ret = gcs_param_set (conn_, key.c_str(), value.c_str()); if (1 == ret) { throw gu::NotFound(); } else if (ret) { gu_throw_error(-ret) << "Setting '" << key << "' to '" << value << "' failed"; } } char* param_get (const std::string& key) const { gu_throw_error(ENOSYS) << "Not implemented: " << __FUNCTION__; return 0; } size_t max_action_size() const { return GCS_MAX_ACT_SIZE; } private: Gcs(const Gcs&); void operator=(const Gcs&); gcs_conn_t* conn_; }; class DummyGcs : public GcsI { public: DummyGcs(gu::Config& config, gcache::GCache& cache, int repl_proto_ver = 0, int appl_proto_ver = 0, const char* node_name = 0, const char* node_incoming = 0); DummyGcs(); // for unit tests ~DummyGcs(); ssize_t connect(const std::string& cluster_name, const std::string& cluster_url, bool bootstrap); ssize_t set_initial_position(const wsrep_uuid_t& uuid, gcs_seqno_t seqno); void close(); ssize_t recv(gcs_action& act); ssize_t send(const void*, size_t, gcs_act_type_t, bool) { return -ENOSYS; } ssize_t repl(gcs_action& act, bool scheduled) { act.seqno_g = GCS_SEQNO_ILL; act.seqno_l = GCS_SEQNO_ILL; ssize_t ret(-EBADFD); { gu::Lock lock(mtx_); switch (state_) { case S_CONNECTED: case S_SYNCED: { ++global_seqno_; act.seqno_g = global_seqno_; ++local_seqno_; act.seqno_l = local_seqno_; ret = act.size; break; } case S_CLOSED: ret = -EBADFD; break; case S_OPEN: ret = -ENOTCONN; break; } } if (gu_likely(0 != gcache_ && ret > 0)) { assert (ret == act.size); void* ptr = gcache_->malloc(act.size); memcpy (ptr, act.buf, act.size); act.buf = ptr; } return ret; } gcs_seqno_t caused() { return global_seqno_; } ssize_t schedule() { return 1; } ssize_t interrupt(ssize_t handle); ssize_t resume_recv() { return 0; } ssize_t set_last_applied(gcs_seqno_t last_applied) { gu::Lock lock(mtx_); last_applied_ = last_applied; report_last_applied_ = true; cond_.signal(); return 0; } gcs_seqno_t last_applied() const { return last_applied_; } ssize_t request_state_transfer(const void* req, ssize_t req_len, const std::string& sst_donor, gcs_seqno_t* seqno_l) { *seqno_l = GCS_SEQNO_ILL; return -ENOSYS; } ssize_t desync (gcs_seqno_t* seqno_l) { *seqno_l = GCS_SEQNO_ILL; return -ENOTCONN; } void join(gcs_seqno_t seqno) { gu_throw_error(ENOTCONN); } void get_stats(gcs_stats* stats) const { memset (stats, 0, sizeof(*stats)); } void param_set (const std::string& key, const std::string& value) {} char* param_get (const std::string& key) const { return 0; } size_t max_action_size() const { return 0x7FFFFFFF; } private: typedef enum { S_CLOSED, S_OPEN, S_CONNECTED, S_SYNCED } conn_state_t; ssize_t generate_seqno_action (gcs_action& act, gcs_act_type_t type); ssize_t generate_cc (bool primary); gu::Config* gconf_; gcache::GCache* gcache_; gu::Mutex mtx_; gu::Cond cond_; gcs_seqno_t global_seqno_; gcs_seqno_t local_seqno_; gu_uuid_t uuid_; gcs_seqno_t last_applied_; conn_state_t state_; gu::Lock* schedule_; void* cc_; ssize_t cc_size_; std::string const my_name_; std::string const incoming_; int repl_proto_ver_; int appl_proto_ver_; bool report_last_applied_; DummyGcs (const DummyGcs&); DummyGcs& operator=(const DummyGcs&); }; } #endif // GALERA_GCS_HPP percona-xtradb-cluster-galera/galera/src/gcs_action_source.cpp0000644000000000000000000001070112247075736025070 0ustar rootroot00000000000000// // Copyright (C) 2010-2013 Codership Oy // #include "replicator.hpp" #include "gcs_action_source.hpp" #include "trx_handle.hpp" #include "gu_serialize.hpp" extern "C" { #include "galera_info.h" } #include // Exception-safe way to release action pointer when it goes out // of scope class Release { public: Release(struct gcs_action& act, gcache::GCache& gcache) : act_(act), gcache_(gcache) { } ~Release() { switch (act_.type) { case GCS_ACT_TORDERED: case GCS_ACT_STATE_REQ: gcache_.free(act_.buf); break; default: free(const_cast(act_.buf)); break; } } private: struct gcs_action& act_; gcache::GCache& gcache_; }; static galera::Replicator::State state2repl(const gcs_act_conf_t& conf) { switch (conf.my_state) { case GCS_NODE_STATE_NON_PRIM: if (conf.my_idx >= 0) return galera::Replicator::S_CONNECTED; else return galera::Replicator::S_CLOSING; case GCS_NODE_STATE_PRIM: return galera::Replicator::S_CONNECTED; case GCS_NODE_STATE_JOINER: return galera::Replicator::S_JOINING; case GCS_NODE_STATE_JOINED: return galera::Replicator::S_JOINED; case GCS_NODE_STATE_SYNCED: return galera::Replicator::S_SYNCED; case GCS_NODE_STATE_DONOR: return galera::Replicator::S_DONOR; default: gu_throw_fatal << "unhandled gcs state: " << conf.my_state; } } galera::GcsActionTrx::GcsActionTrx(const struct gcs_action& act) : trx_(new TrxHandle()) { assert(act.seqno_l != GCS_SEQNO_ILL); assert(act.seqno_g != GCS_SEQNO_ILL); const gu::byte_t* const buf = reinterpret_cast(act.buf); size_t offset(unserialize(buf, act.size, 0, *trx_)); // trx_->append_write_set(buf + offset, act.size - offset); trx_->set_write_set_buffer(buf + offset, act.size - offset); trx_->set_received(act.buf, act.seqno_l, act.seqno_g); trx_->lock(); } galera::GcsActionTrx::~GcsActionTrx() { assert(trx_->refcnt() >= 1); trx_->unlock(); trx_->unref(); } void galera::GcsActionSource::dispatch(void* const recv_ctx, const struct gcs_action& act, bool& exit_loop) { assert(recv_ctx != 0); assert(act.buf != 0); assert(act.seqno_l > 0); switch (act.type) { case GCS_ACT_TORDERED: { assert(act.seqno_g > 0); GcsActionTrx trx(act); trx.trx()->set_state(TrxHandle::S_REPLICATING); replicator_.process_trx(recv_ctx, trx.trx()); exit_loop = trx.trx()->exit_loop(); // this is the end of trx lifespan break; } case GCS_ACT_COMMIT_CUT: { wsrep_seqno_t seq; gu::unserialize8(reinterpret_cast(act.buf), act.size, 0, seq); replicator_.process_commit_cut(seq, act.seqno_l); break; } case GCS_ACT_CONF: { const gcs_act_conf_t* conf( reinterpret_cast(act.buf) ); wsrep_view_info_t* view_info( galera_view_info_create(conf, conf->my_state == GCS_NODE_STATE_PRIM) ); replicator_.process_conf_change(recv_ctx, *view_info, conf->repl_proto_ver, state2repl(*conf), act.seqno_l); free(view_info); break; } case GCS_ACT_STATE_REQ: replicator_.process_state_req(recv_ctx, act.buf, act.size, act.seqno_l, act.seqno_g); break; case GCS_ACT_JOIN: { wsrep_seqno_t seq; gu::unserialize8(reinterpret_cast(act.buf), act.size, 0, seq); replicator_.process_join(seq, act.seqno_l); break; } case GCS_ACT_SYNC: replicator_.process_sync(act.seqno_l); break; default: gu_throw_fatal << "unrecognized action type: " << act.type; } } ssize_t galera::GcsActionSource::process(void* recv_ctx, bool& exit_loop) { struct gcs_action act; ssize_t rc(gcs_.recv(act)); if (rc > 0) { Release release(act, gcache_); ++received_; received_bytes_ += rc; dispatch(recv_ctx, act, exit_loop); } return rc; } percona-xtradb-cluster-galera/galera/src/gcs_action_source.hpp0000644000000000000000000000303012247075736025072 0ustar rootroot00000000000000// // Copyright (C) 2010-2013 Codership Oy // #ifndef GALERA_GCS_ACTION_SOURCE_HPP #define GALERA_GCS_ACTION_SOURCE_HPP #include "action_source.hpp" #include "gcs.hpp" #include "replicator.hpp" #include "GCache.hpp" #include "gu_atomic.hpp" namespace galera { class GcsActionSource : public galera::ActionSource { public: GcsActionSource(GCS_IMPL& gcs, Replicator& replicator, gcache::GCache& gcache) : gcs_ (gcs ), replicator_ (replicator), gcache_ (gcache ), received_ (0 ), received_bytes_(0 ) { } ~GcsActionSource() { } ssize_t process(void*, bool& exit_loop); long long received() const { return received_(); } long long received_bytes() const { return received_bytes_(); } private: void dispatch(void*, const gcs_action&, bool& exit_loop); GCS_IMPL& gcs_; Replicator& replicator_; gcache::GCache& gcache_; gu::Atomic received_; gu::Atomic received_bytes_; }; class GcsActionTrx { public: GcsActionTrx(const struct gcs_action& act); ~GcsActionTrx(); TrxHandle* trx() const { return trx_; } private: GcsActionTrx(const GcsActionTrx&); void operator=(const GcsActionTrx&); TrxHandle* trx_; }; } #endif // GALERA_GCS_ACTION_SOURCE_HPP percona-xtradb-cluster-galera/galera/src/gcs_dummy.cpp0000644000000000000000000001447512247075736023402 0ustar rootroot00000000000000// // Copyright (C) 2011-2012 Codership Oy // #include "gcs.hpp" namespace galera { DummyGcs::DummyGcs(gu::Config& config, gcache::GCache& cache, int repl_proto_ver, int appl_proto_ver, const char* node_name, const char* node_incoming) : gconf_ (&config), gcache_ (&cache), mtx_ (), cond_ (), global_seqno_ (0), local_seqno_ (0), uuid_ (), last_applied_ (GCS_SEQNO_ILL), state_ (S_OPEN), schedule_ (0), cc_ (0), cc_size_ (0), my_name_ (node_name ? node_name : "not specified"), incoming_ (node_incoming ? node_incoming : "not given"), repl_proto_ver_(repl_proto_ver), appl_proto_ver_(appl_proto_ver), report_last_applied_(false) { gu_uuid_generate (&uuid_, 0, 0); } DummyGcs::DummyGcs() : gconf_ (0), gcache_ (0), mtx_ (), cond_ (), global_seqno_ (0), local_seqno_ (0), uuid_ (), last_applied_ (GCS_SEQNO_ILL), state_ (S_OPEN), schedule_ (0), cc_ (0), cc_size_ (0), my_name_ ("not specified"), incoming_ ("not given"), repl_proto_ver_(1), appl_proto_ver_(1), report_last_applied_(false) { gu_uuid_generate (&uuid_, 0, 0); } DummyGcs::~DummyGcs() { gu::Lock lock(mtx_); assert(0 == schedule_); if (cc_) { assert (cc_size_ > 0); ::free(cc_); } } ssize_t DummyGcs::generate_cc (bool primary) { cc_size_ = sizeof(gcs_act_conf_t) + primary * (my_name_.length() + incoming_.length() + GU_UUID_STR_LEN + 3); cc_ = ::malloc(cc_size_); if (!cc_) { cc_size_ = 0; return -ENOMEM; } gcs_act_conf_t* const cc(reinterpret_cast(cc_)); if (primary) { cc->seqno = global_seqno_; cc->conf_id = 1; memcpy (cc->uuid, &uuid_, sizeof(uuid_)); cc->memb_num = 1; cc->my_idx = 0; cc->my_state = GCS_NODE_STATE_JOINED; cc->repl_proto_ver = repl_proto_ver_; cc->appl_proto_ver = appl_proto_ver_; char* const str(cc->data); ssize_t offt(0); offt += gu_uuid_print (&uuid_, str, GU_UUID_STR_LEN+1) + 1; offt += sprintf (str + offt, "%s", my_name_.c_str()) + 1; sprintf (str + offt, "%s", incoming_.c_str()); } else { cc->seqno = GCS_SEQNO_ILL; cc->conf_id = -1; cc->memb_num = 0; cc->my_idx = -1; cc->my_state = GCS_NODE_STATE_NON_PRIM; } return cc_size_; } ssize_t DummyGcs::connect(const std::string& cluster_name, const std::string& cluster_url, bool bootstrap) { gu::Lock lock(mtx_); ssize_t ret = generate_cc (true); if (ret > 0) { // state_ = S_CONNECTED; cond_.signal(); ret = 0; } return ret; } ssize_t DummyGcs::set_initial_position(const wsrep_uuid_t& uuid, gcs_seqno_t seqno) { gu::Lock lock(mtx_); if (memcmp(&uuid, &GU_UUID_NIL, sizeof(wsrep_uuid_t)) && seqno >= 0) { uuid_ = *(reinterpret_cast(&uuid)); global_seqno_ = seqno; } return 0; } void DummyGcs::close() { log_info << "Closing DummyGcs"; gu::Lock lock(mtx_); generate_cc (false); // state_ = S_CLOSED; cond_.broadcast(); // usleep(100000); // 0.1s } ssize_t DummyGcs::generate_seqno_action (gcs_action& act, gcs_act_type_t type) { gcs_seqno_t* const seqno( reinterpret_cast( ::malloc(sizeof(gcs_seqno_t)))); if (!seqno) return -ENOMEM; *seqno = global_seqno_; ++local_seqno_; act.buf = seqno; act.size = sizeof(gcs_seqno_t); act.seqno_l = local_seqno_; act.type = type; return act.size; } ssize_t DummyGcs::recv(gcs_action& act) { act.seqno_g = GCS_SEQNO_ILL; act.seqno_l = GCS_SEQNO_ILL; gu::Lock lock(mtx_); do { if (cc_) { ++local_seqno_; act.buf = cc_; act.size = cc_size_; act.seqno_l = local_seqno_; act.type = GCS_ACT_CONF; cc_ = 0; cc_size_ = 0; const gcs_act_conf_t* const cc( reinterpret_cast(act.buf)); if (cc->my_idx < 0) { assert (0 == cc->memb_num); state_ = S_CLOSED; } else { assert (1 == cc->memb_num); state_ = S_CONNECTED; } return act.size; } else if (S_CONNECTED == state_) { ssize_t ret = generate_seqno_action(act, GCS_ACT_SYNC); if (ret > 0) state_ = S_SYNCED; return ret; } else if (report_last_applied_) { report_last_applied_ = false; return generate_seqno_action(act, GCS_ACT_COMMIT_CUT); } } while (state_ > S_OPEN && (lock.wait(cond_), true)); switch (state_) { case S_OPEN: return -ENOTCONN; case S_CLOSED: return 0; default: abort(); } } ssize_t DummyGcs::interrupt(ssize_t handle) { log_fatal << "Attempt to interrupt handle: " << handle; abort(); return -ENOSYS; } } percona-xtradb-cluster-galera/galera/src/ist.cpp0000644000000000000000000005664212247075736022214 0ustar rootroot00000000000000// // Copyright (C) 2011 Codership Oy // #include "ist.hpp" #include "ist_proto.hpp" #include "gu_logger.hpp" #include "gu_uri.hpp" #include "GCache.hpp" #include "galera_common.hpp" #include "trx_handle.hpp" #include #include #include namespace { static std::string const CONF_KEEP_KEYS ("ist.keep_keys"); static bool const CONF_KEEP_KEYS_DEFAULT (true); #ifdef HAVE_ASIO_SSL_HPP static std::string const CONF_SSL_KEY ("socket.ssl_key"); static std::string const CONF_SSL_CERT ("socket.ssl_cert"); static std::string const CONF_SSL_CA ("socket.ssl_ca"); static std::string const CONF_SSL_PSWD_FILE ("socket.ssl_password_file"); #endif std::string escape_addr(const asio::ip::address& addr) { if (addr.is_v4()) { return addr.to_v4().to_string(); } else { return "[" + addr.to_v6().to_string() + "]"; } } static inline std::string unescape_addr(const std::string& addr) { std::string ret(addr); size_t pos(ret.find('[')); if (pos != std::string::npos) ret.erase(pos, 1); pos = ret.find(']'); if (pos != std::string::npos) ret.erase(pos, 1); return ret; } template void set_fd_options(S& socket) { long flags(FD_CLOEXEC); if (fcntl(socket.native(), F_SETFD, flags) == -1) { gu_throw_error(errno) << "failed to set FD_CLOEXEC"; } } #ifdef HAVE_ASIO_SSL_HPP class SSLPasswordCallback { public: SSLPasswordCallback(const gu::Config& conf) : conf_(conf) { } std::string get_password() const { std::string file(conf_.get(CONF_SSL_PSWD_FILE)); std::ifstream ifs(file.c_str(), std::ios_base::in); if (ifs.good() == false) { gu_throw_error(errno) << "could not open password file '" << file << "'"; } std::string ret; std::getline(ifs, ret); return ret; } private: const gu::Config& conf_; }; static void prepare_ssl_ctx(const gu::Config& conf, asio::ssl::context& ctx) { // Here we blindly assume that ssl globals have been initialized // by gcomm. ctx.set_verify_mode(asio::ssl::context::verify_peer); SSLPasswordCallback cb(conf); ctx.set_password_callback( boost::bind(&SSLPasswordCallback::get_password, &cb)); ctx.use_private_key_file(conf.get(CONF_SSL_KEY), asio::ssl::context::pem); ctx.use_certificate_file(conf.get(CONF_SSL_CERT), asio::ssl::context::pem); ctx.load_verify_file(conf.get(CONF_SSL_CA, conf.get(CONF_SSL_CERT))); } #endif } namespace galera { namespace ist { class AsyncSender : public Sender { public: AsyncSender(const gu::Config& conf, const std::string& peer, wsrep_seqno_t first, wsrep_seqno_t last, AsyncSenderMap& asmap, int version) : Sender(conf, asmap.gcache(), peer, version), conf_(conf), peer_(peer), first_(first), last_(last), asmap_(asmap), thread_() { } const gu::Config& conf() { return conf_; } const std::string& peer() { return peer_; } wsrep_seqno_t first() { return first_; } wsrep_seqno_t last() { return last_; } AsyncSenderMap& asmap() { return asmap_; } pthread_t thread() { return thread_; } private: friend class AsyncSenderMap; const gu::Config& conf_; const std::string peer_; wsrep_seqno_t first_; wsrep_seqno_t last_; AsyncSenderMap& asmap_; pthread_t thread_; }; } } std::string const galera::ist::Receiver::RECV_ADDR("ist.recv_addr"); galera::ist::Receiver::Receiver(gu::Config& conf, const char* addr) : conf_ (conf), io_service_(), acceptor_ (io_service_), #ifdef HAVE_ASIO_SSL_HPP ssl_ctx_ (io_service_, asio::ssl::context::sslv23), #endif thread_(), mutex_(), cond_(), consumers_(), running_(false), ready_(false), error_code_(0), current_seqno_(-1), last_seqno_(-1), #ifdef HAVE_ASIO_SSL_HPP use_ssl_(false), #endif version_(-1) { std::string recv_addr; try /* check if receive address is explicitly set */ { recv_addr = conf_.get(RECV_ADDR); return; } catch (gu::NotFound& e) {} /* if not, check the alternative. TODO: try to find from system. */ if (addr) { try { recv_addr = gu::URI(std::string("tcp://") + addr).get_host(); conf_.set(RECV_ADDR, recv_addr); } catch (gu::NotSet& e) {} } } galera::ist::Receiver::~Receiver() { } extern "C" void* run_receiver_thread(void* arg) { galera::ist::Receiver* receiver(reinterpret_cast(arg)); receiver->run(); return 0; } static std::string IST_determine_recv_addr (gu::Config& conf) { std::string recv_addr; try { recv_addr = conf.get(galera::ist::Receiver::RECV_ADDR); } catch (gu::NotFound&) { try { recv_addr = conf.get(galera::BASE_HOST_KEY); } catch (gu::NotSet&) { gu_throw_error(EINVAL) << "Could not determine IST receinve address: '" << galera::ist::Receiver::RECV_ADDR << "' not set."; } } /* check if explicit scheme is present */ if (recv_addr.find("://") == std::string::npos) { #ifdef HAVE_ASIO_SSL_HPP bool ssl(false); try { std::string ssl_key = conf.get(CONF_SSL_KEY); if (ssl_key.length() != 0) ssl = true; } catch (gu::NotFound&) {} if (ssl) recv_addr.insert(0, "ssl://"); else #endif recv_addr.insert(0, "tcp://"); } gu::URI ra_uri(recv_addr); if (!conf.has(galera::BASE_HOST_KEY)) conf.set(galera::BASE_HOST_KEY, ra_uri.get_host()); try /* check for explicit port, TODO: make it possible to use any free port (explicit 0?) */ { ra_uri.get_port(); } catch (gu::NotSet&) /* use gmcast listen port + 1 */ { int port(0); try { port = gu::from_string( // gu::URI(conf.get("gmcast.listen_addr")).get_port() conf.get(galera::BASE_PORT_KEY) ); } catch (...) { port = gu::from_string(galera::BASE_PORT_DEFAULT); } port += 1; recv_addr += ":" + gu::to_string(port); } return recv_addr; } std::string galera::ist::Receiver::prepare(wsrep_seqno_t first_seqno, wsrep_seqno_t last_seqno, int version) { ready_ = false; version_ = version; recv_addr_ = IST_determine_recv_addr(conf_); gu::URI const uri(recv_addr_); try { #ifdef HAVE_ASIO_SSL_HPP if (uri.get_scheme() == "ssl") { log_info << "IST receiver using ssl"; use_ssl_ = true; prepare_ssl_ctx(conf_, ssl_ctx_); } #endif asio::ip::tcp::resolver resolver(io_service_); asio::ip::tcp::resolver::query query(unescape_addr(uri.get_host()), uri.get_port(), asio::ip::tcp::resolver::query::flags(0)); asio::ip::tcp::resolver::iterator i(resolver.resolve(query)); acceptor_.open(i->endpoint().protocol()); acceptor_.set_option(asio::ip::tcp::socket::reuse_address(true)); set_fd_options(acceptor_); acceptor_.bind(*i); acceptor_.listen(); // read recv_addr_ from acceptor_ in case zero port was specified recv_addr_ = uri.get_scheme() + "://" // + + uri.get_host() + ":" + gu::to_string(acceptor_.local_endpoint().port()); } catch (asio::system_error& e) { recv_addr_ = ""; gu_throw_error(e.code().value()) << "Failed to open IST listener at " << uri.to_string() << "', asio error '" << e.what() << "'"; } current_seqno_ = first_seqno; last_seqno_ = last_seqno; int err; if ((err = pthread_create(&thread_, 0, &run_receiver_thread, this)) != 0) { recv_addr_ = ""; gu_throw_error(err) << "Unable to create receiver thread"; } running_ = true; log_info << "Prepared IST receiver, listening at: " << (uri.get_scheme() + "://" + escape_addr(acceptor_.local_endpoint().address()) + ":" + gu::to_string(acceptor_.local_endpoint().port())); return recv_addr_; } void galera::ist::Receiver::run() { asio::ip::tcp::socket socket(io_service_); #ifdef HAVE_ASIO_SSL_HPP asio::ssl::context ssl_ctx(io_service_, asio::ssl::context::sslv23); asio::ssl::stream ssl_stream(io_service_, ssl_ctx_); #endif try { #ifdef HAVE_ASIO_SSL_HPP if (use_ssl_ == true) { acceptor_.accept(ssl_stream.lowest_layer()); set_fd_options(ssl_stream.lowest_layer()); ssl_stream.handshake(asio::ssl::stream::server); } else #endif { acceptor_.accept(socket); set_fd_options(socket); } } catch (asio::system_error& e) { gu_throw_error(e.code().value()) << "accept() failed" << "', asio error '" << e.what() << "'"; } acceptor_.close(); int ec(0); try { Proto p(version_, conf_.get(CONF_KEEP_KEYS, CONF_KEEP_KEYS_DEFAULT)); #ifdef HAVE_ASIO_SSL_HPP if (use_ssl_ == true) { p.send_handshake(ssl_stream); p.recv_handshake_response(ssl_stream); p.send_ctrl(ssl_stream, Ctrl::C_OK); } else #endif { p.send_handshake(socket); p.recv_handshake_response(socket); p.send_ctrl(socket, Ctrl::C_OK); } while (true) { TrxHandle* trx; #ifdef HAVE_ASIO_SSL_HPP if (use_ssl_ == true) { trx = p.recv_trx(ssl_stream); } else #endif { trx = p.recv_trx(socket); } if (trx != 0) { if (trx->global_seqno() != current_seqno_) { log_error << "unexpected trx seqno: " << trx->global_seqno() << " expected: " << current_seqno_; ec = EINVAL; goto err; } ++current_seqno_; } gu::Lock lock(mutex_); while (ready_ == false || consumers_.empty()) { lock.wait(cond_); } Consumer* cons(consumers_.top()); consumers_.pop(); cons->trx(trx); cons->cond().signal(); if (trx == 0) { log_debug << "eof received, closing socket"; break; } } } catch (asio::system_error& e) { log_error << "got error while reading ist stream: " << e.code(); ec = e.code().value(); } catch (gu::Exception& e) { ec = e.get_errno(); if (ec != EINTR) { log_error << "got exception while reading ist stream: " << e.what(); } } err: gu::Lock lock(mutex_); #ifdef HAVE_ASIO_SSL_HPP if (use_ssl_ == true) { ssl_stream.lowest_layer().close(); // ssl_stream.shutdown(); } else #endif { socket.close(); } running_ = false; if (ec != EINTR && current_seqno_ - 1 < last_seqno_) { log_error << "IST didn't contain all write sets, expected last: " << last_seqno_ << " last received: " << current_seqno_ - 1; ec = EPROTO; } if (ec != EINTR) { error_code_ = ec; } while (consumers_.empty() == false) { consumers_.top()->cond().signal(); consumers_.pop(); } } void galera::ist::Receiver::ready() { gu::Lock lock(mutex_); ready_ = true; cond_.signal(); } int galera::ist::Receiver::recv(TrxHandle** trx) { Consumer cons; gu::Lock lock(mutex_); if (running_ == false) { if (error_code_ != 0) { gu_throw_error(error_code_) << "IST receiver reported error"; } return EINTR; } consumers_.push(&cons); cond_.signal(); lock.wait(cons.cond()); if (cons.trx() == 0) { if (error_code_ != 0) { gu_throw_error(error_code_) << "IST receiver reported error"; } return EINTR; } *trx = cons.trx(); return 0; } wsrep_seqno_t galera::ist::Receiver::finished() { if (recv_addr_ == "") { log_debug << "IST was not prepared before calling finished()"; } else { interrupt(); int err; if ((err = pthread_join(thread_, 0)) != 0) { log_warn << "Failed to join IST receiver thread: " << err; } acceptor_.close(); gu::Lock lock(mutex_); running_ = false; while (consumers_.empty() == false) { consumers_.top()->cond().signal(); consumers_.pop(); } recv_addr_ = ""; } return (current_seqno_ - 1); } void galera::ist::Receiver::interrupt() { gu::URI uri(recv_addr_); try { asio::ip::tcp::resolver::iterator i; try { asio::ip::tcp::resolver resolver(io_service_); asio::ip::tcp::resolver::query query(unescape_addr(uri.get_host()), uri.get_port(), asio::ip::tcp::resolver::query::flags(0)); i = resolver.resolve(query); } catch (asio::system_error& e) { gu_throw_error(e.code().value()) << "failed to resolve host '" << uri.to_string() << "', asio error '" << e.what() << "'"; } #ifdef HAVE_ASIO_SSL_HPP if (use_ssl_ == true) { asio::ssl::stream ssl_stream(io_service_, ssl_ctx_); ssl_stream.lowest_layer().connect(*i); set_fd_options(ssl_stream.lowest_layer()); ssl_stream.handshake(asio::ssl::stream::client); Proto p(version_, conf_.get(CONF_KEEP_KEYS, CONF_KEEP_KEYS_DEFAULT)); p.recv_handshake(ssl_stream); p.send_ctrl(ssl_stream, Ctrl::C_EOF); p.recv_ctrl(ssl_stream); } else #endif { asio::ip::tcp::socket socket(io_service_); socket.connect(*i); set_fd_options(socket); Proto p(version_, conf_.get(CONF_KEEP_KEYS, CONF_KEEP_KEYS_DEFAULT)); p.recv_handshake(socket); p.send_ctrl(socket, Ctrl::C_EOF); p.recv_ctrl(socket); } } catch (asio::system_error& e) { // ignore } } galera::ist::Sender::Sender(const gu::Config& conf, gcache::GCache& gcache, const std::string& peer, int version) : conf_(conf), io_service_(), socket_(io_service_), #ifdef HAVE_ASIO_SSL_HPP ssl_ctx_(io_service_, asio::ssl::context::sslv23), ssl_stream_(io_service_, ssl_ctx_), use_ssl_(false), #endif gcache_(gcache), version_(version) { gu::URI uri(peer); try { asio::ip::tcp::resolver resolver(io_service_); asio::ip::tcp::resolver::query query(unescape_addr(uri.get_host()), uri.get_port(), asio::ip::tcp::resolver::query::flags(0)); asio::ip::tcp::resolver::iterator i(resolver.resolve(query)); #ifdef HAVE_ASIO_SSL_HPP if (uri.get_scheme() == "ssl") { use_ssl_ = true; } if (use_ssl_ == true) { log_info << "IST sender using ssl"; prepare_ssl_ctx(conf, ssl_ctx_); ssl_stream_.lowest_layer().connect(*i); set_fd_options(ssl_stream_.lowest_layer()); ssl_stream_.handshake(asio::ssl::stream::client); } else #endif { socket_.connect(*i); set_fd_options(socket_); } } catch (asio::system_error& e) { gu_throw_error(e.code().value()) << "IST sender, failed to connect '" << peer.c_str() << "': " << e.what(); } } galera::ist::Sender::~Sender() { #ifdef HAVE_ASIO_SSL_HPP if (use_ssl_ == true) { ssl_stream_.lowest_layer().close(); } else #endif { socket_.close(); } gcache_.seqno_release(); } void galera::ist::Sender::send(wsrep_seqno_t first, wsrep_seqno_t last) { if (first > last) { gu_throw_error(EINVAL) << "sender send first greater than last: " << first << " > " << last ; } try { Proto p(version_, conf_.get(CONF_KEEP_KEYS, CONF_KEEP_KEYS_DEFAULT)); int32_t ctrl; #ifdef HAVE_ASIO_SSL_HPP if (use_ssl_ == true) { p.recv_handshake(ssl_stream_); p.send_handshake_response(ssl_stream_); ctrl = p.recv_ctrl(ssl_stream_); } else #endif { p.recv_handshake(socket_); p.send_handshake_response(socket_); ctrl = p.recv_ctrl(socket_); } if (ctrl < 0) { gu_throw_error(EPROTO) << "ist send failed, peer reported error: " << ctrl; } std::vector buf_vec( std::min(static_cast(last - first + 1), static_cast(1024))); ssize_t n_read; while ((n_read = gcache_.seqno_get_buffers(buf_vec, first)) > 0) { // log_info << "read " << first << " + " << n_read << " from gcache"; for (wsrep_seqno_t i(0); i < n_read; ++i) { // log_info << "sending " << buf_vec[i].seqno_g(); #ifdef HAVE_ASIO_SSL_HPP if (use_ssl_ == true) { p.send_trx(ssl_stream_, buf_vec[i]); } else #endif { p.send_trx(socket_, buf_vec[i]); } if (buf_vec[i].seqno_g() == last) { #ifdef HAVE_ASIO_SSL_HPP if (use_ssl_ == true) { p.send_ctrl(ssl_stream_, Ctrl::C_EOF); } else #endif { p.send_ctrl(socket_, Ctrl::C_EOF); } // wait until receiver closes the connection try { gu::byte_t b; size_t n; #ifdef HAVE_ASIO_SSL_HPP if (use_ssl_ == true) { n = asio::read(ssl_stream_, asio::buffer(&b, 1)); } else #endif { n = asio::read(socket_, asio::buffer(&b, 1)); } if (n > 0) { log_warn << "received " << n << " bytes, expected none"; } } catch (asio::system_error& e) { } return; } } first += n_read; // resize buf_vec to avoid scanning gcache past last size_t next_size(std::min(static_cast(last - first + 1), static_cast(1024))); if (buf_vec.size() != next_size) { buf_vec.resize(next_size); } } } catch (asio::system_error& e) { gu_throw_error(e.code().value()) << "ist send failed: " << e.code() << "', asio error '" << e.what() << "'"; } } extern "C" void* run_async_sender(void* arg) { galera::ist::AsyncSender* as(reinterpret_cast(arg)); log_info << "async IST sender starting to serve " << as->peer().c_str() << " sending " << as->first() << "-" << as->last(); wsrep_seqno_t join_seqno; try { as->send(as->first(), as->last()); join_seqno = as->last(); } catch (gu::Exception& e) { log_error << "async IST sender failed to serve " << as->peer().c_str() << ": " << e.what(); join_seqno = -e.get_errno(); } catch (...) { log_error << "async IST sender, failed to serve " << as->peer().c_str(); throw; } try { as->asmap().remove(as, join_seqno); pthread_detach(as->thread()); delete as; } catch (gu::NotFound& nf) { log_debug << "async IST sender already removed"; } log_info << "async IST sender served"; return 0; } void galera::ist::AsyncSenderMap::run(const gu::Config& conf, const std::string& peer, wsrep_seqno_t first, wsrep_seqno_t last, int version) { gu::Critical crit(monitor_); AsyncSender* as(new AsyncSender(conf, peer, first, last, *this, version)); int err(pthread_create(&as->thread_, 0, &run_async_sender, as)); if (err != 0) { delete as; gu_throw_error(err) << "failed to start sender thread"; } senders_.insert(as); } void galera::ist::AsyncSenderMap::remove(AsyncSender* as, wsrep_seqno_t seqno) { gu::Critical crit(monitor_); std::set::iterator i(senders_.find(as)); if (i == senders_.end()) { throw gu::NotFound(); } senders_.erase(i); gcs_.join(seqno); } void galera::ist::AsyncSenderMap::cancel() { gu::Critical crit(monitor_); while (senders_.empty() == false) { AsyncSender* as(*senders_.begin()); senders_.erase(*senders_.begin()); int err; as->cancel(); monitor_.leave(); if ((err = pthread_join(as->thread_, 0)) != 0) { log_warn << "pthread_join() failed: " << err; } monitor_.enter(); delete as; } } percona-xtradb-cluster-galera/galera/src/ist.hpp0000644000000000000000000001072012247075736022204 0ustar rootroot00000000000000// // Copyright (C) 2011 Codership Oy // #ifndef GALERA_IST_HPP #define GALERA_IST_HPP #include #include "wsrep_api.h" #include "gcs.hpp" #include "gu_config.hpp" #include "gu_lock.hpp" #include "gu_monitor.hpp" #pragma GCC diagnostic ignored "-Weffc++" #pragma GCC diagnostic ignored "-Wold-style-cast" #include "asio.hpp" #ifdef HAVE_ASIO_SSL_HPP #include "asio/ssl.hpp" #endif #include #include namespace gcache { class GCache; } namespace galera { class TrxHandle; namespace ist { class Receiver { public: static std::string const RECV_ADDR; Receiver(gu::Config& conf, const char* addr); ~Receiver(); std::string prepare(wsrep_seqno_t, wsrep_seqno_t, int); void ready(); int recv(TrxHandle** trx); wsrep_seqno_t finished(); void run(); private: void interrupt(); gu::Config& conf_; std::string recv_addr_; asio::io_service io_service_; asio::ip::tcp::acceptor acceptor_; #ifdef HAVE_ASIO_SSL_HPP asio::ssl::context ssl_ctx_; #endif pthread_t thread_; gu::Mutex mutex_; gu::Cond cond_; class Consumer { public: Consumer() : cond_(), trx_(0) { } ~Consumer() { } gu::Cond& cond() { return cond_; } void trx(TrxHandle* trx) { trx_ = trx; } TrxHandle* trx() const { return trx_; } private: gu::Cond cond_; TrxHandle* trx_; }; std::stack consumers_; bool running_; bool ready_; int error_code_; wsrep_seqno_t current_seqno_; wsrep_seqno_t last_seqno_; #ifdef HAVE_ASIO_SSL_HPP bool use_ssl_; #endif int version_; }; class Sender { public: Sender(const gu::Config& conf, gcache::GCache& gcache, const std::string& peer, int version); ~Sender(); void send(wsrep_seqno_t first, wsrep_seqno_t last); void cancel() { #ifdef HAVE_ASIO_SSL_HPP if (use_ssl_ == true) { ssl_stream_.lowest_layer().close(); } else #endif { socket_.close(); } } private: Sender(const Sender&); void operator=(const Sender&); const gu::Config& conf_; asio::io_service io_service_; asio::ip::tcp::socket socket_; #ifdef HAVE_ASIO_SSL_HPP asio::ssl::context ssl_ctx_; asio::ssl::stream ssl_stream_; bool use_ssl_; #endif gcache::GCache& gcache_; int version_; }; class AsyncSender; class AsyncSenderMap { public: AsyncSenderMap(GCS_IMPL& gcs, gcache::GCache& gcache) : senders_(), monitor_(), gcs_(gcs), gcache_(gcache) { } void run(const gu::Config& conf, const std::string& peer, wsrep_seqno_t, wsrep_seqno_t, int); void remove(AsyncSender*, wsrep_seqno_t); void cancel(); gcache::GCache& gcache() { return gcache_; } private: std::set senders_; // use monitor instead of mutex, it provides cancellation point gu::Monitor monitor_; GCS_IMPL& gcs_; gcache::GCache& gcache_; }; } // namespace ist } // namespace galera #endif // GALERA_IST_HPP percona-xtradb-cluster-galera/galera/src/ist_proto.hpp0000644000000000000000000005211512247075736023433 0ustar rootroot00000000000000// // Copyright (C) 2011-2012 Codership Oy // #ifndef GALERA_IST_PROTO_HPP #define GALERA_IST_PROTO_HPP #include "trx_handle.hpp" #include "GCache.hpp" #include "gu_logger.hpp" #include "gu_serialize.hpp" // // Sender Receiver // connect() -----> accept() // <----- send_handshake() // send_handshake_response() -----> // <----- send_ctrl(OK) // send_trx() -----> // -----> // send_ctrl(EOF) -----> // <----- close() // close() // // Note about protocol/message versioning: // Version is determined by GCS and IST protocol is initialized in total // order. Therefore it is not necessary to negotiate version at IST level, // it should be enough to check that message version numbers match. // namespace galera { namespace ist { class Message { public: typedef enum { T_NONE = 0, T_HANDSHAKE = 1, T_HANDSHAKE_RESPONSE = 2, T_CTRL = 3, T_TRX = 4 } Type; Message(int version = -1, Type type = T_NONE, uint8_t flags = 0, int8_t ctrl = 0, uint64_t len = 0) : version_(version), type_ (type ), flags_ (flags ), ctrl_ (ctrl ), len_ (len ) { } int version() const { return version_; } Type type() const { return type_ ; } uint8_t flags() const { return flags_ ; } int8_t ctrl() const { return ctrl_ ; } uint64_t len() const { return len_ ; } private: friend size_t serial_size(const Message&); friend size_t serialize(const Message&, gu::byte_t*, size_t, size_t); friend size_t unserialize(const gu::byte_t*, size_t, size_t, Message&); int version_; // unfortunately for compatibility with older // versions we must leave it as int (4 bytes) Type type_; uint8_t flags_; int8_t ctrl_; uint64_t len_; }; class Handshake : public Message { public: Handshake(int version = -1) : Message(version, Message::T_HANDSHAKE, 0, 0, 0) { } }; class HandshakeResponse : public Message { public: HandshakeResponse(int version = -1) : Message(version, Message::T_HANDSHAKE_RESPONSE, 0, 0, 0) { } }; class Ctrl : public Message { public: enum { // negative values reserved for error codes C_OK = 0, C_EOF = 1 }; Ctrl(int version = -1, int8_t code = 0) : Message(version, Message::T_CTRL, 0, code, 0) { } }; class Trx : public Message { public: Trx(int version = -1, uint64_t len = 0) : Message(version, Message::T_TRX, 0, 0, len) { } }; inline size_t serial_size(const Message& msg) { if (msg.version_ > 3) { // header: version 1 byte, type 1 byte, flags 1 byte, // ctrl field 1 byte return 4 + sizeof(msg.len_); } else { return sizeof(msg); } } inline size_t serialize(const Message& msg, gu::byte_t* buf, size_t buflen, size_t offset) { #ifndef NDEBUG size_t orig_offset(offset); #endif // NDEBUG if (msg.version_ > 3) { offset = gu::serialize1(uint8_t(msg.version_), buf, buflen, offset); offset = gu::serialize1(uint8_t(msg.type_), buf, buflen, offset); offset = gu::serialize1(msg.flags_, buf, buflen, offset); offset = gu::serialize1(msg.ctrl_, buf, buflen, offset); offset = gu::serialize8(msg.len(), buf, buflen, offset); } else { if (buflen < offset + sizeof(msg)) { gu_throw_error(EMSGSIZE) << "buffer too short"; } *reinterpret_cast(buf + offset) = msg; offset += sizeof(msg); } assert((msg.version_ > 3 && offset - orig_offset == 12) || (offset - orig_offset == sizeof(msg))); return offset; } inline size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset, Message& msg) { assert(msg.version_ >= 0); #ifndef NDEBUG size_t orig_offset(offset); #endif // NDEBUG uint8_t u8; if (msg.version_ > 3) { offset = gu::unserialize1(buf, buflen, offset, u8); } else { u8 = *reinterpret_cast(buf + offset); } if (u8 != msg.version_) { gu_throw_error(EPROTO) << "invalid protocol version " << int(u8) << ", expected " << msg.version_; } if (u8 > 3) { msg.version_ = u8; offset = gu::unserialize1(buf, buflen, offset, u8); msg.type_ = static_cast(u8); offset = gu::unserialize1(buf, buflen, offset, msg.flags_); offset = gu::unserialize1(buf, buflen, offset, msg.ctrl_); offset = gu::unserialize8(buf, buflen, offset, msg.len_); } else { if (buflen < offset + sizeof(msg)) { gu_throw_error(EMSGSIZE) << "buffer too short for version " << msg.version_ << ": " << buflen << " " << offset << " " << sizeof(msg); } msg = *reinterpret_cast(buf + offset); offset += sizeof(msg); } assert((msg.version_ > 3 && offset - orig_offset == 12) || (offset - orig_offset == sizeof(msg))); return offset; } class Proto { public: Proto(int version, bool keep_keys) : version_(version), keep_keys_(keep_keys), raw_sent_(0), real_sent_(0) { } ~Proto() { if (raw_sent_ > 0) { log_info << "ist proto finished, raw sent: " << raw_sent_ << " real sent: " << real_sent_ << " frac: " << (raw_sent_ == 0 ? 0. : static_cast(real_sent_)/raw_sent_); } } template void send_handshake(ST& socket) { Handshake hs(version_); gu::Buffer buf(serial_size(hs)); size_t offset(serialize(hs, &buf[0], buf.size(), 0)); size_t n(asio::write(socket, asio::buffer(&buf[0], buf.size()))); if (n != offset) { gu_throw_error(EPROTO) << "error sending handshake"; } } template void recv_handshake(ST& socket) { Message msg(version_); gu::Buffer buf(serial_size(msg)); size_t n(asio::read(socket, asio::buffer(&buf[0], buf.size()))); if (n != buf.size()) { gu_throw_error(EPROTO) << "error receiving handshake"; } (void)unserialize(&buf[0], buf.size(), 0, msg); log_debug << "handshake msg: " << msg.version() << " " << msg.type() << " " << msg.len(); switch (msg.type()) { case Message::T_HANDSHAKE: break; case Message::T_CTRL: switch (msg.ctrl()) { case Ctrl::C_EOF: gu_throw_error(EINTR); default: gu_throw_error(EPROTO) << "unexpected ctrl code: " << msg.ctrl(); } break; default: gu_throw_error(EPROTO) << "unexpected message type: " << msg.type(); } if (msg.version() != version_) { gu_throw_error(EPROTO) << "mismatching protocol version: " << msg.version() << " required: " << version_; } // TODO: Figure out protocol versions to use } template void send_handshake_response(ST& socket) { HandshakeResponse hsr(version_); gu::Buffer buf(serial_size(hsr)); size_t offset(serialize(hsr, &buf[0], buf.size(), 0)); size_t n(asio::write(socket, asio::buffer(&buf[0], buf.size()))); if (n != offset) { gu_throw_error(EPROTO) << "error sending handshake response"; } } template void recv_handshake_response(ST& socket) { Message msg(version_); gu::Buffer buf(serial_size(msg)); size_t n(asio::read(socket, asio::buffer(&buf[0], buf.size()))); if (n != buf.size()) { gu_throw_error(EPROTO) << "error receiving handshake"; } (void)unserialize(&buf[0], buf.size(), 0, msg); log_debug << "handshake response msg: " << msg.version() << " " << msg.type() << " " << msg.len(); switch (msg.type()) { case Message::T_HANDSHAKE_RESPONSE: break; case Message::T_CTRL: switch (msg.ctrl()) { case Ctrl::C_EOF: gu_throw_error(EINTR) << "interrupted by ctrl"; default: gu_throw_error(EPROTO) << "unexpected ctrl code: " << msg.ctrl(); } default: gu_throw_error(EINVAL) << "unexpected message type: " << msg.type(); } } template void send_ctrl(ST& socket, int8_t code) { Ctrl ctrl(version_, code); gu::Buffer buf(serial_size(ctrl)); size_t offset(serialize(ctrl, &buf[0], buf.size(), 0)); size_t n(asio::write(socket, asio::buffer(&buf[0], buf.size()))); if (n != offset) { gu_throw_error(EPROTO) << "error sending ctrl message"; } } template int8_t recv_ctrl(ST& socket) { Message msg(version_); gu::Buffer buf(serial_size(msg)); size_t n(asio::read(socket, asio::buffer(&buf[0], buf.size()))); if (n != buf.size()) { gu_throw_error(EPROTO) << "error receiving handshake"; } (void)unserialize(&buf[0], buf.size(), 0, msg); log_debug << "msg: " << msg.version() << " " << msg.type() << " " << msg.len(); switch (msg.type()) { case Message::T_CTRL: break; default: gu_throw_error(EPROTO) << "unexpected message type: " << msg.type(); } return msg.ctrl(); } template void send_trx(ST& socket, const gcache::GCache::Buffer& buffer) { const size_t trx_meta_size( 8 // serial_size(buffer.seqno_g()) + 8 // serial_size(buffer.seqno_d()) ); const bool rolled_back(buffer.seqno_d() == -1); size_t n; if (rolled_back == true) { Trx trx_msg(version_, trx_meta_size); gu::Buffer buf(serial_size(trx_msg) + trx_meta_size); size_t offset(serialize(trx_msg, &buf[0], buf.size(), 0)); offset = gu::serialize8(buffer.seqno_g(), &buf[0], buf.size(), offset); offset = gu::serialize8(buffer.seqno_d(), &buf[0], buf.size(), offset); n = asio::write(socket, asio::buffer(&buf[0], buf.size())); } else if (keep_keys_ == true) { Trx trx_msg(version_, trx_meta_size + buffer.size()); gu::Buffer buf(serial_size(trx_msg) + trx_meta_size); size_t offset(serialize(trx_msg, &buf[0], buf.size(), 0)); offset = gu::serialize8(buffer.seqno_g(), &buf[0], buf.size(), offset); offset = gu::serialize8(buffer.seqno_d(), &buf[0], buf.size(), offset); boost::array cbs; cbs[0] = asio::const_buffer(&buf[0], buf.size()); cbs[1] = asio::const_buffer(buffer.ptr(), buffer.size()); n = asio::write(socket, cbs); } else { class AutoRelease { public: AutoRelease(TrxHandle* trx) : trx_(trx) { } ~AutoRelease() { trx_->unref(); } TrxHandle* trx() { return trx_; } private: AutoRelease(const AutoRelease&); void operator=(const AutoRelease&); TrxHandle* trx_; }; // reconstruct trx without keys AutoRelease ar(new TrxHandle); galera::TrxHandle* trx(ar.trx()); const gu::byte_t* const ptr( reinterpret_cast(buffer.ptr())); size_t offset(unserialize(ptr,buffer.size(), 0, *trx)); while (offset < static_cast(buffer.size())) { // skip over keys uint32_t len; offset = gu::unserialize4( ptr, buffer.size(), offset, len); offset += len; offset = gu::unserialize4( ptr, buffer.size(), offset, len); if (offset + len > static_cast(buffer.size())) { gu_throw_error(ERANGE) << (offset + len) << " > " << buffer.size(); } trx->append_data(ptr + offset, len); offset += len; } trx->flush(0); Trx trx_msg(version_, trx_meta_size + trx->write_set_collection().size()); gu::Buffer buf(serial_size(trx_msg) + trx_meta_size); offset = serialize(trx_msg, &buf[0], buf.size(), 0); offset = gu::serialize8(buffer.seqno_g(), &buf[0], buf.size(), offset); offset = gu::serialize8(buffer.seqno_d(), &buf[0], buf.size(), offset); boost::array cbs; cbs[0] = asio::const_buffer(&buf[0], buf.size()); cbs[1] = asio::const_buffer( &trx->write_set_collection()[0], trx->write_set_collection().size()); raw_sent_ += buffer.size(); real_sent_ += trx->write_set_collection().size(); n = asio::write(socket, cbs); } log_debug << "sent " << n << " bytes"; } template galera::TrxHandle* recv_trx(ST& socket) { Message msg(version_); gu::Buffer buf(serial_size(msg)); size_t n(asio::read(socket, asio::buffer(&buf[0], buf.size()))); if (n != buf.size()) { gu_throw_error(EPROTO) << "error receiving trx header"; } (void)unserialize(&buf[0], buf.size(), 0, msg); log_debug << "received header: " << n << " bytes, type " << msg.type() << " len " << msg.len(); switch (msg.type()) { case Message::T_TRX: { buf.resize(msg.len()); n = asio::read(socket, asio::buffer(&buf[0], buf.size())); if (n != buf.size()) { gu_throw_error(EPROTO) << "error reading trx data"; } wsrep_seqno_t seqno_g, seqno_d; galera::TrxHandle* trx(new galera::TrxHandle); size_t offset(gu::unserialize8(&buf[0], buf.size(), 0, seqno_g)); offset = gu::unserialize8(&buf[0], buf.size(), offset, seqno_d); if (seqno_d == -1) { if (offset != msg.len()) { gu_throw_error(EINVAL) << "message size " << msg.len() << " does not match expected size " << offset; } } else { offset = unserialize(&buf[0], buf.size(), offset, *trx); trx->append_write_set(&buf[0] + offset, buf.size() - offset); } trx->set_received(0, -1, seqno_g); trx->set_depends_seqno(seqno_d); trx->mark_certified(); log_debug << "received trx body: " << *trx; return trx; } case Message::T_CTRL: switch (msg.ctrl()) { case Ctrl::C_EOF: return 0; default: if (msg.ctrl() >= 0) { gu_throw_error(EPROTO) << "unexpected ctrl code: " << msg.ctrl(); } else { gu_throw_error(-msg.ctrl()) << "peer reported error"; } } default: gu_throw_error(EPROTO) << "unexpected message type: " << msg.type(); } gu_throw_fatal; } private: int version_; bool keep_keys_; uint64_t raw_sent_; uint64_t real_sent_; }; } } #endif // GALERA_IST_PROTO_HPP percona-xtradb-cluster-galera/galera/src/key.hpp0000644000000000000000000002223712247075736022203 0ustar rootroot00000000000000// // Copyright (C) 2011-2012 Codership Oy // #ifndef GALERA_KEY_HPP #define GALERA_KEY_HPP #include "wsrep_api.h" #include "gu_hash.h" #include "gu_serialize.hpp" #include "gu_unordered.hpp" #include "gu_throw.hpp" #include "gu_vlq.hpp" #include #include #include #include #include namespace galera { // helper to cast from any kind of pointer to void template static inline void* void_cast(const C* c) { return const_cast(reinterpret_cast(c)); } class KeyPart { public: KeyPart(const gu::byte_t* buf, size_t buf_size) : buf_(buf), buf_size_(buf_size) { } const gu::byte_t* buf() const { return buf_; } size_t size() const { return buf_size_; } size_t key_len() const { #ifndef GALERA_KEY_VLQ return buf_[0]; #else size_t ret; (void)gu::uleb128_decode(buf_, buf_size_, 0, ret); return ret; #endif } #ifndef GALERA_KEY_VLQ const gu::byte_t* key() const { return buf_ + 1; } #else const gu::byte_t* key() const { size_t not_used; return buf_ + gu::uleb128_decode(buf_, buf_size_, 0, not_used); } #endif bool operator==(const KeyPart& other) const { return (other.buf_size_ == buf_size_ && memcmp(other.buf_, buf_, buf_size_) == 0); } private: const gu::byte_t* buf_; size_t buf_size_; }; inline std::ostream& operator<<(std::ostream& os, const KeyPart& kp) { const std::ostream::fmtflags prev_flags(os.flags(std::ostream::hex)); const char prev_fill(os.fill('0')); for (const gu::byte_t* i(kp.key()); i != kp.key() + kp.key_len(); ++i) { os << std::setw(2) << static_cast(*i); } os.flags(prev_flags); os.fill(prev_fill); return os; } class Key { public: enum { F_SHARED = 0x1 }; Key(int version) : version_(version), flags_(), keys_() { } Key(int version, const wsrep_buf_t* keys, size_t keys_len, uint8_t flags) : version_(version), flags_ (flags), keys_ () { if (keys_len > 255) { gu_throw_error(EINVAL) << "maximum number of key parts exceeded: " << keys_len; } switch (version) { case 1: case 2: for (size_t i(0); i < keys_len; ++i) { size_t const offset(keys_.size()); size_t key_len(keys[i].len); const gu::byte_t* base(reinterpret_cast( keys[i].ptr)); #ifndef GALERA_KEY_VLQ if (gu_unlikely(key_len > 0xff)) key_len = 0xff; keys_.reserve(offset + 1 + key_len); keys_.insert(keys_.end(), key_len); keys_.insert(keys_.end(), base, base + key_len); #else size_t len_size(gu::uleb128_size(key_len)); keys_.resize(offset + len_size); (void)gu::uleb128_encode( key_len, &keys_[0], keys_.size(), offset); keys_.insert(keys_.end(), base, base + keys[i].key_len); #endif } break; default: gu_throw_fatal << "unsupported key version: " << version_; } } template Key(int version, Ci begin, Ci end, uint8_t flags) : version_(version), flags_(flags), keys_() { for (Ci i(begin); i != end; ++i) { keys_.insert( keys_.end(), i->buf(), i->buf() + i->size()); } } int version() const { return version_; } template C key_parts() const { C ret; size_t i(0); size_t const keys_size(keys_.size()); while (i < keys_size) { #ifndef GALERA_KEY_VLQ size_t key_len(keys_[i] + 1); #else size_t key_len; size_t offset( gu::uleb128_decode(&keys_[0], keys_size, i, key_len)); key_len += offset - i; #endif if (gu_unlikely((i + key_len) > keys_size)) { gu_throw_fatal << "Keys buffer overflow by " << i + key_len - keys_size << " bytes: " << i + key_len << '/' << keys_size; } KeyPart kp(&keys_[i], key_len); ret.push_back(kp); i += key_len; } assert(i == keys_size); return ret; } uint8_t flags() const { return flags_; } bool operator==(const Key& other) const { return (keys_ == other.keys_); } bool equal_all(const Key& other) const { return (version_ == other.version_ && flags_ == other.flags_ && keys_ == other.keys_); } size_t size() const { return keys_.size() + sizeof(*this); } size_t hash() const { return gu_table_hash(&keys_[0], keys_.size()); } size_t hash_with_flags() const { return hash() ^ gu_table_hash(&flags_, sizeof(flags_)); } private: friend size_t serialize(const Key&, gu::byte_t*, size_t, size_t); friend size_t unserialize(const gu::byte_t*, size_t, size_t, Key&); friend size_t serial_size(const Key&); friend std::ostream& operator<<(std::ostream& os, const Key& key); int version_; uint8_t flags_; gu::Buffer keys_; }; inline std::ostream& operator<<(std::ostream& os, const Key& key) { std::ostream::fmtflags flags(os.flags()); switch (key.version_) { case 2: os << std::hex << static_cast(key.flags()) << " "; // Fall through case 1: { std::deque dq(key.key_parts >()); std::copy(dq.begin(), dq.end(), std::ostream_iterator(os, " ")); break; } default: gu_throw_fatal << "unsupported key version: " << key.version_; } os.flags(flags); return os; } inline size_t serialize(const Key& key, gu::byte_t* buf, size_t buflen, size_t offset) { switch (key.version_) { #ifndef GALERA_KEY_VLQ case 1: return gu::serialize2(key.keys_, buf, buflen, offset); case 2: offset = gu::serialize1(key.flags_, buf, buflen, offset); return gu::serialize2(key.keys_, buf, buflen, offset); #else case 1: { size_t keys_size(key.keys_.size()); offset = gu::uleb128_encode(keys_size, buf, buflen, offset); assert (offset + key_size <= buflen); std::copy(&key.keys_[0], &key.keys_[0] + keys_size, buf + offset); return (offset + keys_size); } #endif default: log_fatal << "Internal error: unsupported key version: " << key.version_; abort(); return 0; } } inline size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset, Key& key) { switch (key.version_) { #ifndef GALERA_KEY_VLQ case 1: return gu::unserialize2(buf, buflen, offset, key.keys_); case 2: offset = gu::unserialize1(buf, buflen, offset, key.flags_); return gu::unserialize2(buf, buflen, offset, key.keys_); #else case 1: { size_t len; offset = gu::uleb128_decode(buf, buflen, offset, len); key.keys_.resize(len); std::copy(buf + offset, buf + offset + len, key.keys_.begin()); return (offset + len); } #endif default: gu_throw_error(EPROTONOSUPPORT) << "unsupported key version: " << key.version_; } } inline size_t serial_size(const Key& key) { switch (key.version_) { #ifndef GALERA_KEY_VLQ case 1: return gu::serial_size2(key.keys_); case 2: return (gu::serial_size(key.flags_) + gu::serial_size2(key.keys_)); #else case 1: { size_t size(gu::uleb128_size(key.keys_.size())); return (size + key.keys_.size()); } #endif default: log_fatal << "Internal error: unsupported key version: " << key.version_; abort(); return 0; } } } #endif // GALERA_KEY_HPP percona-xtradb-cluster-galera/galera/src/key_entry.cpp0000644000000000000000000000307112247075736023412 0ustar rootroot00000000000000// // Copyright (C) 2012 Codership Oy // #include "key_entry.hpp" #include "trx_handle.hpp" namespace galera { #ifndef NDEBUG void KeyEntry::assert_ref(TrxHandle* trx, bool full_key) { assert(ref_trx_ == 0 || ref_trx_->global_seqno() <= trx->global_seqno()); if (full_key) { assert(ref_full_trx_ == 0 || (ref_full_trx_->global_seqno() <= trx->global_seqno() && ref_trx_ != 0)); } } void KeyEntry::assert_unref(TrxHandle* trx) { if (ref_full_trx_ != 0 && ref_trx_ == 0) { log_fatal << "dereferencing EXCLUSIVE partial key: " << key_ << " by " << trx->global_seqno() << ", while full key referenced by " << ref_full_trx_->global_seqno(); assert(0); } } void KeyEntry::assert_ref_shared(TrxHandle* trx, bool full_key) { assert(ref_shared_trx_ == 0 || ref_shared_trx_->global_seqno() <= trx->global_seqno()); if (full_key) { assert(ref_full_shared_trx_ == 0 || (ref_full_shared_trx_->global_seqno() <= trx->global_seqno() && ref_shared_trx_ != 0)); } } void KeyEntry::assert_unref_shared(TrxHandle* trx) { if (ref_full_shared_trx_ != 0 && ref_shared_trx_ == 0) { log_fatal << "dereferencing SHARED partial key: " << key_ << " by " << trx->global_seqno() << ", while full key referenced by " << ref_full_shared_trx_->global_seqno(); assert(0); } } #endif /* NDEBUG */ } percona-xtradb-cluster-galera/galera/src/key_entry.hpp0000644000000000000000000001050712247075736023421 0ustar rootroot00000000000000// // Copyright (C) 2012 Codership Oy // #ifndef GALERA_KEY_ENTRY_HPP #define GALERA_KEY_ENTRY_HPP #include "key.hpp" namespace galera { class TrxHandle; class KeyEntry { public: KeyEntry(const Key& row_key) : key_(row_key), ref_trx_(0), ref_full_trx_(0), ref_shared_trx_(0), ref_full_shared_trx_(0) {} template KeyEntry(int version, Ci begin, Ci end, uint8_t flags) : key_(version, begin, end, flags), ref_trx_(0), ref_full_trx_(0), ref_shared_trx_(0), ref_full_shared_trx_(0) {} KeyEntry(const KeyEntry& other) : key_(other.key_), ref_trx_(other.ref_trx_), ref_full_trx_(other.ref_full_trx_), ref_shared_trx_(other.ref_shared_trx_), ref_full_shared_trx_(other.ref_full_shared_trx_) {} ~KeyEntry() { assert(ref_trx_ == 0); assert(ref_full_trx_ == 0); assert(ref_shared_trx_ == 0); assert(ref_full_shared_trx_ == 0); } const Key& get_key() const { return key_; } const Key& get_key(int version) const { return key_; } void ref(TrxHandle* trx, bool full_key) { #ifndef NDEBUG assert_ref(trx, full_key); #endif /* NDEBUG */ ref_trx_ = trx; if (full_key == true) { ref_full_trx_ = trx; } } void unref(TrxHandle* trx, bool full_key) { assert(ref_trx_ != 0); if (ref_trx_ == trx) ref_trx_ = 0; if (full_key == true && ref_full_trx_ == trx) { ref_full_trx_ = 0; } else { #ifndef NDEBUG assert_unref(trx); #endif /* NDEBUG */ } } void ref_shared(TrxHandle* trx, bool full_key) { #ifndef NDEBUG assert_ref_shared(trx, full_key); #endif /* NDEBUG */ ref_shared_trx_ = trx; if (full_key == true) { ref_full_shared_trx_ = trx; } } void unref_shared(TrxHandle* trx, bool full_key) { assert(ref_shared_trx_ != 0); if (ref_shared_trx_ == trx) ref_shared_trx_ = 0; if (full_key == true && ref_full_shared_trx_ == trx) { ref_full_shared_trx_ = 0; } else { #ifndef NDEBUG assert_unref_shared(trx); #endif /* NDEBUG */ } } const TrxHandle* ref_trx() const { return ref_trx_; } const TrxHandle* ref_full_trx() const { return ref_full_trx_; } const TrxHandle* ref_shared_trx() const { return ref_shared_trx_; } const TrxHandle* ref_full_shared_trx() const { return ref_full_shared_trx_; } size_t size() const { return key_.size() + sizeof(*this); } private: void operator=(const KeyEntry&); Key key_; TrxHandle* ref_trx_; TrxHandle* ref_full_trx_; TrxHandle* ref_shared_trx_; TrxHandle* ref_full_shared_trx_; #ifndef NDEBUG void assert_ref(TrxHandle*, bool); void assert_unref(TrxHandle*); void assert_ref_shared(TrxHandle*, bool); void assert_unref_shared(TrxHandle*); #endif /* NDEBUG */ }; class KeyEntryPtrHash { public: size_t operator()(const KeyEntry* const ke) const { return ke->get_key().hash(); } }; class KeyEntryPtrHashAll { public: size_t operator()(const KeyEntry* const ke) const { return ke->get_key().hash_with_flags(); } }; class KeyEntryPtrEqual { public: bool operator()(const KeyEntry* const left, const KeyEntry* const right) const { return left->get_key() == right->get_key(); } }; class KeyEntryPtrEqualAll { public: bool operator()(const KeyEntry* const left, const KeyEntry* const right) const { return left->get_key().equal_all(right->get_key()); } }; } #endif // GALERA_KEY_ENTRY_HPP percona-xtradb-cluster-galera/galera/src/mapped_buffer.cpp0000644000000000000000000000735312247075736024207 0ustar rootroot00000000000000// // Copyright (C) 2010 Codership Oy // #define _FILE_OFFSET_BITS 64 #include "mapped_buffer.hpp" #include "gu_throw.hpp" #include "gu_logger.hpp" #include "gu_macros.h" #include #include #include #include #include // MAP_FAILED is defined as (void *) -1 #pragma GCC diagnostic ignored "-Wold-style-cast" using namespace std; using namespace gu; galera::MappedBuffer::MappedBuffer(const std::string& working_dir, size_t threshold) : working_dir_ (working_dir), file_ (), fd_ (-1), threshold_ (threshold), buf_ (0), buf_size_ (0), real_buf_size_(0) { } galera::MappedBuffer::~MappedBuffer() { if (fd_ != -1) { struct stat st; fstat(fd_, &st); log_debug << "file size " << st.st_size; } clear(); } void galera::MappedBuffer::reserve(size_t sz) { if (real_buf_size_ >= sz) { // no need for reallocation return; } if (sz > threshold_) { // buffer size exceeds in-memory threshold, have to mmap if (gu_unlikely(std::numeric_limits::max() - sz < threshold_)) { sz = std::numeric_limits::max(); } else { sz = (sz/threshold_ + 1)*threshold_; } if (gu_unlikely(sz > static_cast(std::numeric_limits::max()))) { gu_throw_error(EINVAL) << "size exceeds maximum of off_t"; } if (fd_ == -1) { file_ = working_dir_ + "/gmb_XXXXXX"; fd_ = mkstemp(&file_[0]); if (fd_ == -1) { gu_throw_error(errno) << "mkstemp(" << file_ << ") failed"; } if (ftruncate(fd_, sz) == -1) { gu_throw_error(errno) << "ftruncate() failed"; } byte_t* tmp(reinterpret_cast( mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd_, 0))); if (tmp == MAP_FAILED) { free(buf_); buf_ = 0; clear(); gu_throw_error(ENOMEM) << "mmap() failed"; } copy(buf_, buf_ + buf_size_, tmp); free(buf_); buf_ = tmp; } else { if (munmap(buf_, real_buf_size_) != 0) { gu_throw_error(errno) << "munmap() failed"; } if (ftruncate(fd_, sz) == -1) { gu_throw_error(errno) << "fruncate() failed"; } byte_t* tmp(reinterpret_cast( mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd_, 0))); if (tmp == MAP_FAILED) { buf_ = 0; clear(); gu_throw_error(ENOMEM) << "mmap() failed"; } buf_ = tmp; } } else { sz = min(threshold_, sz*2); byte_t* tmp(reinterpret_cast(realloc(buf_, sz))); if (tmp == 0) { gu_throw_error(ENOMEM) << "realloc failed"; } buf_ = tmp; } real_buf_size_ = sz; } void galera::MappedBuffer::resize(size_t sz) { reserve(sz); buf_size_ = sz; } void galera::MappedBuffer::clear() { if (fd_ != -1) { if (buf_ != 0) munmap(buf_, real_buf_size_); while (close(fd_) == EINTR) { } unlink(file_.c_str()); } else { free(buf_); } fd_ = -1; buf_ = 0; buf_size_ = 0; real_buf_size_ = 0; } percona-xtradb-cluster-galera/galera/src/mapped_buffer.hpp0000644000000000000000000000331012247075736024201 0ustar rootroot00000000000000// // Copyright (C) 2010 Codership Oy // #ifndef GALERA_MAPPED_BUFFER_HPP #define GALERA_MAPPED_BUFFER_HPP #include #include "gu_buffer.hpp" namespace galera { class MappedBuffer { public: typedef gu::byte_t& reference; typedef gu::byte_t const& const_reference; typedef gu::byte_t* iterator; typedef gu::byte_t const* const_iterator; MappedBuffer(const std::string& working_dir, size_t threshold = 1 << 20); ~MappedBuffer(); reference operator[](size_t i) { return buf_[i]; } const_reference operator[](size_t i) const { return buf_[i]; } void reserve(size_t sz); void resize(size_t sz); void clear(); size_t size() const { return buf_size_; } bool empty() const { return (buf_size_ == 0); } iterator begin() { return buf_; } iterator end() { return (buf_ + buf_size_); } const_iterator begin() const { return buf_; } const_iterator end() const { return (buf_ + buf_size_); } private: MappedBuffer(const MappedBuffer&); void operator=(const MappedBuffer&); const std::string& working_dir_; // working dir for data files std::string file_; int fd_; // file descriptor size_t threshold_; // in-memory threshold gu::byte_t* buf_; // data buffer size_t buf_size_; // buffer size (inserted data size) size_t real_buf_size_; // real buffer size (allocated size) }; } #endif // GALERA_MAPPED_BUFFER_HPP percona-xtradb-cluster-galera/galera/src/monitor.hpp0000644000000000000000000003564612247075736023112 0ustar rootroot00000000000000// // Copyright (C) 2010 Codership Oy // #ifndef GALERA_MONITOR_HPP #define GALERA_MONITOR_HPP #include "trx_handle.hpp" #include #include namespace galera { template class Monitor { private: struct Process { Process() : obj_(0), cond_(), wait_cond_(), state_(S_IDLE) { } const C* obj_; gu::Cond cond_; gu::Cond wait_cond_; enum State { S_IDLE, // Slot is free S_WAITING, // Waiting to enter applying critical section S_CANCELED, S_APPLYING, // Applying S_FINISHED // Finished } state_; private: // non-copyable Process(const Process& other); void operator=(const Process&); }; static const ssize_t process_size_ = (1ULL << 16); static const size_t process_mask_ = process_size_ - 1; public: Monitor() : mutex_(), cond_(), last_entered_(-1), last_left_(-1), drain_seqno_(LLONG_MAX), process_(new Process[process_size_]), entered_(0), oooe_(0), oool_(0), win_size_(0), locked_(false) { } ~Monitor() { delete[] process_; if (entered_ > 0) { log_info << "mon: entered " << entered_ << " oooe fraction " << double(oooe_)/entered_ << " oool fraction " << double(oool_)/entered_; } else { log_info << "apply mon: entered 0"; } } void set_initial_position(wsrep_seqno_t seqno) { gu::Lock lock(mutex_); if (last_entered_ == -1 || seqno == -1) { // first call or reset last_entered_ = last_left_ = seqno; } else { // drain monitor up to seqno but don't reset last_entered_ // or last_left_ drain_common(seqno, lock); drain_seqno_ = LLONG_MAX; } if (seqno != -1) { const size_t idx(indexof(seqno)); process_[idx].wait_cond_.broadcast(); } } void enter(C& obj) { const wsrep_seqno_t obj_seqno(obj.seqno()); const size_t idx(indexof(obj_seqno)); gu::Lock lock(mutex_); assert(obj_seqno > last_left_); pre_enter(obj, lock); if (gu_likely(process_[idx].state_ != Process::S_CANCELED)) { assert(process_[idx].state_ == Process::S_IDLE); process_[idx].state_ = Process::S_WAITING; process_[idx].obj_ = &obj; while (may_enter(obj) == false && process_[idx].state_ == Process::S_WAITING) { obj.unlock(); lock.wait(process_[idx].cond_); obj.lock(); } if (process_[idx].state_ != Process::S_CANCELED) { assert(process_[idx].state_ == Process::S_WAITING || process_[idx].state_ == Process::S_APPLYING); process_[idx].state_ = Process::S_APPLYING; ++entered_; oooe_ += ((last_left_ + 1) < obj_seqno); win_size_ += (last_entered_ - last_left_); return; } } assert(process_[idx].state_ == Process::S_CANCELED); process_[idx].state_ = Process::S_IDLE; gu_throw_error(EINTR); } void leave(const C& obj) { #ifndef NDEBUG size_t idx(indexof(obj.seqno())); #endif /* NDEBUG */ gu::Lock lock(mutex_); assert(process_[idx].state_ == Process::S_APPLYING || process_[idx].state_ == Process::S_CANCELED); assert(process_[indexof(last_left_)].state_ == Process::S_IDLE); post_leave(obj, lock); } void self_cancel(C& obj) { wsrep_seqno_t const obj_seqno(obj.seqno()); size_t idx(indexof(obj_seqno)); gu::Lock lock(mutex_); assert(obj_seqno > last_left_); while (obj_seqno - last_left_ >= process_size_) // TODO: exit on error { log_warn << "Trying to self-cancel seqno out of process " << "space: obj_seqno - last_left_ = " << obj_seqno << " - " << last_left_ << " = " << (obj_seqno - last_left_) << ", process_size_: " << process_size_ << ". Deadlock is very likely."; obj.unlock(); lock.wait(cond_); obj.lock(); } assert(process_[idx].state_ == Process::S_IDLE || process_[idx].state_ == Process::S_CANCELED); if (obj_seqno > last_entered_) last_entered_ = obj_seqno; if (obj_seqno <= drain_seqno_) { post_leave(obj, lock); } else { process_[idx].state_ = Process::S_FINISHED; } } void interrupt(const C& obj) { size_t idx (indexof(obj.seqno())); gu::Lock lock(mutex_); while (obj.seqno() - last_left_ >= process_size_) // TODO: exit on error { lock.wait(cond_); } if ((process_[idx].state_ == Process::S_IDLE && obj.seqno() > last_left_ ) || process_[idx].state_ == Process::S_WAITING ) { process_[idx].state_ = Process::S_CANCELED; process_[idx].cond_.signal(); // since last_left + 1 cannot be <= S_WAITING we're not // modifying a window here. No broadcasting. } else { log_debug << "interrupting " << obj.seqno() << " state " << process_[idx].state_ << " le " << last_entered_ << " ll " << last_left_; } } wsrep_seqno_t last_left() const { gu::Lock lock(mutex_); return last_left_; } ssize_t size() const { return process_size_; } bool would_block (wsrep_seqno_t seqno) const { return (seqno - last_left_ >= process_size_ || seqno > drain_seqno_); } void drain(wsrep_seqno_t seqno) { gu::Lock lock(mutex_); while (drain_seqno_ != LLONG_MAX) { lock.wait(cond_); } drain_common(seqno, lock); // there can be some stale canceled entries update_last_left(); drain_seqno_ = LLONG_MAX; cond_.broadcast(); } /*! Locks the monitor and/or increments lock counter; * throws EALREADY if the monitor is already locked */ void lock() { gu::Lock lock(mutex_); assert(locked_ >= 0); if (locked_ > 0) { log_warn << "Attempt to lock an already locked monitor."; locked_++; if (locked_ > 0) { gu_throw_error(EALREADY); } else { gu_throw_fatal << "More than " << (locked_ - 1) << " concurrent locks."; } } if (last_entered_ != -1) { while (drain_seqno_ != LLONG_MAX) lock.wait(cond_); /*! @note: last_entered_ probably changed since last check */ drain_common(last_entered_, lock); /* would_block() should return true when drain_seqno_ is set * so the monitor should be totally empty at this point. */ } locked_ = 1; log_debug << "Locked local monitor at " << (last_left_ + 1); } /*! Decrements lock counter and unlocks monitor if counter is 0; * throws EBUSY if lock counter is not 0 */ void unlock() { gu::Lock lock(mutex_); assert (locked_ >= 0); if (0 == locked_) { assert (locked_ != 0); gu_throw_error(EALREADY) << "Attempt to unlock an already unlocked monitor"; } locked_--; if (0 == locked_) { update_last_left(); drain_seqno_ = LLONG_MAX; cond_.broadcast(); log_debug << "Unlocked local monitor at " << last_left_; } else { gu_throw_error(EBUSY); } } void wait(wsrep_seqno_t seqno) { gu::Lock lock(mutex_); if (last_left_ < seqno) { size_t idx(indexof(seqno)); lock.wait(process_[idx].wait_cond_); } } void wait(wsrep_seqno_t seqno, const gu::datetime::Date& wait_until) { gu::Lock lock(mutex_); if (last_left_ < seqno) { size_t idx(indexof(seqno)); lock.wait(process_[idx].wait_cond_, wait_until); } } void get_stats(double* oooe, double* oool, double* win_size) { gu::Lock lock(mutex_); if (entered_ > 0) { *oooe = (oooe_ > 0 ? double(oooe_)/entered_ : .0); *oool = (oool_ > 0 ? double(oool_)/entered_ : .0); *win_size = (win_size_ > 0 ? double(win_size_)/entered_ : .0); } else { *oooe = .0; *oool = .0; *win_size = .0; } oooe_ = 0; oool_ = 0; win_size_ = 0; entered_ = 0; } private: size_t indexof(wsrep_seqno_t seqno) { return (seqno & process_mask_); } bool may_enter(const C& obj) const { return obj.condition(last_entered_, last_left_); } // wait until it is possible to grab slot in monitor, // update last entered void pre_enter(C& obj, gu::Lock& lock) { assert(last_left_ <= last_entered_); const wsrep_seqno_t obj_seqno(obj.seqno()); while (would_block (obj_seqno)) // TODO: exit on error { obj.unlock(); lock.wait(cond_); obj.lock(); } if (last_entered_ < obj_seqno) last_entered_ = obj_seqno; } void update_last_left() { for (wsrep_seqno_t i = last_left_ + 1; i <= last_entered_; ++i) { Process& a(process_[indexof(i)]); if (Process::S_FINISHED == a.state_) { a.state_ = Process::S_IDLE; last_left_ = i; a.wait_cond_.broadcast(); } else { break; } } assert(last_left_ <= last_entered_); } void wake_up_next() { for (wsrep_seqno_t i = last_left_ + 1; i <= last_entered_; ++i) { Process& a(process_[indexof(i)]); if (a.state_ == Process::S_WAITING && may_enter(*a.obj_) == true) { // We need to set state to APPLYING here because if // it is the last_left_ + 1 and it gets canceled in // the race that follows exit from this function, // there will be nobody to clean up and advance // last_left_. a.state_ = Process::S_APPLYING; a.cond_.signal(); } } } void post_leave(const C& obj, gu::Lock& lock) { const wsrep_seqno_t obj_seqno(obj.seqno()); const size_t idx(indexof(obj_seqno)); if (last_left_ + 1 == obj_seqno) // we're shrinking window { process_[idx].state_ = Process::S_IDLE; last_left_ = obj_seqno; process_[idx].wait_cond_.broadcast(); update_last_left(); oool_ += (last_left_ > obj_seqno); // wake up waiters that may remain above us (last_left_ // now is max) wake_up_next(); } else { process_[idx].state_ = Process::S_FINISHED; } process_[idx].obj_ = 0; assert((last_left_ >= obj_seqno && process_[idx].state_ == Process::S_IDLE) || process_[idx].state_ == Process::S_FINISHED); assert(last_left_ != last_entered_ || process_[indexof(last_left_)].state_ == Process::S_IDLE); if ((last_left_ >= obj_seqno) || // - occupied window shrinked (last_left_ >= drain_seqno_)) // - this is to notify drain that // we reached drain_seqno_ { cond_.broadcast(); } } void drain_common(wsrep_seqno_t seqno, gu::Lock& lock) { log_debug << "draining up to " << seqno; drain_seqno_ = seqno; if (last_left_ > drain_seqno_) { log_debug << "last left greater than drain seqno"; for (wsrep_seqno_t i = drain_seqno_; i <= last_left_; ++i) { const Process& a(process_[indexof(i)]); log_debug << "applier " << i << " in state " << a.state_; } } while (last_left_ < drain_seqno_) lock.wait(cond_); } Monitor(const Monitor&); void operator=(const Monitor&); gu::Mutex mutex_; gu::Cond cond_; wsrep_seqno_t last_entered_; wsrep_seqno_t last_left_; wsrep_seqno_t drain_seqno_; Process* process_; long entered_; // entered long oooe_; // out of order entered long oool_; // out of order left long win_size_; // window between last_left_ and last_entered_ int locked_; }; } #endif // GALERA_APPLY_MONITOR_HPP percona-xtradb-cluster-galera/galera/src/replicator.cpp0000644000000000000000000000032212247075736023541 0ustar rootroot00000000000000// // Copyright (C) 2010 Codership Oy // #include "replicator.hpp" namespace galera { const char* const Replicator::TRIVIAL_SST(WSREP_STATE_TRANSFER_TRIVIAL); } /* namespace galera */ percona-xtradb-cluster-galera/galera/src/replicator.hpp0000644000000000000000000001073412247075736023556 0ustar rootroot00000000000000// // Copyright (C) 2010-2012 Codership Oy // #ifndef GALERA_REPLICATOR_HPP #define GALERA_REPLICATOR_HPP #include "wsrep_api.h" #include "galera_exception.hpp" #include #include namespace galera { class Statement; class RowId; class TrxHandle; //! @class Galera // // @brief Abstract Galera replicator interface class Replicator { public: static const char* const TRIVIAL_SST; typedef enum { S_DESTROYED, S_CLOSED, S_CLOSING, S_CONNECTED, S_JOINING, S_JOINED, S_SYNCED, S_DONOR } State; Replicator() { } virtual ~Replicator() { } virtual wsrep_status_t connect(const std::string& cluster_name, const std::string& cluster_url, const std::string& state_donor, bool bootstrap) = 0; virtual wsrep_status_t close() = 0; virtual wsrep_status_t async_recv(void* recv_ctx) = 0; virtual int trx_proto_ver() const = 0; virtual TrxHandle* local_trx(wsrep_trx_id_t) = 0; virtual TrxHandle* local_trx(wsrep_ws_handle_t*, bool) = 0; virtual void unref_local_trx(TrxHandle* trx) = 0; virtual void discard_local_trx(wsrep_trx_id_t trx_id) = 0; virtual TrxHandle* local_conn_trx(wsrep_conn_id_t conn_id, bool create) = 0; virtual void discard_local_conn_trx(wsrep_conn_id_t conn_id) = 0; virtual void discard_local_conn(wsrep_conn_id_t conn_id) = 0; virtual wsrep_status_t replicate(TrxHandle* trx, wsrep_trx_meta_t*) = 0; virtual wsrep_status_t pre_commit(TrxHandle* trx, wsrep_trx_meta_t*) = 0; virtual wsrep_status_t post_commit(TrxHandle* trx) = 0; virtual wsrep_status_t post_rollback(TrxHandle* trx) = 0; virtual wsrep_status_t replay_trx(TrxHandle* trx, void* replay_ctx) = 0; virtual void abort_trx(TrxHandle* trx) = 0; virtual wsrep_status_t causal_read(wsrep_gtid_t*) = 0; virtual wsrep_status_t to_isolation_begin(TrxHandle* trx, wsrep_trx_meta_t*) = 0; virtual wsrep_status_t to_isolation_end(TrxHandle* trx) = 0; virtual wsrep_status_t sst_sent(const wsrep_gtid_t& state_id, int rcode) = 0; virtual wsrep_status_t sst_received(const wsrep_gtid_t& state_id, const void* state, size_t state_len, int rcode) = 0; // action source interface virtual void process_trx(void* recv_ctx, TrxHandle* trx) = 0; virtual void process_commit_cut(wsrep_seqno_t seq, wsrep_seqno_t seqno_l) = 0; virtual void process_conf_change(void* recv_ctx, const wsrep_view_info_t& view_info, int repl_proto, State next_state, wsrep_seqno_t seqno_l) = 0; virtual void process_state_req(void* recv_ctx, const void* req, size_t req_size, wsrep_seqno_t seqno_l, wsrep_seqno_t donor_seq) = 0; virtual void process_join(wsrep_seqno_t seqno, wsrep_seqno_t seqno_l) = 0; virtual void process_sync(wsrep_seqno_t seqno_l) = 0; virtual const struct wsrep_stats_var* stats_get() const = 0; // static void stats_free(struct wsrep_stats_var*) must be declared in // the child class /*! @throws NotFound */ virtual void param_set (const std::string& key, const std::string& value) = 0; /*! @throws NotFound */ virtual std::string param_get (const std::string& key) const = 0; virtual const gu::Config& params() const = 0; virtual wsrep_seqno_t pause() = 0; virtual void resume() = 0; virtual void desync() = 0; virtual void resync() = 0; }; } #endif // GALERA_REPLICATOR_HPP percona-xtradb-cluster-galera/galera/src/replicator_smm.cpp0000644000000000000000000014074112247075736024427 0ustar rootroot00000000000000// // Copyright (C) 2010-2012 Codership Oy // #include "galera_common.hpp" #include "replicator_smm.hpp" #include "galera_exception.hpp" #include "uuid.hpp" extern "C" { #include "galera_info.h" } #include #include static inline void apply_wscoll(void* recv_ctx, wsrep_apply_cb_t apply_cb, const galera::TrxHandle& trx, const wsrep_trx_meta_t& meta) { using galera::TrxHandle; const gu::byte_t* buf(trx.write_set_buffer().first); const size_t buf_len(trx.write_set_buffer().second); size_t offset(0); while (offset < buf_len) { // Skip key segment std::pair k( galera::WriteSet::segment(buf, buf_len, offset)); offset = k.first + k.second; // Data part std::pair d( galera::WriteSet::segment(buf, buf_len, offset)); offset = d.first + d.second; wsrep_cb_status_t const err( apply_cb (recv_ctx, buf + d.first, d.second, TrxHandle::trx_flags_to_wsrep_flags( trx.flags()), &meta)); if (gu_unlikely(err > 0)) { std::ostringstream os; os << "Failed to apply app buffer: " << "seqno: "<< trx.global_seqno() << ", status: " << err; galera::ApplyException ae(os.str(), err); GU_TRACE(ae); throw ae; } } assert(offset == buf_len); return; } static void apply_trx_ws(void* recv_ctx, wsrep_apply_cb_t apply_cb, wsrep_commit_cb_t commit_cb, const galera::TrxHandle& trx, const wsrep_trx_meta_t& meta) { using galera::TrxHandle; static const size_t max_apply_attempts(10); size_t attempts(1); do { try { if (trx.is_toi()) { log_debug << "Executing TO isolated action: " << trx; } gu_trace(apply_wscoll(recv_ctx, apply_cb, trx, meta)); if (trx.is_toi()) { log_debug << "Done executing TO isolated action: " << trx.global_seqno(); } break; } catch (galera::ApplyException& e) { if (trx.is_toi()) { log_warn << "Ignoring error for TO isolated action: " << trx; break; } else { int const err(e.status()); if (err > 0) { wsrep_bool_t unused(false); int const rcode( commit_cb( recv_ctx, TrxHandle::trx_flags_to_wsrep_flags(trx.flags()), &meta, &unused, false)); if (WSREP_OK != rcode) { gu_throw_fatal << "Rollback failed. Trx: " << trx; } ++attempts; if (attempts <= max_apply_attempts) { log_warn << e.what() << "\nRetrying " << attempts << "th time"; } } else { GU_TRACE(e); throw; } } } } while (attempts <= max_apply_attempts); if (gu_unlikely(attempts > max_apply_attempts)) { std::ostringstream msg; msg << "Failed to apply trx " << trx.global_seqno() << " " << max_apply_attempts << " times"; throw galera::ApplyException(msg.str(), WSREP_CB_FAILURE); } return; } std::ostream& galera::operator<<(std::ostream& os, ReplicatorSMM::State state) { switch (state) { case ReplicatorSMM::S_DESTROYED: return (os << "DESTROYED"); case ReplicatorSMM::S_CLOSED: return (os << "CLOSED"); case ReplicatorSMM::S_CLOSING: return (os << "CLOSING"); case ReplicatorSMM::S_CONNECTED: return (os << "CONNECTED"); case ReplicatorSMM::S_JOINING: return (os << "JOINING"); case ReplicatorSMM::S_JOINED: return (os << "JOINED"); case ReplicatorSMM::S_SYNCED: return (os << "SYNCED"); case ReplicatorSMM::S_DONOR: return (os << "DONOR"); } gu_throw_fatal << "invalid state " << static_cast(state); } ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// // Public ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// galera::ReplicatorSMM::ReplicatorSMM(const struct wsrep_init_args* args) : logger_ (reinterpret_cast(args->logger_cb)), config_ (args->options), set_defaults_ (config_, defaults, args->node_address), trx_proto_ver_ (-1), str_proto_ver_ (-1), protocol_version_ (-1), state_ (S_CLOSED), sst_state_ (SST_NONE), co_mode_ (CommitOrder::from_string( config_.get(Param::commit_order))), data_dir_ (args->data_dir ? args->data_dir : ""), state_file_ (data_dir_.length() ? data_dir_+'/'+GALERA_STATE_FILE : GALERA_STATE_FILE), st_ (state_file_), uuid_ (WSREP_UUID_UNDEFINED), state_uuid_ (WSREP_UUID_UNDEFINED), state_uuid_str_ (), cc_seqno_ (WSREP_SEQNO_UNDEFINED), app_ctx_ (args->app_ctx), view_cb_ (args->view_handler_cb), apply_cb_ (args->apply_cb), commit_cb_ (args->commit_cb), sst_donate_cb_ (args->sst_donate_cb), synced_cb_ (args->synced_cb), sst_donor_ (), sst_uuid_ (WSREP_UUID_UNDEFINED), sst_seqno_ (WSREP_SEQNO_UNDEFINED), sst_mutex_ (), sst_cond_ (), sst_retry_sec_ (1), ist_sst_ (false), gcache_ (config_, data_dir_), gcs_ (config_, gcache_, MAX_PROTO_VER, args->proto_ver, args->node_name, args->node_incoming), service_thd_ (gcs_), as_ (0), gcs_as_ (gcs_, *this, gcache_), ist_receiver_ (config_, args->node_address), ist_senders_ (gcs_, gcache_), wsdb_ (), cert_ (config_), local_monitor_ (), apply_monitor_ (), commit_monitor_ (), causal_read_timeout_(config_.get(Param::causal_read_timeout)), receivers_ (), replicated_ (), replicated_bytes_ (), local_commits_ (), local_rollbacks_ (), local_cert_failures_(), local_replays_ (), incoming_list_ (""), incoming_mutex_ (), wsrep_stats_ () { // @todo add guards (and perhaps actions) state_.add_transition(Transition(S_CLOSED, S_DESTROYED)); state_.add_transition(Transition(S_CLOSED, S_CONNECTED)); state_.add_transition(Transition(S_CLOSING, S_CLOSED)); state_.add_transition(Transition(S_CONNECTED, S_CLOSING)); state_.add_transition(Transition(S_CONNECTED, S_CONNECTED)); state_.add_transition(Transition(S_CONNECTED, S_JOINING)); // the following is possible only when bootstrapping new cluster // (trivial wsrep_cluster_address) state_.add_transition(Transition(S_CONNECTED, S_JOINED)); // the following are possible on PC remerge state_.add_transition(Transition(S_CONNECTED, S_DONOR)); state_.add_transition(Transition(S_CONNECTED, S_SYNCED)); state_.add_transition(Transition(S_JOINING, S_CLOSING)); // the following is possible if one non-prim conf follows another state_.add_transition(Transition(S_JOINING, S_CONNECTED)); state_.add_transition(Transition(S_JOINING, S_JOINED)); state_.add_transition(Transition(S_JOINED, S_CLOSING)); state_.add_transition(Transition(S_JOINED, S_CONNECTED)); state_.add_transition(Transition(S_JOINED, S_SYNCED)); state_.add_transition(Transition(S_SYNCED, S_CLOSING)); state_.add_transition(Transition(S_SYNCED, S_CONNECTED)); state_.add_transition(Transition(S_SYNCED, S_DONOR)); state_.add_transition(Transition(S_DONOR, S_CLOSING)); state_.add_transition(Transition(S_DONOR, S_CONNECTED)); state_.add_transition(Transition(S_DONOR, S_JOINED)); local_monitor_.set_initial_position(0); wsrep_uuid_t uuid; wsrep_seqno_t seqno; st_.get (uuid, seqno); if (0 != args->state_id && args->state_id->uuid != WSREP_UUID_UNDEFINED && args->state_id->uuid == uuid && seqno == WSREP_SEQNO_UNDEFINED) { /* non-trivial recovery information provided on startup, and db is safe * so use recovered seqno value */ seqno = args->state_id->seqno; } log_debug << "End state: " << uuid << ':' << seqno << " #################"; update_state_uuid (uuid); cc_seqno_ = seqno; // is it needed here? apply_monitor_.set_initial_position(seqno); if (co_mode_ != CommitOrder::BYPASS) commit_monitor_.set_initial_position(seqno); cert_.assign_initial_position(seqno, trx_proto_ver_); build_stats_vars(wsrep_stats_); } galera::ReplicatorSMM::~ReplicatorSMM() { log_info << "dtor state: " << state_(); switch (state_()) { case S_CONNECTED: case S_JOINING: case S_JOINED: case S_SYNCED: case S_DONOR: close(); case S_CLOSING: // @todo wait that all users have left the building case S_CLOSED: ist_senders_.cancel(); break; case S_DESTROYED: break; } } wsrep_status_t galera::ReplicatorSMM::connect(const std::string& cluster_name, const std::string& cluster_url, const std::string& state_donor, bool const bootstrap) { sst_donor_ = state_donor; service_thd_.reset(); ssize_t err; wsrep_status_t ret(WSREP_OK); wsrep_seqno_t const seqno(cert_.position()); wsrep_uuid_t const gcs_uuid(seqno < 0 ? WSREP_UUID_UNDEFINED :state_uuid_); log_info << "Setting initial position to " << gcs_uuid << ':' << seqno; if ((err = gcs_.set_initial_position(gcs_uuid, seqno)) != 0) { log_error << "gcs init failed:" << strerror(-err); ret = WSREP_NODE_FAIL; } gcache_.reset(); if (ret == WSREP_OK && (err = gcs_.connect(cluster_name, cluster_url, bootstrap)) != 0) { log_error << "gcs connect failed: " << strerror(-err); ret = WSREP_NODE_FAIL; } if (ret == WSREP_OK) { state_.shift_to(S_CONNECTED); } return ret; } wsrep_status_t galera::ReplicatorSMM::close() { if (state_() != S_CLOSED) { gcs_.close(); } return WSREP_OK; } wsrep_status_t galera::ReplicatorSMM::async_recv(void* recv_ctx) { assert(recv_ctx != 0); if (state_() == S_CLOSED || state_() == S_CLOSING) { log_error <<"async recv cannot start, provider in closed/closing state"; return WSREP_FATAL; } ++receivers_; as_ = &gcs_as_; bool exit_loop(false); wsrep_status_t retval(WSREP_OK); while (WSREP_OK == retval && state_() != S_CLOSING) { ssize_t rc; while (gu_unlikely((rc = as_->process(recv_ctx, exit_loop)) == -ECANCELED)) { recv_IST(recv_ctx); // hack: prevent fast looping until ist controlling thread // resumes gcs prosessing usleep(10000); } if (gu_unlikely(rc <= 0)) { retval = WSREP_CONN_FAIL; } else if (gu_unlikely(exit_loop == true)) { assert(WSREP_OK == retval); if (receivers_.sub_and_fetch(1) > 0) { log_info << "Slave thread exiting on request."; break; } ++receivers_; log_warn << "Refusing exit for the last slave thread."; } } /* exiting loop already did proper checks */ if (!exit_loop && receivers_.sub_and_fetch(1) == 0) { if (state_() != S_CLOSING) { log_warn << "Broken shutdown sequence, provider state: " << state_() << ", retval: " << retval; assert (0); /* avoid abort in production */ state_.shift_to(S_CLOSING); } state_.shift_to(S_CLOSED); } log_debug << "Slave thread exit. Return code: " << retval; return retval; } galera::TrxHandle* galera::ReplicatorSMM::local_trx(wsrep_trx_id_t trx_id) { return wsdb_.get_trx(trx_proto_ver_, uuid_, trx_id, false); } galera::TrxHandle* galera::ReplicatorSMM::local_trx(wsrep_ws_handle_t* handle, bool create) { TrxHandle* trx; assert(handle != 0); if (handle->opaque != 0) { trx = reinterpret_cast(handle->opaque); assert(trx->trx_id() == handle->trx_id || wsrep_trx_id_t(-1) == handle->trx_id); trx->ref(); } else { trx = wsdb_.get_trx(trx_proto_ver_, uuid_, handle->trx_id, create); handle->opaque = trx; } return trx; } void galera::ReplicatorSMM::unref_local_trx(TrxHandle* trx) { assert(trx->refcnt() > 1); trx->unref(); } void galera::ReplicatorSMM::discard_local_trx(wsrep_trx_id_t trx_id) { wsdb_.discard_trx(trx_id); } galera::TrxHandle* galera::ReplicatorSMM::local_conn_trx(wsrep_conn_id_t conn_id, bool create) { return wsdb_.get_conn_query(trx_proto_ver_, uuid_, conn_id, create); } void galera::ReplicatorSMM::discard_local_conn_trx(wsrep_conn_id_t conn_id) { wsdb_.discard_conn_query(conn_id); } void galera::ReplicatorSMM::discard_local_conn(wsrep_conn_id_t conn_id) { wsdb_.discard_conn(conn_id); } void galera::ReplicatorSMM::apply_trx(void* recv_ctx, TrxHandle* trx) { assert(trx != 0); assert(trx->global_seqno() > 0); assert(trx->is_certified() == true); assert(trx->global_seqno() > apply_monitor_.last_left()); assert(trx->is_local() == false); ApplyOrder ao(*trx); CommitOrder co(*trx, co_mode_); gu_trace(apply_monitor_.enter(ao)); trx->set_state(TrxHandle::S_APPLYING); wsrep_trx_meta_t meta = {{state_uuid_, trx->global_seqno() }, trx->depends_seqno()}; gu_trace(apply_trx_ws(recv_ctx, apply_cb_, commit_cb_, *trx, meta)); /* at this point any exception in apply_trx_ws() is fatal, not * catching anything. */ if (gu_likely(co_mode_ != CommitOrder::BYPASS)) { gu_trace(commit_monitor_.enter(co)); } trx->set_state(TrxHandle::S_COMMITTING); wsrep_bool_t exit_loop(false); wsrep_cb_status_t const rcode( commit_cb_( recv_ctx, TrxHandle::trx_flags_to_wsrep_flags(trx->flags()), &meta, &exit_loop, true)); if (gu_unlikely (rcode > 0)) gu_throw_fatal << "Commit failed. Trx: " << trx; if (gu_likely(co_mode_ != CommitOrder::BYPASS)) { commit_monitor_.leave(co); } trx->set_state(TrxHandle::S_COMMITTED); apply_monitor_.leave(ao); if (trx->local_seqno() != -1) { // trx with local seqno -1 originates from IST (or other source not gcs) report_last_committed(cert_.set_trx_committed(trx)); } trx->set_exit_loop(exit_loop); } wsrep_status_t galera::ReplicatorSMM::replicate(TrxHandle* trx, wsrep_trx_meta_t* meta) { if (state_() < S_JOINED) return WSREP_TRX_FAIL; assert(trx->state() == TrxHandle::S_EXECUTING || trx->state() == TrxHandle::S_MUST_ABORT); assert(trx->local_seqno() == WSREP_SEQNO_UNDEFINED && trx->global_seqno() == WSREP_SEQNO_UNDEFINED); wsrep_status_t retval(WSREP_TRX_FAIL); if (trx->state() == TrxHandle::S_MUST_ABORT) { must_abort: trx->set_state(TrxHandle::S_ABORTING); return retval; } trx->set_last_seen_seqno(last_committed()); trx->flush(0); trx->set_state(TrxHandle::S_REPLICATING); const MappedBuffer& wscoll(trx->write_set_collection()); ssize_t rcode; gcs_action act; act.size = wscoll.size(); act.type = GCS_ACT_TORDERED; #ifndef NDEBUG act.seqno_g = GCS_SEQNO_ILL; #endif do { act.buf = &wscoll[0]; assert(act.buf); assert(act.seqno_g == GCS_SEQNO_ILL); const ssize_t gcs_handle(gcs_.schedule()); if (gu_unlikely(gcs_handle < 0)) { log_debug << "gcs schedule " << strerror(-gcs_handle); trx->set_state(TrxHandle::S_MUST_ABORT); goto must_abort; } trx->set_gcs_handle(gcs_handle); trx->unlock(); rcode = gcs_.repl(act, true); trx->lock(); } while (rcode == -EAGAIN && trx->state() != TrxHandle::S_MUST_ABORT && (usleep(1000), true)); if (rcode < 0) { if (rcode != -EINTR) { log_debug << "gcs_repl() failed with " << strerror(-rcode) << " for trx " << *trx; } assert(rcode != -EINTR || trx->state() == TrxHandle::S_MUST_ABORT); assert(act.seqno_l == GCS_SEQNO_ILL && act.seqno_g == GCS_SEQNO_ILL); if (trx->state() != TrxHandle::S_MUST_ABORT) { trx->set_state(TrxHandle::S_MUST_ABORT); } trx->set_gcs_handle(-1); goto must_abort; } assert(act.buf); assert(act.seqno_l != GCS_SEQNO_ILL && act.seqno_g != GCS_SEQNO_ILL); ++replicated_; replicated_bytes_ += wscoll.size(); trx->set_gcs_handle(-1); trx->set_received(act.buf, act.seqno_l, act.seqno_g); if (trx->state() == TrxHandle::S_MUST_ABORT) { retval = cert_for_aborted(trx); if (retval != WSREP_BF_ABORT) { LocalOrder lo(*trx); ApplyOrder ao(*trx); CommitOrder co(*trx, co_mode_); local_monitor_.self_cancel(lo); apply_monitor_.self_cancel(ao); if (co_mode_ !=CommitOrder::BYPASS) commit_monitor_.self_cancel(co); } else if (meta != 0) { meta->gtid.uuid = state_uuid_; meta->gtid.seqno = trx->global_seqno(); meta->depends_on = trx->depends_seqno(); } if (trx->state() == TrxHandle::S_MUST_ABORT) goto must_abort; } else { retval = WSREP_OK; } return retval; } void galera::ReplicatorSMM::abort_trx(TrxHandle* trx) { assert(trx != 0); assert(trx->is_local() == true); log_debug << "aborting trx " << *trx << " " << trx; switch (trx->state()) { case TrxHandle::S_MUST_ABORT: case TrxHandle::S_ABORTING: // guess this is here because we can have a race return; case TrxHandle::S_EXECUTING: trx->set_state(TrxHandle::S_MUST_ABORT); break; case TrxHandle::S_REPLICATING: { trx->set_state(TrxHandle::S_MUST_ABORT); // trx is in gcs repl int rc; if (trx->gcs_handle() > 0 && ((rc = gcs_.interrupt(trx->gcs_handle()))) != 0) { log_debug << "gcs_interrupt(): handle " << trx->gcs_handle() << " trx id " << trx->trx_id() << ": " << strerror(-rc); } break; } case TrxHandle::S_CERTIFYING: { trx->set_state(TrxHandle::S_MUST_ABORT); // trx is waiting in local monitor LocalOrder lo(*trx); trx->unlock(); local_monitor_.interrupt(lo); trx->lock(); break; } case TrxHandle::S_APPLYING: { trx->set_state(TrxHandle::S_MUST_ABORT); // trx is waiting in apply monitor ApplyOrder ao(*trx); trx->unlock(); apply_monitor_.interrupt(ao); trx->lock(); break; } case TrxHandle::S_COMMITTING: trx->set_state(TrxHandle::S_MUST_ABORT); if (co_mode_ != CommitOrder::BYPASS) { // trx waiting in commit monitor CommitOrder co(*trx, co_mode_); trx->unlock(); commit_monitor_.interrupt(co); trx->lock(); } break; default: gu_throw_fatal << "invalid state " << trx->state(); } } wsrep_status_t galera::ReplicatorSMM::pre_commit(TrxHandle* trx, wsrep_trx_meta_t* meta) { if (meta != 0) { meta->gtid.uuid = state_uuid_; meta->gtid.seqno = trx->global_seqno(); meta->depends_on = trx->depends_seqno(); } // State should not be checked here: If trx has been replicated, // it has to be certified and potentially applied. #528 // if (state_() < S_JOINED) return WSREP_TRX_FAIL; assert(trx->state() == TrxHandle::S_REPLICATING); assert(trx->local_seqno() > -1 && trx->global_seqno() > -1); wsrep_status_t retval(cert(trx)); if (gu_unlikely(retval != WSREP_OK)) { assert(trx->state() == TrxHandle::S_MUST_ABORT || trx->state() == TrxHandle::S_MUST_REPLAY_AM || trx->state() == TrxHandle::S_MUST_CERT_AND_REPLAY); if (trx->state() == TrxHandle::S_MUST_ABORT) { trx->set_state(TrxHandle::S_ABORTING); } return retval; } assert(trx->state() == TrxHandle::S_CERTIFYING); assert(trx->global_seqno() > apply_monitor_.last_left()); trx->set_state(TrxHandle::S_APPLYING); ApplyOrder ao(*trx); CommitOrder co(*trx, co_mode_); bool interrupted(false); try { gu_trace(apply_monitor_.enter(ao)); } catch (gu::Exception& e) { if (e.get_errno() == EINTR) { interrupted = true; } else throw; } if (gu_unlikely(interrupted) || trx->state() == TrxHandle::S_MUST_ABORT) { assert(trx->state() == TrxHandle::S_MUST_ABORT); if (interrupted) trx->set_state(TrxHandle::S_MUST_REPLAY_AM); else trx->set_state(TrxHandle::S_MUST_REPLAY_CM); retval = WSREP_BF_ABORT; } else if ((trx->flags() & TrxHandle::F_COMMIT) != 0) { trx->set_state(TrxHandle::S_COMMITTING); if (co_mode_ != CommitOrder::BYPASS) { try { gu_trace(commit_monitor_.enter(co)); } catch (gu::Exception& e) { if (e.get_errno() == EINTR) { interrupted = true; } else throw; } if (gu_unlikely(interrupted) || trx->state() == TrxHandle::S_MUST_ABORT) { assert(trx->state() == TrxHandle::S_MUST_ABORT); if (interrupted) trx->set_state(TrxHandle::S_MUST_REPLAY_CM); else trx->set_state(TrxHandle::S_MUST_REPLAY); retval = WSREP_BF_ABORT; } } } else { trx->set_state(TrxHandle::S_EXECUTING); } assert((retval == WSREP_OK && (trx->state() == TrxHandle::S_COMMITTING || trx->state() == TrxHandle::S_EXECUTING)) || (retval == WSREP_TRX_FAIL && trx->state() == TrxHandle::S_ABORTING) || (retval == WSREP_BF_ABORT && ( trx->state() == TrxHandle::S_MUST_REPLAY_AM || trx->state() == TrxHandle::S_MUST_REPLAY_CM || trx->state() == TrxHandle::S_MUST_REPLAY))); return retval; } wsrep_status_t galera::ReplicatorSMM::replay_trx(TrxHandle* trx, void* trx_ctx) { assert(trx->state() == TrxHandle::S_MUST_CERT_AND_REPLAY || trx->state() == TrxHandle::S_MUST_REPLAY_AM || trx->state() == TrxHandle::S_MUST_REPLAY_CM || trx->state() == TrxHandle::S_MUST_REPLAY); assert(trx->trx_id() != static_cast(-1)); assert(trx->global_seqno() > apply_monitor_.last_left()); wsrep_status_t retval(WSREP_OK); switch (trx->state()) { case TrxHandle::S_MUST_CERT_AND_REPLAY: retval = cert(trx); if (retval != WSREP_OK) { // apply monitor is self canceled in cert break; } trx->set_state(TrxHandle::S_MUST_REPLAY_AM); // fall through case TrxHandle::S_MUST_REPLAY_AM: { // safety measure to make sure that all preceding trxs finish before // replaying trx->set_depends_seqno(trx->global_seqno() - 1); ApplyOrder ao(*trx); gu_trace(apply_monitor_.enter(ao)); trx->set_state(TrxHandle::S_MUST_REPLAY_CM); // fall through } case TrxHandle::S_MUST_REPLAY_CM: if (co_mode_ != CommitOrder::BYPASS) { CommitOrder co(*trx, co_mode_); gu_trace(commit_monitor_.enter(co)); } trx->set_state(TrxHandle::S_MUST_REPLAY); // fall through case TrxHandle::S_MUST_REPLAY: ++local_replays_; trx->set_state(TrxHandle::S_REPLAYING); try { wsrep_trx_meta_t meta = {{state_uuid_, trx->global_seqno() }, trx->depends_seqno()}; gu_trace(apply_trx_ws(trx_ctx, apply_cb_, commit_cb_, *trx, meta)); wsrep_bool_t unused(false); wsrep_cb_status_t rcode( commit_cb_( trx_ctx, TrxHandle::trx_flags_to_wsrep_flags(trx->flags()), &meta, &unused, true)); if (gu_unlikely(rcode > 0)) gu_throw_fatal << "Commit failed. Trx: " << trx; } catch (gu::Exception& e) { st_.mark_corrupt(); throw; } // apply, commit monitors are released in post commit return WSREP_OK; default: gu_throw_fatal << "Invalid state in replay for trx " << *trx; } log_debug << "replaying failed for trx " << *trx; trx->set_state(TrxHandle::S_ABORTING); return retval; } wsrep_status_t galera::ReplicatorSMM::post_commit(TrxHandle* trx) { if (trx->state() == TrxHandle::S_MUST_ABORT) { // This is possible in case of ALG: BF applier BF aborts // trx that has already grabbed commit monitor and is committing. // However, this should be acceptable assuming that commit // operation does not reserve any more resources and is able // to release already reserved resources. log_debug << "trx was BF aborted during commit: " << *trx; // manipulate state to avoid crash trx->set_state(TrxHandle::S_MUST_REPLAY); trx->set_state(TrxHandle::S_REPLAYING); } assert(trx->state() == TrxHandle::S_COMMITTING || trx->state() == TrxHandle::S_REPLAYING); assert(trx->local_seqno() > -1 && trx->global_seqno() > -1); CommitOrder co(*trx, co_mode_); if (co_mode_ != CommitOrder::BYPASS) commit_monitor_.leave(co); ApplyOrder ao(*trx); apply_monitor_.leave(ao); report_last_committed(cert_.set_trx_committed(trx)); trx->set_state(TrxHandle::S_COMMITTED); ++local_commits_; return WSREP_OK; } wsrep_status_t galera::ReplicatorSMM::post_rollback(TrxHandle* trx) { if (trx->state() == TrxHandle::S_MUST_ABORT) { trx->set_state(TrxHandle::S_ABORTING); } assert(trx->state() == TrxHandle::S_ABORTING || trx->state() == TrxHandle::S_EXECUTING); trx->set_state(TrxHandle::S_ROLLED_BACK); // Trx was either rolled back by user or via certification failure, // last committed report not needed since cert index state didn't change. // report_last_committed(); ++local_rollbacks_; return WSREP_OK; } wsrep_status_t galera::ReplicatorSMM::causal_read(wsrep_gtid_t* gtid) { wsrep_seqno_t cseq(static_cast(gcs_.caused())); if (cseq < 0) { log_warn << "gcs_caused() returned " << cseq << " (" << strerror(-cseq) << ')'; return WSREP_TRX_FAIL; } try { // @note: Using timed wait for monitor is currently a hack // to avoid deadlock resulting from race between monitor wait // and drain during configuration change. Instead of this, // monitor should have proper mechanism to interrupt waiters // at monitor drain and disallowing further waits until // configuration change related operations (SST etc) have been // finished. gu::datetime::Date wait_until(gu::datetime::Date::calendar() + causal_read_timeout_); if (gu_likely(co_mode_ != CommitOrder::BYPASS)) { commit_monitor_.wait(cseq, wait_until); } else { apply_monitor_.wait(cseq, wait_until); } if (gtid != 0) { gtid->uuid = state_uuid_; gtid->seqno = cseq; } ++causal_reads_; return WSREP_OK; } catch (gu::Exception& e) { log_debug << "monitor wait failed for causal read: " << e.what(); return WSREP_TRX_FAIL; } } wsrep_status_t galera::ReplicatorSMM::to_isolation_begin(TrxHandle* trx, wsrep_trx_meta_t* meta) { if (meta != 0) { meta->gtid.uuid = state_uuid_; meta->gtid.seqno = trx->global_seqno(); meta->depends_on = trx->depends_seqno(); } assert(trx->state() == TrxHandle::S_REPLICATING); assert(trx->trx_id() == static_cast(-1)); assert(trx->local_seqno() > -1 && trx->global_seqno() > -1); assert(trx->global_seqno() > apply_monitor_.last_left()); wsrep_status_t retval; switch ((retval = cert(trx))) { case WSREP_OK: { ApplyOrder ao(*trx); CommitOrder co(*trx, co_mode_); gu_trace(apply_monitor_.enter(ao)); if (co_mode_ != CommitOrder::BYPASS) try { commit_monitor_.enter(co); } catch (...) { gu_throw_fatal << "unable to enter commit monitor: " << *trx; } trx->set_state(TrxHandle::S_APPLYING); log_debug << "Executing TO isolated action: " << *trx; st_.mark_unsafe(); break; } case WSREP_TRX_FAIL: // Apply monitor is released in cert() in case of failure. trx->set_state(TrxHandle::S_ABORTING); // Called now from cert() // report_last_committed(); break; default: log_error << "unrecognized retval " << retval << " for to isolation certification for " << *trx; retval = WSREP_FATAL; break; } return retval; } wsrep_status_t galera::ReplicatorSMM::to_isolation_end(TrxHandle* trx) { assert(trx->state() == TrxHandle::S_APPLYING); log_debug << "Done executing TO isolated action: " << *trx; CommitOrder co(*trx, co_mode_); if (co_mode_ != CommitOrder::BYPASS) commit_monitor_.leave(co); ApplyOrder ao(*trx); apply_monitor_.leave(ao); st_.mark_safe(); report_last_committed(cert_.set_trx_committed(trx)); return WSREP_OK; } wsrep_status_t galera::ReplicatorSMM::sst_sent(const wsrep_gtid_t& state_id, int const rcode) { assert (rcode <= 0); assert (rcode == 0 || state_id.seqno == WSREP_SEQNO_UNDEFINED); assert (rcode != 0 || state_id.seqno >= 0); if (state_() != S_DONOR) { log_error << "sst sent called when not SST donor, state " << state_(); return WSREP_CONN_FAIL; } gcs_seqno_t seqno(rcode ? rcode : state_id.seqno); if (state_id.uuid != state_uuid_ && seqno >= 0) { // state we have sent no longer corresponds to the current group state // mark an error seqno = -EREMCHG; } try { // #557 - remove this if() when we return back to joining after SST if (!ist_sst_ || rcode < 0) gcs_.join(seqno); ist_sst_ = false; return WSREP_OK; } catch (gu::Exception& e) { log_error << "failed to recover from DONOR state: " << e.what(); return WSREP_CONN_FAIL; } } void galera::ReplicatorSMM::process_trx(void* recv_ctx, TrxHandle* trx) { assert(recv_ctx != 0); assert(trx != 0); assert(trx->local_seqno() > 0); assert(trx->global_seqno() > 0); assert(trx->last_seen_seqno() >= 0); assert(trx->depends_seqno() == -1); assert(trx->state() == TrxHandle::S_REPLICATING); wsrep_status_t const retval(cert(trx)); switch (retval) { case WSREP_OK: try { gu_trace(apply_trx(recv_ctx, trx)); } catch (std::exception& e) { st_.mark_corrupt(); log_fatal << "Failed to apply trx: " << *trx; log_fatal << e.what(); log_fatal << "Node consistency compromized, aborting..."; abort(); } break; case WSREP_TRX_FAIL: // certification failed, apply monitor has been canceled trx->set_state(TrxHandle::S_ABORTING); trx->set_state(TrxHandle::S_ROLLED_BACK); break; default: // this should not happen for remote actions gu_throw_error(EINVAL) << "unrecognized retval for remote trx certification: " << retval << " trx: " << *trx; } } void galera::ReplicatorSMM::process_commit_cut(wsrep_seqno_t seq, wsrep_seqno_t seqno_l) { assert(seq > 0); assert(seqno_l > 0); LocalOrder lo(seqno_l); gu_trace(local_monitor_.enter(lo)); cert_.purge_trxs_upto(seq); local_monitor_.leave(lo); log_debug << "Got commit cut from GCS: " << seq; } void galera::ReplicatorSMM::establish_protocol_versions (int proto_ver) { switch (proto_ver) { case 1: trx_proto_ver_ = 1; str_proto_ver_ = 0; break; case 2: trx_proto_ver_ = 1; str_proto_ver_ = 1; break; case 3: case 4: trx_proto_ver_ = 2; str_proto_ver_ = 1; break; default: log_fatal << "Configuration change resulted in an unsupported protocol " "version: " << proto_ver << ". Can't continue."; abort(); }; protocol_version_ = proto_ver; log_debug << "REPL Protocols: " << protocol_version_ << " (" << trx_proto_ver_ << ", " << str_proto_ver_ << ")"; } static bool app_wants_state_transfer (const void* const req, ssize_t const req_len) { return (req_len != (strlen(WSREP_STATE_TRANSFER_NONE) + 1) || memcmp(req, WSREP_STATE_TRANSFER_NONE, req_len)); } void galera::ReplicatorSMM::update_incoming_list(const wsrep_view_info_t& view) { static char const separator(','); ssize_t new_size(0); if (view.memb_num > 0) { new_size += view.memb_num - 1; // separators for (int i = 0; i < view.memb_num; ++i) { new_size += strlen(view.members[i].incoming); } } gu::Lock lock(incoming_mutex_); incoming_list_.clear(); incoming_list_.resize(new_size); if (new_size <= 0) return; incoming_list_ = view.members[0].incoming; for (int i = 1; i < view.memb_num; ++i) { incoming_list_ += separator; incoming_list_ += view.members[i].incoming; } } void galera::ReplicatorSMM::process_conf_change(void* recv_ctx, const wsrep_view_info_t& view_info, int repl_proto, State next_state, wsrep_seqno_t seqno_l) { assert(seqno_l > -1); update_incoming_list(view_info); LocalOrder lo(seqno_l); gu_trace(local_monitor_.enter(lo)); wsrep_seqno_t const upto(cert_.position()); apply_monitor_.drain(upto); if (co_mode_ != CommitOrder::BYPASS) commit_monitor_.drain(upto); if (view_info.my_idx >= 0) { uuid_ = view_info.members[view_info.my_idx].id; } bool const st_required(state_transfer_required(view_info)); wsrep_seqno_t const group_seqno(view_info.state_id.seqno); const wsrep_uuid_t& group_uuid (view_info.state_id.uuid); if (st_required) { log_info << "State transfer required: " << "\n\tGroup state: " << group_uuid << ":" << group_seqno << "\n\tLocal state: " << state_uuid_<< ":" << apply_monitor_.last_left(); if (S_CONNECTED != state_()) state_.shift_to(S_CONNECTED); } void* app_req(0); size_t app_req_len(0); const_cast(view_info).state_gap = st_required; wsrep_cb_status_t const rcode( view_cb_(app_ctx_, recv_ctx, &view_info, 0, 0, &app_req, &app_req_len)); if (WSREP_CB_SUCCESS != rcode) { assert(app_req_len <= 0); close(); gu_throw_fatal << "View callback failed. This is unrecoverable, " "restart required."; } else if (st_required && 0 == app_req_len && state_uuid_ != group_uuid) { close(); gu_throw_fatal << "Local state UUID " << state_uuid_ << " is different from group state UUID " << group_uuid << ", and SST request is null: restart required."; } if (view_info.view >= 0) // Primary configuration { establish_protocol_versions (repl_proto); // we have to reset cert initial position here, SST does not contain // cert index yet (see #197). cert_.assign_initial_position(group_seqno, trx_proto_ver_); // record state seqno, needed for IST on DONOR cc_seqno_ = group_seqno; bool const app_wants_st(app_wants_state_transfer(app_req, app_req_len)); if (st_required && app_wants_st) { request_state_transfer (recv_ctx, group_uuid, group_seqno, app_req, app_req_len); } else { if (view_info.view == 1 || !app_wants_st) { update_state_uuid (group_uuid); apply_monitor_.set_initial_position(group_seqno); if (co_mode_ != CommitOrder::BYPASS) commit_monitor_.set_initial_position(group_seqno); } if (state_() == S_CONNECTED || state_() == S_DONOR) { switch (next_state) { case S_JOINING: state_.shift_to(S_JOINING); break; case S_DONOR: if (state_() == S_CONNECTED) { state_.shift_to(S_DONOR); } break; case S_JOINED: state_.shift_to(S_JOINED); break; case S_SYNCED: state_.shift_to(S_SYNCED); synced_cb_(app_ctx_); break; default: log_debug << "next_state " << next_state; break; } } st_.set(state_uuid_, WSREP_SEQNO_UNDEFINED); } if (state_() == S_JOINING && sst_state_ != SST_NONE) { /* There are two reasons we can be here: * 1) we just got state transfer in request_state_transfer() above; * 2) we failed here previously (probably due to partition). */ try { gcs_.join(sst_seqno_); sst_state_ = SST_NONE; } catch (gu::Exception& e) { log_error << "Failed to JOIN the cluster after SST"; } } } else { // Non-primary configuration if (state_uuid_ != WSREP_UUID_UNDEFINED) { st_.set (state_uuid_, apply_monitor_.last_left()); } if (next_state != S_CONNECTED && next_state != S_CLOSING) { log_fatal << "Internal error: unexpected next state for " << "non-prim: " << next_state << ". Restart required."; abort(); } state_.shift_to(next_state); } local_monitor_.leave(lo); gcs_.resume_recv(); free(app_req); } void galera::ReplicatorSMM::process_join(wsrep_seqno_t seqno_j, wsrep_seqno_t seqno_l) { LocalOrder lo(seqno_l); gu_trace(local_monitor_.enter(lo)); wsrep_seqno_t const upto(cert_.position()); apply_monitor_.drain(upto); if (co_mode_ != CommitOrder::BYPASS) commit_monitor_.drain(upto); if (seqno_j < 0 && S_JOINING == state_()) { // #595, @todo: find a way to re-request state transfer log_fatal << "Failed to receive state transfer: " << seqno_j << " (" << strerror (-seqno_j) << "), need to restart."; abort(); } else { state_.shift_to(S_JOINED); } local_monitor_.leave(lo); } void galera::ReplicatorSMM::process_sync(wsrep_seqno_t seqno_l) { LocalOrder lo(seqno_l); gu_trace(local_monitor_.enter(lo)); wsrep_seqno_t const upto(cert_.position()); apply_monitor_.drain(upto); if (co_mode_ != CommitOrder::BYPASS) commit_monitor_.drain(upto); state_.shift_to(S_SYNCED); synced_cb_(app_ctx_); local_monitor_.leave(lo); } wsrep_seqno_t galera::ReplicatorSMM::pause() { try { gu_trace(local_monitor_.lock()); } catch (gu::Exception& e) { if (e.get_errno() == EALREADY) return cert_.position(); throw; } wsrep_seqno_t const ret(cert_.position()); apply_monitor_.drain(ret); assert (apply_monitor_.last_left() >= ret); if (co_mode_ != CommitOrder::BYPASS) { commit_monitor_.drain(ret); assert (commit_monitor_.last_left() >= ret); } st_.set(state_uuid_, ret); log_info << "Provider paused at " << state_uuid_ << ':' << ret; return ret; } void galera::ReplicatorSMM::resume() { st_.set(state_uuid_, WSREP_SEQNO_UNDEFINED); try { gu_trace(local_monitor_.unlock()); } catch (gu::Exception& e) { if (e.get_errno() == EBUSY) { /* monitor is still locked, restore saved state */ st_.set(state_uuid_, cert_.position()); return; } throw; } log_info << "Provider resumed."; } void galera::ReplicatorSMM::desync() { wsrep_seqno_t seqno_l; ssize_t const ret(gcs_.desync(&seqno_l)); if (seqno_l > 0) { LocalOrder lo(seqno_l); // need to process it regardless of ret value if (ret == 0) { /* #706 - the check below must be state request-specific. We are not holding any locks here and must be able to wait like any other action. However practice may prove different, leaving it here as a reminder. if (local_monitor_.would_block(seqno_l)) { gu_throw_error (-EDEADLK) << "Ran out of resources waiting to " << "desync the node. " << "The node must be restarted."; } */ local_monitor_.enter(lo); state_.shift_to(S_DONOR); local_monitor_.leave(lo); } else { local_monitor_.self_cancel(lo); } } if (ret) { gu_throw_error (-ret) << "Node desync failed."; } } void galera::ReplicatorSMM::resync() { gcs_.join(commit_monitor_.last_left()); } ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// //// Private ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// wsrep_status_t galera::ReplicatorSMM::cert(TrxHandle* trx) { assert(trx->state() == TrxHandle::S_REPLICATING || trx->state() == TrxHandle::S_MUST_CERT_AND_REPLAY); assert(trx->local_seqno() != WSREP_SEQNO_UNDEFINED && trx->global_seqno() != WSREP_SEQNO_UNDEFINED && trx->last_seen_seqno() != WSREP_SEQNO_UNDEFINED); trx->set_state(TrxHandle::S_CERTIFYING); LocalOrder lo(*trx); ApplyOrder ao(*trx); CommitOrder co(*trx, co_mode_); bool interrupted(false); try { gu_trace(local_monitor_.enter(lo)); } catch (gu::Exception& e) { if (e.get_errno() == EINTR) { interrupted = true; } else throw; } wsrep_status_t retval(WSREP_OK); if (gu_likely (!interrupted)) { switch (cert_.append_trx(trx)) { case Certification::TEST_OK: if (trx->global_seqno() > apply_monitor_.last_left()) { if (trx->state() == TrxHandle::S_CERTIFYING) { retval = WSREP_OK; } else { assert(trx->state() == TrxHandle::S_MUST_ABORT); trx->set_state(TrxHandle::S_MUST_REPLAY_AM); retval = WSREP_BF_ABORT; } } else { // this can happen after SST position has been submitted // but not all actions preceding SST initial position // have been processed trx->set_state(TrxHandle::S_MUST_ABORT); report_last_committed(cert_.set_trx_committed(trx)); retval = WSREP_TRX_FAIL; } break; case Certification::TEST_FAILED: if (trx->global_seqno() > apply_monitor_.last_left()) { if (gu_unlikely(trx->is_toi())) // small sanity check { log_error << "Certification failed for TO isolated action: " << *trx; assert(0); // should never happen } apply_monitor_.self_cancel(ao); if (co_mode_ != CommitOrder::BYPASS) commit_monitor_.self_cancel(co); } trx->set_state(TrxHandle::S_MUST_ABORT); local_cert_failures_ += trx->is_local(); report_last_committed(cert_.set_trx_committed(trx)); retval = WSREP_TRX_FAIL; break; } // we probably want to do it 'in order' for std::map reasons, so keeping // it inside the monitor /*! @todo: benchmark both variants on SMP */ gcache_.seqno_assign (trx->action(), trx->global_seqno(), trx->depends_seqno(), trx->is_local()); // frees local actions local_monitor_.leave(lo); } else { retval = cert_for_aborted(trx); if (retval != WSREP_BF_ABORT) { local_monitor_.self_cancel(lo); apply_monitor_.self_cancel(ao); if (co_mode_ != CommitOrder::BYPASS) commit_monitor_.self_cancel(co); } } return retval; } wsrep_status_t galera::ReplicatorSMM::cert_for_aborted(TrxHandle* trx) { wsrep_status_t retval(WSREP_OK); switch (cert_.test(trx, false)) { case Certification::TEST_OK: trx->set_state(TrxHandle::S_MUST_CERT_AND_REPLAY); retval = WSREP_BF_ABORT; break; case Certification::TEST_FAILED: if (trx->state() != TrxHandle::S_MUST_ABORT) { trx->set_state(TrxHandle::S_MUST_ABORT); } retval = WSREP_TRX_FAIL; gcache_.seqno_assign (trx->action(), trx->global_seqno(), -1, trx->is_local()); break; } return retval; } void galera::ReplicatorSMM::update_state_uuid (const wsrep_uuid_t& uuid) { if (state_uuid_ != uuid) { *(const_cast(&state_uuid_)) = uuid; std::ostringstream os; os << state_uuid_; strncpy(const_cast(state_uuid_str_), os.str().c_str(), sizeof(state_uuid_str_)); } st_.set(uuid, WSREP_SEQNO_UNDEFINED); } void galera::ReplicatorSMM::abort() { gcs_.close(); gu_abort(); } percona-xtradb-cluster-galera/galera/src/replicator_smm.hpp0000644000000000000000000003532012247075736024430 0ustar rootroot00000000000000// // Copyright (C) 2010-2013 Codership Oy // //! @file replicator_smm.hpp // // @brief Galera Synchronous Multi-Master replicator // #ifndef GALERA_REPLICATOR_SMM_HPP #define GALERA_REPLICATOR_SMM_HPP #include "replicator.hpp" #include "GCache.hpp" #include "gcs.hpp" #include "monitor.hpp" #include "wsdb.hpp" #include "certification.hpp" #include "trx_handle.hpp" #include "write_set.hpp" #include "galera_service_thd.hpp" #include "fsm.hpp" #include "gcs_action_source.hpp" #include "ist.hpp" #include "gu_atomic.hpp" #include "saved_state.hpp" #include namespace galera { class ReplicatorSMM : public Replicator { public: typedef enum { SST_NONE, SST_WAIT, SST_REQ_FAILED, SST_FAILED } SstState; static const size_t N_STATES = S_DONOR + 1; ReplicatorSMM(const wsrep_init_args* args); ~ReplicatorSMM(); int trx_proto_ver() const { return trx_proto_ver_; } wsrep_status_t connect(const std::string& cluster_name, const std::string& cluster_url, const std::string& state_donor, bool bootstrap); wsrep_status_t close(); wsrep_status_t async_recv(void* recv_ctx); TrxHandle* local_trx(wsrep_trx_id_t); TrxHandle* local_trx(wsrep_ws_handle_t*, bool); void unref_local_trx(TrxHandle* trx); void discard_local_trx(wsrep_trx_id_t trx_id); TrxHandle* local_conn_trx(wsrep_conn_id_t, bool); void discard_local_conn_trx(wsrep_conn_id_t conn_id); void discard_local_conn(wsrep_conn_id_t conn_id); void apply_trx(void* recv_ctx, TrxHandle* trx); wsrep_status_t replicate(TrxHandle* trx, wsrep_trx_meta_t*); void abort_trx(TrxHandle* trx) ; wsrep_status_t pre_commit(TrxHandle* trx, wsrep_trx_meta_t*); wsrep_status_t replay_trx(TrxHandle* trx, void* replay_ctx); wsrep_status_t post_commit(TrxHandle* trx); wsrep_status_t post_rollback(TrxHandle* trx); wsrep_status_t causal_read(wsrep_gtid_t*); wsrep_status_t to_isolation_begin(TrxHandle* trx, wsrep_trx_meta_t*); wsrep_status_t to_isolation_end(TrxHandle* trx); wsrep_status_t sst_sent(const wsrep_gtid_t& state_id, int rcode); wsrep_status_t sst_received(const wsrep_gtid_t& state_id, const void* state, size_t state_len, int rcode); void process_trx(void* recv_ctx, TrxHandle* trx); void process_commit_cut(wsrep_seqno_t seq, wsrep_seqno_t seqno_l); void process_conf_change(void* recv_ctx, const wsrep_view_info_t& view, int repl_proto, State next_state, wsrep_seqno_t seqno_l); void process_state_req(void* recv_ctx, const void* req, size_t req_size, wsrep_seqno_t seqno_l, wsrep_seqno_t donor_seq); void process_join(wsrep_seqno_t seqno, wsrep_seqno_t seqno_l); void process_sync(wsrep_seqno_t seqno_l); const struct wsrep_stats_var* stats_get() const; static void stats_free(struct wsrep_stats_var*); /*! @throws NotFound */ void set_param (const std::string& key, const std::string& value); /*! @throws NotFound */ void param_set (const std::string& key, const std::string& value); std::string param_get (const std::string& key) const; const gu::Config& params() const { return config_; } wsrep_seqno_t pause(); void resume(); void desync(); void resync(); private: ReplicatorSMM(const ReplicatorSMM&); void operator=(const ReplicatorSMM&); struct Param { static const std::string commit_order; static const std::string causal_read_timeout; static const std::string base_host; static const std::string base_port; }; typedef std::pair Default; struct Defaults { std::map map_; Defaults (); }; static const Defaults defaults; // both a list of parameters and a list of default values wsrep_seqno_t last_committed() { return co_mode_ != CommitOrder::BYPASS ? commit_monitor_.last_left() : apply_monitor_.last_left(); } void report_last_committed(wsrep_seqno_t purge_seqno) { if (gu_unlikely(purge_seqno != -1)) { service_thd_.report_last_committed(purge_seqno); } } wsrep_status_t cert(TrxHandle* trx); wsrep_status_t cert_for_aborted(TrxHandle* trx); void update_state_uuid (const wsrep_uuid_t& u); void update_incoming_list (const wsrep_view_info_t& v); /* aborts/exits the program in a clean way */ void abort() GU_NORETURN; class LocalOrder { public: LocalOrder(TrxHandle& trx) : seqno_(trx.local_seqno()), trx_(&trx) { } LocalOrder(wsrep_seqno_t seqno) : seqno_(seqno), trx_(0) { } void lock() { if (trx_ != 0) trx_->lock(); } void unlock() { if (trx_ != 0) trx_->unlock(); } wsrep_seqno_t seqno() const { return seqno_; } bool condition(wsrep_seqno_t last_entered, wsrep_seqno_t last_left) const { return (last_left + 1 == seqno_); } private: LocalOrder(const LocalOrder&); wsrep_seqno_t seqno_; TrxHandle* trx_; }; class ApplyOrder { public: ApplyOrder(TrxHandle& trx) : trx_(trx) { } void lock() { trx_.lock(); } void unlock() { trx_.unlock(); } wsrep_seqno_t seqno() const { return trx_.global_seqno(); } bool condition(wsrep_seqno_t last_entered, wsrep_seqno_t last_left) const { return (trx_.is_local() == true || last_left >= trx_.depends_seqno()); } private: ApplyOrder(const ApplyOrder&); TrxHandle& trx_; }; public: class CommitOrder { public: typedef enum { BYPASS = 0, OOOC = 1, LOCAL_OOOC = 2, NO_OOOC = 3 } Mode; static Mode from_string(const std::string& str) { int ret(gu::from_string(str)); switch (ret) { case BYPASS: case OOOC: case LOCAL_OOOC: case NO_OOOC: break; default: gu_throw_error(EINVAL) << "invalid value " << str << " for commit order mode"; } return static_cast(ret); } CommitOrder(TrxHandle& trx, Mode mode) : trx_ (trx ), mode_(mode) { } void lock() { trx_.lock(); } void unlock() { trx_.unlock(); } wsrep_seqno_t seqno() const { return trx_.global_seqno(); } bool condition(wsrep_seqno_t last_entered, wsrep_seqno_t last_left) const { switch (mode_) { case BYPASS: gu_throw_fatal << "commit order condition called in bypass mode"; case OOOC: return true; case LOCAL_OOOC: return trx_.is_local(); // in case of remote trx fall through case NO_OOOC: return (last_left + 1 == trx_.global_seqno()); } gu_throw_fatal << "invalid commit mode value " << mode_; } private: CommitOrder(const CommitOrder&); TrxHandle& trx_; const Mode mode_; }; class StateRequest { public: virtual const void* req () const = 0; virtual ssize_t len () const = 0; virtual const void* sst_req () const = 0; virtual ssize_t sst_len () const = 0; virtual const void* ist_req () const = 0; virtual ssize_t ist_len () const = 0; virtual ~StateRequest() {} }; private: // state machine class Transition { public: Transition(State const from, State const to) : from_(from), to_(to) { } State from() const { return from_; } State to() const { return to_; } bool operator==(Transition const& other) const { return (from_ == other.from_ && to_ == other.to_); } class Hash { public: size_t operator()(Transition const& tr) const { return (gu::HashValue(static_cast(tr.from_)) ^ gu::HashValue(static_cast(tr.to_))); } }; private: State from_; State to_; }; void build_stats_vars (std::vector& stats); void establish_protocol_versions (int version); bool state_transfer_required(const wsrep_view_info_t& view_info); void prepare_for_IST (void*& req, ssize_t& req_len, const wsrep_uuid_t& group_uuid, wsrep_seqno_t group_seqno); void recv_IST(void* recv_ctx); StateRequest* prepare_state_request (const void* sst_req, ssize_t sst_req_len, const wsrep_uuid_t& group_uuid, wsrep_seqno_t group_seqno); void send_state_request (const wsrep_uuid_t& group_uuid, wsrep_seqno_t group_seqno, const StateRequest* req); void request_state_transfer (void* recv_ctx, const wsrep_uuid_t& group_uuid, wsrep_seqno_t group_seqno, const void* sst_req, ssize_t sst_req_len); class Logger { public: Logger (gu_log_cb_t cb) { gu_conf_set_log_callback(cb); } }; Logger logger_; gu::Config config_; struct SetDefaults { SetDefaults(gu::Config&, const Defaults&, const char* base_addr); } set_defaults_; // sets missing parameters to default values static const int MAX_PROTO_VER = 4; /* * |------------------------------------------------------ * | protocol_version_ | trx_proto_ver_ | str_proto_ver_ | * |------------------------------------------------------ * | 1 | 1 | 0 | * | 2 | 1 | 1 | * | 3 | 2 | 1 | * | 4 | 2 | 1 | * ------------------------------------------------------- */ int trx_proto_ver_;// transaction protocol int str_proto_ver_;// state transfer request protocol int protocol_version_; // general repl layer proto FSM state_; SstState sst_state_; // configurable params const CommitOrder::Mode co_mode_; // commit order mode // persistent data location std::string data_dir_; std::string state_file_; SavedState st_; // identifiers wsrep_uuid_t uuid_; wsrep_uuid_t const state_uuid_; const char state_uuid_str_[37]; wsrep_seqno_t cc_seqno_; // seqno of last CC // application callbacks void* app_ctx_; wsrep_view_cb_t view_cb_; wsrep_apply_cb_t apply_cb_; wsrep_commit_cb_t commit_cb_; wsrep_sst_donate_cb_t sst_donate_cb_; wsrep_synced_cb_t synced_cb_; // SST std::string sst_donor_; wsrep_uuid_t sst_uuid_; wsrep_seqno_t sst_seqno_; gu::Mutex sst_mutex_; gu::Cond sst_cond_; int sst_retry_sec_; bool ist_sst_; // services gcache::GCache gcache_; GCS_IMPL gcs_; ServiceThd service_thd_; // action sources ActionSource* as_; GcsActionSource gcs_as_; ist::Receiver ist_receiver_; ist::AsyncSenderMap ist_senders_; // trx processing Wsdb wsdb_; Certification cert_; // concurrency control Monitor local_monitor_; Monitor apply_monitor_; Monitor commit_monitor_; gu::datetime::Period causal_read_timeout_; // counters gu::Atomic receivers_; gu::Atomic replicated_; gu::Atomic replicated_bytes_; gu::Atomic local_commits_; gu::Atomic local_rollbacks_; gu::Atomic local_cert_failures_; gu::Atomic local_replays_; gu::Atomic causal_reads_; // non-atomic stats std::string incoming_list_; mutable gu::Mutex incoming_mutex_; mutable std::vector wsrep_stats_; }; std::ostream& operator<<(std::ostream& os, ReplicatorSMM::State state); } #endif /* GALERA_REPLICATOR_SMM_HPP */ percona-xtradb-cluster-galera/galera/src/replicator_smm_params.cpp0000644000000000000000000000770012247075736025767 0ustar rootroot00000000000000/* Copyright (C) 2012 Codership Oy */ #include "replicator_smm.hpp" #include "gcs.hpp" #include "galera_common.hpp" #include static const std::string common_prefix = "replicator."; const std::string galera::ReplicatorSMM::Param::commit_order = common_prefix + "commit_order"; const std::string galera::ReplicatorSMM::Param::causal_read_timeout = common_prefix + "causal_read_timeout"; const std::string galera::ReplicatorSMM::Param::base_host = "base_host"; const std::string galera::ReplicatorSMM::Param::base_port = "base_port"; galera::ReplicatorSMM::Defaults::Defaults() : map_() { map_.insert(Default(Param::commit_order, "3")); map_.insert(Default(Param::causal_read_timeout, "PT30S")); map_.insert(Default(CERTIFICATION_PARAM_LOG_CONFLICTS_STR, CERTIFICATION_DEFAULTS_LOG_CONFLICTS_STR)); map_.insert(Default(Param::base_port, BASE_PORT_DEFAULT)); } const galera::ReplicatorSMM::Defaults galera::ReplicatorSMM::defaults; galera::ReplicatorSMM::SetDefaults::SetDefaults(gu::Config& conf, const Defaults& def, const char* const node_address) { std::map::const_iterator i; for (i = def.map_.begin(); i != def.map_.end(); ++i) { if (!conf.has(i->first)) conf.set(i->first, i->second); } if (node_address && strlen(node_address) > 0) { gu::URI na(node_address, false); try { std::string const host = na.get_host(); if (host == "0.0.0.0" || host == "0:0:0:0:0:0:0:0" || host == "::") { gu_throw_error(EINVAL) << "Bad value for 'node_address': '" << host << '\''; } conf.set(BASE_HOST_KEY, host); } catch (gu::NotSet&) {} try { conf.set(BASE_PORT_KEY, na.get_port()); } catch (gu::NotSet&) {} } } void galera::ReplicatorSMM::set_param (const std::string& key, const std::string& value) { if (key == Param::commit_order) { log_error << "setting '" << key << "' during runtime not allowed"; gu_throw_error(EPERM) << "setting '" << key << "' during runtime not allowed"; } else if (key == Param::causal_read_timeout) { causal_read_timeout_ = gu::datetime::Period(value); } else if (key == Certification::Param::log_conflicts) { cert_.set_log_conflicts(value); } else if (key == Param::base_host || key == Param::base_port) { // nothing to do here, these params take effect only at // provider (re)start } else { log_warn << "parameter '" << "' not found"; assert(0); throw gu::NotFound(); } } void galera::ReplicatorSMM::param_set (const std::string& key, const std::string& value) { try { if (config_.get(key) == value) return; } catch (gu::NotFound&) {} bool found(false); // Note: base_host is treated separately here as it cannot have // default value known at compile time. if (defaults.map_.find(key) != defaults.map_.end() || key == Param::base_host) // is my key? { found = true; set_param (key, value); config_.set (key, value); } if (0 != key.find(common_prefix)) // this key might be for another module { try { gcs_.param_set (key, value); found = true; } catch (gu::NotFound&) {} try { gcache_.param_set (key, value); found = true; } catch (gu::NotFound&) {} } if (!found) throw gu::NotFound(); } std::string galera::ReplicatorSMM::param_get (const std::string& key) const { return config_.get(key); } percona-xtradb-cluster-galera/galera/src/replicator_smm_stats.cpp0000644000000000000000000002126212247075736025641 0ustar rootroot00000000000000/* Copyright (C) 2010 Codership Oy */ #include "replicator_smm.hpp" #include "uuid.hpp" // @todo: should be protected static member of the parent class static const size_t GALERA_STAGE_MAX(11); // @todo: should be protected static member of the parent class static const char* state_str[GALERA_STAGE_MAX] = { "Initialized", "Joining", "Joining: preparing for State Transfer", "Joining: requested State Transfer", "Joining: receiving State Transfer", "Joined", "Synced", "Donor/Desynced", "Joining: State Transfer request failed", "Joining: State Transfer failed", "Destroyed" }; // @todo: should be protected static member of the parent class static wsrep_member_status_t state2stats(galera::ReplicatorSMM::State state) { switch (state) { case galera::ReplicatorSMM::S_DESTROYED : case galera::ReplicatorSMM::S_CLOSED : case galera::ReplicatorSMM::S_CLOSING : case galera::ReplicatorSMM::S_CONNECTED : return WSREP_MEMBER_UNDEFINED; case galera::ReplicatorSMM::S_JOINING : return WSREP_MEMBER_JOINER; case galera::ReplicatorSMM::S_JOINED : return WSREP_MEMBER_JOINED; case galera::ReplicatorSMM::S_SYNCED : return WSREP_MEMBER_SYNCED; case galera::ReplicatorSMM::S_DONOR : return WSREP_MEMBER_DONOR; } gu_throw_fatal << "invalid state " << state; } // @todo: should be protected static member of the parent class static const char* state2stats_str(galera::ReplicatorSMM::State state, galera::ReplicatorSMM::SstState sst_state) { using galera::ReplicatorSMM; switch (state) { case galera::ReplicatorSMM::S_DESTROYED : return state_str[10]; case galera::ReplicatorSMM::S_CLOSED : case galera::ReplicatorSMM::S_CLOSING: case galera::ReplicatorSMM::S_CONNECTED: { if (sst_state == ReplicatorSMM::SST_REQ_FAILED) return state_str[8]; else if (sst_state == ReplicatorSMM::SST_FAILED) return state_str[9]; else return state_str[0]; } case galera::ReplicatorSMM::S_JOINING: { if (sst_state == ReplicatorSMM::SST_WAIT) return state_str[4]; else return state_str[1]; } case galera::ReplicatorSMM::S_JOINED : return state_str[5]; case galera::ReplicatorSMM::S_SYNCED : return state_str[6]; case galera::ReplicatorSMM::S_DONOR : return state_str[7]; } gu_throw_fatal << "invalid state " << state; } typedef enum status_vars { STATS_STATE_UUID = 0, STATS_PROTOCOL_VERSION, STATS_LAST_APPLIED, STATS_REPLICATED, STATS_REPLICATED_BYTES, STATS_RECEIVED, STATS_RECEIVED_BYTES, STATS_LOCAL_COMMITS, STATS_LOCAL_CERT_FAILURES, STATS_LOCAL_REPLAYS, STATS_LOCAL_SEND_QUEUE, STATS_LOCAL_SEND_QUEUE_AVG, STATS_LOCAL_RECV_QUEUE, STATS_LOCAL_RECV_QUEUE_AVG, STATS_FC_PAUSED, STATS_FC_SENT, STATS_FC_RECEIVED, STATS_CERT_DEPS_DISTANCE, STATS_APPLY_OOOE, STATS_APPLY_OOOL, STATS_APPLY_WINDOW, STATS_COMMIT_OOOE, STATS_COMMIT_OOOL, STATS_COMMIT_WINDOW, STATS_LOCAL_STATE, STATS_LOCAL_STATE_COMMENT, STATS_CERT_INDEX_SIZE, STATS_CAUSAL_READS, STATS_INCOMING_LIST, STATS_MAX } StatusVars; static const struct wsrep_stats_var wsrep_stats[STATS_MAX + 1] = { { "local_state_uuid", WSREP_VAR_STRING, { 0 } }, { "protocol_version", WSREP_VAR_INT64, { 0 } }, { "last_committed", WSREP_VAR_INT64, { -1 } }, { "replicated", WSREP_VAR_INT64, { 0 } }, { "replicated_bytes", WSREP_VAR_INT64, { 0 } }, { "received", WSREP_VAR_INT64, { 0 } }, { "received_bytes", WSREP_VAR_INT64, { 0 } }, { "local_commits", WSREP_VAR_INT64, { 0 } }, { "local_cert_failures", WSREP_VAR_INT64, { 0 } }, { "local_replays", WSREP_VAR_INT64, { 0 } }, { "local_send_queue", WSREP_VAR_INT64, { 0 } }, { "local_send_queue_avg", WSREP_VAR_DOUBLE, { 0 } }, { "local_recv_queue", WSREP_VAR_INT64, { 0 } }, { "local_recv_queue_avg", WSREP_VAR_DOUBLE, { 0 } }, { "flow_control_paused", WSREP_VAR_DOUBLE, { 0 } }, { "flow_control_sent", WSREP_VAR_INT64, { 0 } }, { "flow_control_recv", WSREP_VAR_INT64, { 0 } }, { "cert_deps_distance", WSREP_VAR_DOUBLE, { 0 } }, { "apply_oooe", WSREP_VAR_DOUBLE, { 0 } }, { "apply_oool", WSREP_VAR_DOUBLE, { 0 } }, { "apply_window", WSREP_VAR_DOUBLE, { 0 } }, { "commit_oooe", WSREP_VAR_DOUBLE, { 0 } }, { "commit_oool", WSREP_VAR_DOUBLE, { 0 } }, { "commit_window", WSREP_VAR_DOUBLE, { 0 } }, { "local_state", WSREP_VAR_INT64, { 0 } }, { "local_state_comment", WSREP_VAR_STRING, { 0 } }, { "cert_index_size", WSREP_VAR_INT64, { 0 } }, { "causal_reads", WSREP_VAR_INT64, { 0 } }, { "incoming_addresses", WSREP_VAR_STRING, { 0 } }, { 0, WSREP_VAR_STRING, { 0 } } }; void galera::ReplicatorSMM::build_stats_vars ( std::vector& stats) { const struct wsrep_stats_var* ptr(wsrep_stats); do { stats.push_back(*ptr); } while (ptr++->name != 0); stats[STATS_STATE_UUID].value._string = state_uuid_str_; } const struct wsrep_stats_var* galera::ReplicatorSMM::stats_get() const { if (S_DESTROYED == state_()) return 0; std::vector& sv(wsrep_stats_); sv[STATS_PROTOCOL_VERSION ].value._int64 = protocol_version_; sv[STATS_LAST_APPLIED ].value._int64 = apply_monitor_.last_left(); sv[STATS_REPLICATED ].value._int64 = replicated_(); sv[STATS_REPLICATED_BYTES ].value._int64 = replicated_bytes_(); sv[STATS_RECEIVED ].value._int64 = gcs_as_.received(); sv[STATS_RECEIVED_BYTES ].value._int64 = gcs_as_.received_bytes(); sv[STATS_LOCAL_COMMITS ].value._int64 = local_commits_(); sv[STATS_LOCAL_CERT_FAILURES].value._int64 = local_cert_failures_(); sv[STATS_LOCAL_REPLAYS ].value._int64 = local_replays_(); struct gcs_stats stats; gcs_.get_stats (&stats); sv[STATS_LOCAL_SEND_QUEUE ].value._int64 = stats.send_q_len; sv[STATS_LOCAL_SEND_QUEUE_AVG].value._double = stats.send_q_len_avg; sv[STATS_LOCAL_RECV_QUEUE ].value._int64 = stats.recv_q_len; sv[STATS_LOCAL_RECV_QUEUE_AVG].value._double = stats.recv_q_len_avg; sv[STATS_FC_PAUSED ].value._double = stats.fc_paused; sv[STATS_FC_SENT ].value._int64 = stats.fc_sent; sv[STATS_FC_RECEIVED ].value._int64 = stats.fc_received; sv[STATS_CERT_DEPS_DISTANCE ].value._double = cert_.get_avg_deps_dist(); double oooe; double oool; double win; const_cast&>(apply_monitor_). get_stats(&oooe, &oool, &win); sv[STATS_APPLY_OOOE ].value._double = oooe; sv[STATS_APPLY_OOOL ].value._double = oool; sv[STATS_APPLY_WINDOW ].value._double = win; const_cast&>(commit_monitor_). get_stats(&oooe, &oool, &win); sv[STATS_COMMIT_OOOE ].value._double = oooe; sv[STATS_COMMIT_OOOL ].value._double = oool; sv[STATS_COMMIT_WINDOW ].value._double = win; sv[STATS_LOCAL_STATE ].value._int64 = state2stats(state_()); sv[STATS_LOCAL_STATE_COMMENT ].value._string = state2stats_str(state_(), sst_state_); sv[STATS_CERT_INDEX_SIZE].value._int64 = cert_.index_size(); sv[STATS_CAUSAL_READS].value._int64 = causal_reads_(); /* Create a buffer to be passed to the caller. */ gu::Lock lock_inc(incoming_mutex_); size_t const inc_size(incoming_list_.size() + 1); size_t const vec_size(sv.size()*sizeof(struct wsrep_stats_var)); struct wsrep_stats_var* const buf( reinterpret_cast(gu_malloc(inc_size + vec_size))); if (buf) { char* const inc_buf(reinterpret_cast(buf + sv.size())); wsrep_stats_[STATS_INCOMING_LIST].value._string = inc_buf; memcpy(buf, &sv[0], vec_size); memcpy(inc_buf, incoming_list_.c_str(), inc_size); assert(inc_buf[inc_size - 1] == '\0'); } else { log_warn << "Failed to allocate stats vars buffer to " << (inc_size + vec_size) << " bytes. System is running out of memory."; } return buf; } void galera::ReplicatorSMM::stats_free(struct wsrep_stats_var* arg) { if (!arg) return; log_debug << "########### Freeing stats object ##########"; gu_free(arg); } percona-xtradb-cluster-galera/galera/src/replicator_str.cpp0000644000000000000000000005670112247075736024445 0ustar rootroot00000000000000// // Copyright (C) 2010-2012 Codership Oy // #include "replicator_smm.hpp" #include "uuid.hpp" #include namespace galera { bool ReplicatorSMM::state_transfer_required(const wsrep_view_info_t& view_info) { if (view_info.state_gap) { assert(view_info.view >= 0); if (state_uuid_ == view_info.state_id.uuid) // common history { wsrep_seqno_t const group_seqno(view_info.state_id.seqno); wsrep_seqno_t const local_seqno(apply_monitor_.last_left()); if (state_() >= S_JOINING) /* See #442 - S_JOINING should be a valid state here */ { return (local_seqno < group_seqno); } else { if (local_seqno > group_seqno) { close(); gu_throw_fatal << "Local state seqno (" << local_seqno << ") is greater than group seqno (" <(req_ + offset))); } void* req (ssize_t offset) const { if (len(offset) > 0) return req_ + offset + sizeof(uint32_t); else return 0; } ssize_t const len_; char* const req_; bool const own_; }; std::string const StateRequest_v1::MAGIC("STRv1"); #ifndef INT32_MAX #define INT32_MAX 0x7fffffff #endif StateRequest_v1::StateRequest_v1 ( const void* const sst_req, ssize_t const sst_req_len, const void* const ist_req, ssize_t const ist_req_len) : len_(MAGIC.length() + 1 + sizeof(uint32_t) + sst_req_len + sizeof(uint32_t) + ist_req_len), req_(reinterpret_cast(malloc(len_))), own_(true) { if (!req_) gu_throw_error (ENOMEM) << "Could not allocate state request v1"; if (sst_req_len > INT32_MAX || sst_req_len < 0) gu_throw_error (EMSGSIZE) << "SST request length (" << sst_req_len << ") unrepresentable"; if (ist_req_len > INT32_MAX || ist_req_len < 0) gu_throw_error (EMSGSIZE) << "IST request length (" << sst_req_len << ") unrepresentable"; char* ptr(req_); strcpy (ptr, MAGIC.c_str()); ptr += MAGIC.length() + 1; uint32_t* tmp(reinterpret_cast(ptr)); *tmp = htogl(sst_req_len); ptr += sizeof(uint32_t); memcpy (ptr, sst_req, sst_req_len); ptr += sst_req_len; tmp = reinterpret_cast(ptr); *tmp = htogl(ist_req_len); ptr += sizeof(uint32_t); memcpy (ptr, ist_req, ist_req_len); assert ((ptr - req_) == (len_ - ist_req_len)); } // takes ownership over str buffer StateRequest_v1::StateRequest_v1 (const void* str, ssize_t str_len) : len_(str_len), req_(reinterpret_cast(const_cast(str))), own_(false) { if (sst_offset() + 2*sizeof(uint32_t) > size_t(len_)) { assert(0); gu_throw_error (EINVAL) << "State transfer request is too short: " << len_ << ", must be at least: " << (sst_offset() + 2*sizeof(uint32_t)); } if (strncmp (req_, MAGIC.c_str(), MAGIC.length())) { assert(0); gu_throw_error (EINVAL) << "Wrong magic signature in state request v1."; } if (sst_offset() + sst_len() + 2*sizeof(uint32_t) > size_t(len_)) { gu_throw_error (EINVAL) << "Malformed state request v1: sst length: " << sst_len() << ", total length: " << len_; } if (ist_offset() + ist_len() + sizeof(uint32_t) != size_t(len_)) { gu_throw_error (EINVAL) << "Malformed state request v1: parsed field " "length " << sst_len() << " is not equal to total request length " << len_; } } static ReplicatorSMM::StateRequest* read_state_request (const void* const req, size_t const req_len) { const char* const str(reinterpret_cast(req)); if (req_len > StateRequest_v1::MAGIC.length() && !strncmp(str, StateRequest_v1::MAGIC.c_str(), StateRequest_v1::MAGIC.length())) { return (new StateRequest_v1(req, req_len)); } else { return (new StateRequest_v0(req, req_len)); } } class IST_request { public: IST_request() : peer_(), uuid_(), last_applied_(), group_seqno_() { } IST_request(const std::string& peer, const wsrep_uuid_t& uuid, wsrep_seqno_t last_applied, wsrep_seqno_t group_seqno) : peer_(peer), uuid_(uuid), last_applied_(last_applied), group_seqno_(group_seqno) { } const std::string& peer() const { return peer_ ; } const wsrep_uuid_t& uuid() const { return uuid_ ; } wsrep_seqno_t last_applied() const { return last_applied_; } wsrep_seqno_t group_seqno() const { return group_seqno_; } private: friend std::ostream& operator<<(std::ostream&, const IST_request&); friend std::istream& operator>>(std::istream&, IST_request&); std::string peer_; wsrep_uuid_t uuid_; wsrep_seqno_t last_applied_; wsrep_seqno_t group_seqno_; }; std::ostream& operator<<(std::ostream& os, const IST_request& istr) { return (os << istr.uuid_ << ":" << istr.last_applied_ << "-" << istr.group_seqno_ << "|" << istr.peer_); } std::istream& operator>>(std::istream& is, IST_request& istr) { char c; return (is >> istr.uuid_ >> c >> istr.last_applied_ >> c >> istr.group_seqno_ >> c >> istr.peer_); } static bool sst_is_trivial (const void* const req, size_t const len) { /* Check that the first string in request == ReplicatorSMM::TRIVIAL_SST */ size_t const trivial_len = strlen(ReplicatorSMM::TRIVIAL_SST) + 1; return (len >= trivial_len && !memcmp (req, ReplicatorSMM::TRIVIAL_SST, trivial_len)); } void ReplicatorSMM::process_state_req(void* recv_ctx, const void* req, size_t req_size, wsrep_seqno_t const seqno_l, wsrep_seqno_t const donor_seq) { assert(recv_ctx != 0); assert(seqno_l > -1); assert(req != 0); LocalOrder lo(seqno_l); gu_trace(local_monitor_.enter(lo)); apply_monitor_.drain(donor_seq); if (co_mode_ != CommitOrder::BYPASS) commit_monitor_.drain(donor_seq); state_.shift_to(S_DONOR); StateRequest* const streq (read_state_request (req, req_size)); // somehow the following does not work, string is initialized beyond // the first \0: //std::string const req_str(reinterpret_cast(streq->sst_req()), // streq->sst_len()); // have to resort to C ways. char* const tmp(strndup(reinterpret_cast(streq->sst_req()), streq->sst_len())); std::string const req_str(tmp); free (tmp); bool const skip_state_transfer (sst_is_trivial(streq->sst_req(), streq->sst_len()) /* compatibility with older garbd, to be removed in * the next release (2.1)*/ || req_str == std::string(WSREP_STATE_TRANSFER_NONE) ); wsrep_seqno_t rcode (0); if (!skip_state_transfer) { if (streq->ist_len()) { IST_request istr; std::string ist_str(reinterpret_cast(streq->ist_req()), streq->ist_len()); std::istringstream is(ist_str); is >> istr; if (istr.uuid() == state_uuid_) { log_info << "IST request: " << istr; try { gcache_.seqno_lock(istr.last_applied() + 1); } catch(gu::NotFound& nf) { log_info << "IST first seqno " << istr.last_applied() + 1 << " not found from cache, falling back to SST"; // @todo: close IST channel explicitly goto full_sst; } if (streq->sst_len()) // if joiner is waiting for SST, notify it { ist_sst_ = true; // gcs_.join() shall be called by IST wsrep_gtid_t state_id = { istr.uuid(),istr.last_applied()}; rcode = sst_donate_cb_(app_ctx_, recv_ctx, streq->sst_req(), streq->sst_len(), &state_id, 0, 0, true); // this must be reset only in sst_sent() call ist_sst_ = false; } if (rcode >= 0) { try { // Note: End of IST range must be cc_seqno_ instead // of istr.group_seqno() in case there are CCs between // sending and delivering STR. If there are no // intermediate CCs, cc_seqno_ == istr.group_seqno(). ist_senders_.run(config_, istr.peer(), istr.last_applied() + 1, cc_seqno_, protocol_version_); } catch (gu::Exception& e) { log_error << "IST failed: " << e.what(); rcode = -e.get_errno(); } } else { log_error << "Failed to bypass SST: " << -rcode << " (" << strerror (-rcode) << ')'; } goto out; } } full_sst: if (streq->sst_len()) { assert(0 == rcode); wsrep_gtid_t const state_id = { state_uuid_, donor_seq }; rcode = sst_donate_cb_(app_ctx_, recv_ctx, streq->sst_req(), streq->sst_len(), &state_id, 0, 0, false); } else { log_warn << "SST request is null, SST canceled."; rcode = -ECANCELED; } } out: delete streq; local_monitor_.leave(lo); if (skip_state_transfer || rcode < 0) { gcs_.join(rcode < 0 ? rcode : donor_seq); } } void ReplicatorSMM::prepare_for_IST (void*& ptr, ssize_t& len, const wsrep_uuid_t& group_uuid, wsrep_seqno_t const group_seqno) { if (state_uuid_ != group_uuid) { gu_throw_error (EPERM) << "Local state UUID (" << state_uuid_ << ") does not match group state UUID (" << group_uuid << ')'; } wsrep_seqno_t const local_seqno(apply_monitor_.last_left()); if (local_seqno < 0) { gu_throw_error (EPERM) << "Local state seqno is undefined"; } assert(local_seqno < group_seqno); std::ostringstream os; std::string recv_addr = ist_receiver_.prepare( local_seqno + 1, group_seqno, protocol_version_); os << IST_request(recv_addr, state_uuid_, apply_monitor_.last_left(), group_seqno); char* str = strdup (os.str().c_str()); // cppcheck-suppress nullPointer if (!str) gu_throw_error (ENOMEM) << "Failed to allocate IST buffer."; len = strlen(str) + 1; ptr = str; } ReplicatorSMM::StateRequest* ReplicatorSMM::prepare_state_request (const void* const sst_req, ssize_t const sst_req_len, const wsrep_uuid_t& group_uuid, wsrep_seqno_t const group_seqno) { try { switch (str_proto_ver_) { case 0: return new StateRequest_v0 (sst_req, sst_req_len); case 1: { void* ist_req(0); ssize_t ist_req_len(0); try { gu_trace(prepare_for_IST (ist_req, ist_req_len, group_uuid, group_seqno)); } catch (gu::Exception& e) { log_warn << "Failed to prepare for incremental state transfer: " << e.what() << ". IST will be unavailable."; } StateRequest* ret = new StateRequest_v1 (sst_req, sst_req_len, ist_req, ist_req_len); free (ist_req); return ret; } default: gu_throw_fatal << "Unsupported STR protocol: " << str_proto_ver_; } } catch (std::exception& e) { log_fatal << "State request preparation failed, aborting: " << e.what(); } catch (...) { log_fatal << "State request preparation failed, aborting: unknown exception"; } abort(); } static bool retry_str(int ret) { return (ret == -EAGAIN || ret == -ENOTCONN); } void ReplicatorSMM::send_state_request (const wsrep_uuid_t& group_uuid, wsrep_seqno_t const group_seqno, const StateRequest* const req) { long ret; long tries = 0; do { tries++; gcs_seqno_t seqno_l; ret = gcs_.request_state_transfer(req->req(), req->len(), sst_donor_, &seqno_l); if (ret < 0) { if (!retry_str(ret)) { log_error << "Requesting state transfer failed: " << ret << "(" << strerror(-ret) << ")"; } else if (1 == tries) { log_info << "Requesting state transfer failed: " << ret << "(" << strerror(-ret) << "). " << "Will keep retrying every " << sst_retry_sec_ << " second(s)"; } } if (seqno_l != GCS_SEQNO_ILL) { /* Check that we're not running out of space in monitor. */ if (local_monitor_.would_block(seqno_l)) { long const seconds = sst_retry_sec_ * tries; log_error << "We ran out of resources, seemingly because " << "we've been unsuccessfully requesting state " << "transfer for over " << seconds << " seconds. " << "Please check that there is " << "at least one fully synced member in the group. " << "Application must be restarted."; ret = -EDEADLK; } else { // we are already holding local monitor LocalOrder lo(seqno_l); local_monitor_.self_cancel(lo); } } } while (retry_str(ret) && (usleep(sst_retry_sec_ * 1000000), true)); if (ret >= 0) { if (1 == tries) { log_info << "Requesting state transfer: success, donor: " << ret; } else { log_info << "Requesting state transfer: success after " << tries << " tries, donor: " << ret; } } else { sst_state_ = SST_REQ_FAILED; st_.set(state_uuid_, apply_monitor_.last_left()); st_.mark_safe(); if (state_() > S_CLOSING) { log_fatal << "State transfer request failed unrecoverably: " << -ret << " (" << strerror(-ret) << "). Most likely " << "it is due to inability to communicate with the " << "cluster primary component. Restart required."; abort(); } else { // connection is being closed, send failure is expected } } } void ReplicatorSMM::request_state_transfer (void* recv_ctx, const wsrep_uuid_t& group_uuid, wsrep_seqno_t const group_seqno, const void* const sst_req, ssize_t const sst_req_len) { assert(sst_req_len >= 0); StateRequest* const req(prepare_state_request(sst_req, sst_req_len, group_uuid, group_seqno)); gu::Lock lock(sst_mutex_); st_.mark_unsafe(); send_state_request (group_uuid, group_seqno, req); state_.shift_to(S_JOINING); sst_state_ = SST_WAIT; /* while waiting for state transfer to complete is a good point * to reset gcache, since it may involve some IO too */ gcache_.seqno_reset(); if (sst_req_len != 0) { if (sst_is_trivial(sst_req, sst_req_len)) { sst_uuid_ = group_uuid; sst_seqno_ = group_seqno; } else { lock.wait(sst_cond_); } if (sst_uuid_ != group_uuid) { log_fatal << "Application received wrong state: " << "\n\tReceived: " << sst_uuid_ << "\n\tRequired: " << group_uuid; sst_state_ = SST_FAILED; log_fatal << "Application state transfer failed. This is " << "unrecoverable condition, restart required."; st_.set(sst_uuid_, sst_seqno_); st_.mark_safe(); abort(); } else { update_state_uuid (sst_uuid_); apply_monitor_.set_initial_position(-1); apply_monitor_.set_initial_position(sst_seqno_); if (co_mode_ != CommitOrder::BYPASS) { commit_monitor_.set_initial_position(-1); commit_monitor_.set_initial_position(sst_seqno_); } log_debug << "Installed new state: " << state_uuid_ << ":" << sst_seqno_; } } else { assert (state_uuid_ == group_uuid); } st_.mark_safe(); if (req->ist_len() > 0) { // IST is prepared only with str proto ver 1 and above if (apply_monitor_.last_left() < group_seqno) { log_info << "Receiving IST: " << (group_seqno - apply_monitor_.last_left()) << " writesets, seqnos " << apply_monitor_.last_left() << "-" << group_seqno; ist_receiver_.ready(); recv_IST(recv_ctx); sst_seqno_ = ist_receiver_.finished(); // Note: apply_monitor_ must be drained to avoid race between // IST appliers and GCS appliers, GCS action source may // provide actions that have already been applied. apply_monitor_.drain(sst_seqno_); log_info << "IST received: " << state_uuid_ << ":" << sst_seqno_; } else { (void)ist_receiver_.finished(); } } delete req; } void ReplicatorSMM::recv_IST(void* recv_ctx) { try { while (true) { TrxHandle* trx(0); int err; if ((err = ist_receiver_.recv(&trx)) == 0) { assert(trx != 0); TrxHandleLock lock(*trx); if (trx->depends_seqno() == -1) { ApplyOrder ao(*trx); apply_monitor_.self_cancel(ao); if (co_mode_ != CommitOrder::BYPASS) { CommitOrder co(*trx, co_mode_); commit_monitor_.self_cancel(co); } } else { // replicating and certifying stages have been // processed on donor, just adjust states here trx->set_state(TrxHandle::S_REPLICATING); trx->set_state(TrxHandle::S_CERTIFYING); apply_trx(recv_ctx, trx); } } else { return; } trx->unref(); } } catch (gu::Exception& e) { log_fatal << "receiving IST failed, node restart required: " << e.what(); gcs_.close(); gu_abort(); } } } /* namespace galera */ percona-xtradb-cluster-galera/galera/src/saved_state.cpp0000644000000000000000000001357112247075736023711 0ustar rootroot00000000000000// // Copyright (C) 2012 Codership Oy // #include "saved_state.hpp" #include "uuid.hpp" #include #define __STDC_FORMAT_MACROS #include namespace galera { #define VERSION "2.1" #define MAX_SIZE 256 SavedState::SavedState (const std::string& file) : fs_ (0), uuid_ (WSREP_UUID_UNDEFINED), seqno_ (WSREP_SEQNO_UNDEFINED), unsafe_ (0), corrupt_ (false), mtx_ (), written_uuid_ (uuid_), current_len_ (0), total_marks_ (0), total_locks_ (0), total_writes_ (0) { std::ifstream ifs(file.c_str()); std::ofstream ofs; if (ifs.fail()) { log_warn << "Could not open saved state file for reading: " << file; } fs_ = fopen(file.c_str(), "a"); if (!fs_) { log_warn << "Could not open saved state file for writing: " << file; /* We are not reading anything from file we can't write to, since it may be terribly outdated. */ return; } std::string version("0.8"); std::string line; while (getline(ifs, line), ifs.good()) { std::istringstream istr(line); std::string param; istr >> param; if (param[0] == '#') { log_debug << "read comment: " << line; } else if (param == "version:") { istr >> version; // nothing to do with this yet log_debug << "read version: " << version; } else if (param == "uuid:") { try { istr >> uuid_; log_debug << "read saved state uuid: " << uuid_; } catch (gu::Exception& e) { log_error << e.what(); uuid_ = WSREP_UUID_UNDEFINED; } } else if (param == "seqno:") { istr >> seqno_; log_debug << "read saved state seqno: " << seqno_; } else if (param == "cert_index:") { // @todo log_debug << "cert index restore not implemented yet"; } } log_info << "Found saved state: " << uuid_ << ':' << seqno_; #if 0 // we'll probably have it legal if (seqno_ < 0 && uuid_ != WSREP_UUID_UNDEFINED) { log_warn << "Negative seqno with valid UUID: " << uuid_ << ':' << seqno_ << ". Discarding UUID."; uuid_ = WSREP_UUID_UNDEFINED; } #endif written_uuid_ = uuid_; current_len_ = ftell (fs_); log_debug << "Initialized current_len_ to " << current_len_; if (current_len_ <= MAX_SIZE) { fs_ = freopen (file.c_str(), "r+", fs_); } else // normalize file contents { fs_ = freopen (file.c_str(), "w+", fs_); // truncate current_len_ = 0; set (uuid_, seqno_); } } SavedState::~SavedState () { if (fs_) fclose(fs_); } void SavedState::get (wsrep_uuid_t& u, wsrep_seqno_t& s) { gu::Lock lock(mtx_); u = uuid_; s = seqno_; } void SavedState::set (const wsrep_uuid_t& u, wsrep_seqno_t s) { gu::Lock lock(mtx_); ++total_locks_; if (corrupt_) return; uuid_ = u; seqno_ = s; if (0 == unsafe_()) write_and_flush (u, s); else log_debug << "Not writing state: unsafe counter is " << unsafe_(); } /* the goal of unsafe_, written_uuid_, current_len_ below is * 1. avoid unnecessary mutex locks * 2. if locked - avoid unnecessary file writes * 3. if writing - avoid metadata operations, write over existing space */ void SavedState::mark_unsafe() { ++total_marks_; if (1 == unsafe_.add_and_fetch (1)) { gu::Lock lock(mtx_); ++total_locks_; assert (unsafe_() > 0); if (written_uuid_ != WSREP_UUID_UNDEFINED) { write_and_flush (WSREP_UUID_UNDEFINED, WSREP_SEQNO_UNDEFINED); } } } void SavedState::mark_safe() { ++total_marks_; long count = unsafe_.sub_and_fetch (1); assert (count >= 0); if (0 == count) { gu::Lock lock(mtx_); ++total_locks_; if (0 == unsafe_() && (written_uuid_ != uuid_ || seqno_ >= 0)) { assert(false == corrupt_); /* this will write down proper seqno if set() was called too early * (in unsafe state) */ write_and_flush (uuid_, seqno_); } } } void SavedState::mark_corrupt() { /* Half LONG_MAX keeps us equally far from overflow and underflow by mark_unsafe()/mark_safe() calls */ unsafe_ = (std::numeric_limits::max() >> 1); gu::Lock lock(mtx_); ++total_locks_; if (corrupt_) return; uuid_ = WSREP_UUID_UNDEFINED; seqno_ = WSREP_SEQNO_UNDEFINED; corrupt_ = true; write_and_flush (WSREP_UUID_UNDEFINED, WSREP_SEQNO_UNDEFINED); } void SavedState::write_and_flush(const wsrep_uuid_t& u, const wsrep_seqno_t s) { assert (current_len_ <= MAX_SIZE); if (fs_) { if (s >= 0) { log_debug << "Saving state: " << u << ':' << s; } char buf[MAX_SIZE]; const gu_uuid_t* const uu(reinterpret_cast(&u)); int state_len = snprintf (buf, MAX_SIZE - 1, "# GALERA saved state" "\nversion: "VERSION "\nuuid: "GU_UUID_FORMAT "\nseqno: %"PRId64"\ncert_index:\n", GU_UUID_ARGS(uu), s); int write_size; for (write_size = state_len; write_size < current_len_; ++write_size) buf[write_size] = ' '; // overwrite whatever is there currently rewind(fs_); fwrite(buf, write_size, 1, fs_); fflush(fs_); current_len_ = state_len; written_uuid_ = u; ++total_writes_; } else { log_debug << "Can't save state: output stream is not open."; } } } /* namespace galera */ percona-xtradb-cluster-galera/galera/src/saved_state.hpp0000644000000000000000000000270712247075736023715 0ustar rootroot00000000000000// // Copyright (C) 2012 Codership Oy // #ifndef GALERA_SAVED_STATE_HPP #define GALERA_SAVED_STATE_HPP #include "gu_atomic.hpp" #include "gu_mutex.hpp" #include "gu_lock.hpp" #include "wsrep_api.h" #include #include namespace galera { class SavedState { public: SavedState (const std::string& file); ~SavedState (); void get (wsrep_uuid_t& u, wsrep_seqno_t& s); void set (const wsrep_uuid_t& u, wsrep_seqno_t s = WSREP_SEQNO_UNDEFINED); void mark_unsafe(); void mark_safe(); void mark_corrupt(); void stats(long& marks, long& locks, long& writes) { marks = total_marks_(); locks = total_locks_; writes = total_writes_; } private: FILE* fs_; wsrep_uuid_t uuid_; wsrep_seqno_t seqno_; gu::Atomic unsafe_; bool corrupt_; /* this mutex is needed because mark_safe() and mark_corrupt() will be * called outside local monitor, so race is possible */ gu::Mutex mtx_; wsrep_uuid_t written_uuid_; ssize_t current_len_; gu::Atomic total_marks_; long total_locks_; long total_writes_; void write_and_flush (const wsrep_uuid_t& u, const wsrep_seqno_t s); SavedState (const SavedState&); SavedState& operator=(const SavedState&); }; /* class SavedState */ } /* namespace galera */ #endif /* GALERA_SAVED_STATE_HPP */ percona-xtradb-cluster-galera/galera/src/trx_handle.cpp0000644000000000000000000002021512247075736023530 0ustar rootroot00000000000000// // Copyright (C) 2010-2012 Codership Oy // #include "trx_handle.hpp" #include "uuid.hpp" #include "gu_serialize.hpp" std::ostream& galera::operator<<(std::ostream& os, TrxHandle::State s) { switch (s) { case TrxHandle::S_EXECUTING: return (os << "EXECUTING"); case TrxHandle::S_MUST_ABORT: return (os << "MUST_ABORT"); case TrxHandle::S_ABORTING: return (os << "ABORTING"); case TrxHandle::S_REPLICATING: return (os << "REPLICATING"); case TrxHandle::S_CERTIFYING: return (os << "CERTIFYING"); case TrxHandle::S_MUST_CERT_AND_REPLAY: return (os << "MUST_CERT_AND_REPLAY"); case TrxHandle::S_MUST_REPLAY_AM: return (os << "MUST_REPLAY_AM"); case TrxHandle::S_MUST_REPLAY_CM: return (os << "MUST_REPLAY_CM"); case TrxHandle::S_MUST_REPLAY: return (os << "MUST_REPLAY"); case TrxHandle::S_REPLAYING: return (os << "REPLAYING"); case TrxHandle::S_APPLYING: return (os << "APPLYING"); case TrxHandle::S_COMMITTING: return (os << "COMMITTING"); case TrxHandle::S_COMMITTED: return (os << "COMMITTED"); case TrxHandle::S_ROLLED_BACK: return (os << "ROLLED_BACK"); } gu_throw_fatal << "invalid state " << static_cast(s); } std::ostream& galera::operator<<(std::ostream& os, const TrxHandle& th) { return (os << "source: " << th.source_id_ << " version: " << th.version_ << " local: " << th.local_ << " state: " << th.state_() << " flags: " << th.write_set_flags_ << " conn_id: " << int64_t(th.conn_id_) << " trx_id: " << int64_t(th.trx_id_) // for readability << " seqnos (l: " << th.local_seqno_ << ", g: " << th.global_seqno_ << ", s: " << th.last_seen_seqno_ << ", d: " << th.depends_seqno_ << ", ts: " << th.timestamp_ << ")"); } galera::TrxHandle::Fsm::TransMap galera::TrxHandle::trans_map_; static class TransMapBuilder { public: void add(galera::TrxHandle::State from, galera::TrxHandle::State to) { using galera::TrxHandle; using std::make_pair; typedef TrxHandle::Transition Transition; typedef TrxHandle::Fsm::TransAttr TransAttr; TrxHandle::Fsm::TransMap& trans_map(TrxHandle::trans_map_); trans_map.insert_unique(make_pair(Transition(from, to), TransAttr())); } TransMapBuilder() { using galera::TrxHandle; add(TrxHandle::S_EXECUTING, TrxHandle::S_MUST_ABORT); add(TrxHandle::S_EXECUTING, TrxHandle::S_REPLICATING); add(TrxHandle::S_EXECUTING, TrxHandle::S_ROLLED_BACK); add(TrxHandle::S_MUST_ABORT, TrxHandle::S_MUST_CERT_AND_REPLAY); add(TrxHandle::S_MUST_ABORT, TrxHandle::S_MUST_REPLAY_AM); add(TrxHandle::S_MUST_ABORT, TrxHandle::S_MUST_REPLAY_CM); add(TrxHandle::S_MUST_ABORT, TrxHandle::S_MUST_REPLAY); add(TrxHandle::S_MUST_ABORT, TrxHandle::S_MUST_ABORT); add(TrxHandle::S_MUST_ABORT, TrxHandle::S_ABORTING); add(TrxHandle::S_ABORTING, TrxHandle::S_ROLLED_BACK); add(TrxHandle::S_REPLICATING, TrxHandle::S_CERTIFYING); add(TrxHandle::S_REPLICATING, TrxHandle::S_MUST_CERT_AND_REPLAY); add(TrxHandle::S_REPLICATING, TrxHandle::S_MUST_ABORT); add(TrxHandle::S_CERTIFYING, TrxHandle::S_MUST_ABORT); add(TrxHandle::S_CERTIFYING, TrxHandle::S_APPLYING); add(TrxHandle::S_CERTIFYING, TrxHandle::S_MUST_CERT_AND_REPLAY); add(TrxHandle::S_CERTIFYING, TrxHandle::S_MUST_REPLAY_AM); // trx replay add(TrxHandle::S_APPLYING, TrxHandle::S_MUST_ABORT); add(TrxHandle::S_APPLYING, TrxHandle::S_COMMITTING); add(TrxHandle::S_COMMITTING, TrxHandle::S_COMMITTED); add(TrxHandle::S_COMMITTING, TrxHandle::S_MUST_ABORT); add(TrxHandle::S_MUST_CERT_AND_REPLAY, TrxHandle::S_CERTIFYING); add(TrxHandle::S_MUST_CERT_AND_REPLAY, TrxHandle::S_ABORTING); add(TrxHandle::S_MUST_REPLAY_AM, TrxHandle::S_MUST_REPLAY_CM); add(TrxHandle::S_MUST_REPLAY_CM, TrxHandle::S_MUST_REPLAY); add(TrxHandle::S_MUST_REPLAY, TrxHandle::S_REPLAYING); add(TrxHandle::S_REPLAYING, TrxHandle::S_COMMITTED); } } trans_map_builder_; size_t galera::serialize(const TrxHandle::Mac& mac, gu::byte_t* buf, size_t buflen, size_t offset) { // header: // type: 1 byte // len: 1 byte return gu::serialize2(uint16_t(0), buf, buflen, offset); } size_t galera::unserialize(const gu::byte_t* buf, size_t buflen, size_t offset, TrxHandle::Mac& mac) { uint16_t hdr; offset = gu::unserialize2(buf, buflen, offset, hdr); switch ((hdr >> 8) & 0xff) { case 0: break; default: log_warn << "unrecognized mac type" << ((hdr >> 8) & 0xff); } // skip over the body offset += (hdr & 0xff); return offset; } size_t galera::serial_size(const TrxHandle::Mac& mac) { return 2; // sizeof(uint16_t); // Hm, isn't is somewhat short for mac? } size_t galera::serialize(const TrxHandle& trx, gu::byte_t* buf, size_t buflen, size_t offset) { uint32_t hdr((trx.version_ << 24) | (trx.write_set_flags_ & 0xff)); offset = gu::serialize4(hdr, buf, buflen, offset); offset = serialize(trx.source_id_, buf, buflen, offset); offset = gu::serialize8(trx.conn_id_, buf, buflen, offset); offset = gu::serialize8(trx.trx_id_, buf, buflen, offset); offset = gu::serialize8(trx.last_seen_seqno_, buf, buflen, offset); offset = gu::serialize8(trx.timestamp_, buf, buflen, offset); if (trx.has_annotation()) { offset = gu::serialize4(trx.annotation_, buf, buflen, offset); } if (trx.has_mac()) { offset = serialize(trx.mac_, buf, buflen, offset); } return offset; } size_t galera::unserialize(const gu::byte_t* buf, size_t buflen, size_t offset, TrxHandle& trx) { uint32_t hdr; try { offset = gu::unserialize4(buf, buflen, offset, hdr); trx.write_set_flags_ = hdr & 0xff; trx.version_ = hdr >> 24; trx.write_set_.set_version(trx.version_); switch (trx.version_) { case 0: case 1: case 2: offset = unserialize(buf, buflen, offset, trx.source_id_); offset = gu::unserialize8(buf, buflen, offset, trx.conn_id_); offset = gu::unserialize8(buf, buflen, offset, trx.trx_id_); offset = gu::unserialize8(buf, buflen, offset, trx.last_seen_seqno_); offset = gu::unserialize8(buf, buflen, offset, trx.timestamp_); if (trx.has_annotation()) { offset = gu::unserialize4(buf, buflen, offset, trx.annotation_); } if (trx.has_mac()) { offset = unserialize(buf, buflen, offset, trx.mac_); } break; default: gu_throw_error(EPROTONOSUPPORT); } return offset; } catch (gu::Exception& e) { GU_TRACE(e); log_fatal << "Writeset deserialization failed: " << e.what() << std::endl << "WS flags: " << trx.write_set_flags_ << std::endl << "Trx proto: " << trx.version_ << std::endl << "Trx source: " << trx.source_id_ << std::endl << "Trx conn_id: " << trx.conn_id_ << std::endl << "Trx trx_id: " << trx.trx_id_ << std::endl << "Trx last_seen: " << trx.last_seen_seqno_; throw; } } size_t galera::serial_size(const TrxHandle& trx) { return (4 // hdr + serial_size(trx.source_id_) + 8 // serial_size(trx.conn_id_) + 8 // serial_size(trx.trx_id_) + 8 // serial_size(trx.last_seen_seqno_) + 8 // serial_size(trx.timestamp_) + (trx.has_annotation() ? gu::serial_size4(trx.annotation_) : 0) + (trx.has_mac() ? serial_size(trx.mac_) : 0)); } percona-xtradb-cluster-galera/galera/src/trx_handle.hpp0000644000000000000000000003434212247075736023543 0ustar rootroot00000000000000// // Copyright (C) 2010-2012 Codership Oy // #ifndef GALERA_TRX_HANDLE_HPP #define GALERA_TRX_HANDLE_HPP #include "write_set.hpp" #include "mapped_buffer.hpp" #include "fsm.hpp" #include "key_entry.hpp" #include "wsrep_api.h" #include "gu_mutex.hpp" #include "gu_atomic.hpp" #include "gu_datetime.hpp" #include "gu_unordered.hpp" #include namespace galera { // class KeyEntry; // Forward declaration static const std::string working_dir = "/tmp"; class TrxHandle { public: enum { F_COMMIT = 1 << 0, F_ROLLBACK = 1 << 1, F_OOC = 1 << 2, F_MAC_HEADER = 1 << 3, F_MAC_PAYLOAD = 1 << 4, F_ANNOTATION = 1 << 5, F_ISOLATION = 1 << 6, F_PA_UNSAFE = 1 << 7 }; static inline uint32_t wsrep_flags_to_trx_flags (uint32_t flags) { uint64_t ret(0); if (flags & WSREP_FLAG_COMMIT) ret |= F_COMMIT; if (flags & WSREP_FLAG_ROLLBACK) ret |= F_ROLLBACK; if (flags & WSREP_FLAG_PA_UNSAFE) ret |= F_PA_UNSAFE; if (flags & WSREP_FLAG_ISOLATION) ret |= F_ISOLATION; return ret; } static inline uint32_t trx_flags_to_wsrep_flags (uint32_t flags) { uint64_t ret(0); if (flags & F_COMMIT) ret |= WSREP_FLAG_COMMIT; if (flags & F_ROLLBACK) ret |= WSREP_FLAG_ROLLBACK; if (flags & F_PA_UNSAFE) ret |= WSREP_FLAG_PA_UNSAFE; if (flags & F_ISOLATION) ret |= WSREP_FLAG_ISOLATION; return ret; } bool has_mac() const { return ((write_set_flags_ & (F_MAC_HEADER | F_MAC_PAYLOAD)) != 0); } bool has_annotation() const { return ((write_set_flags_ & F_ANNOTATION) != 0); } bool is_toi() const { return ((write_set_flags_ & F_ISOLATION) != 0); } bool pa_unsafe() const { return ((write_set_flags_ & F_PA_UNSAFE) != 0); } typedef enum { S_EXECUTING, S_MUST_ABORT, S_ABORTING, S_REPLICATING, S_CERTIFYING, S_MUST_CERT_AND_REPLAY, S_MUST_REPLAY_AM, // grab apply_monitor, commit_monitor, replay S_MUST_REPLAY_CM, // commit_monitor, replay S_MUST_REPLAY, // replay S_REPLAYING, S_APPLYING, // grabbing apply monitor, applying S_COMMITTING, // grabbing commit monitor, committing changes S_COMMITTED, S_ROLLED_BACK } State; class Transition { public: Transition(State const from, State const to) : from_(from), to_(to) { } State from() const { return from_; } State to() const { return to_; } bool operator==(Transition const& other) const { return (from_ == other.from_ && to_ == other.to_); } class Hash { public: size_t operator()(Transition const& tr) const { return (gu::HashValue(static_cast(tr.from_)) ^ gu::HashValue(static_cast(tr.to_))); } }; private: State from_; State to_; }; typedef FSM Fsm; static Fsm::TransMap trans_map_; // Placeholder for message authentication code class Mac { public: Mac() { } ~Mac() { } private: friend size_t serialize(const galera::TrxHandle::Mac& mac, gu::byte_t* buf, size_t buflen, size_t offset); friend size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset, galera::TrxHandle::Mac& mac); friend size_t serial_size(const galera::TrxHandle::Mac&); }; explicit TrxHandle(int version = -1, const wsrep_uuid_t& source_id = WSREP_UUID_UNDEFINED, wsrep_conn_id_t conn_id = -1, wsrep_trx_id_t trx_id = -1, bool local = false) : version_ (version), source_id_ (source_id), conn_id_ (conn_id), trx_id_ (trx_id), local_ (local), mutex_ (), write_set_collection_(working_dir), state_ (&trans_map_, S_EXECUTING), local_seqno_ (WSREP_SEQNO_UNDEFINED), global_seqno_ (WSREP_SEQNO_UNDEFINED), last_seen_seqno_ (WSREP_SEQNO_UNDEFINED), depends_seqno_ (WSREP_SEQNO_UNDEFINED), refcnt_ (1), write_set_ (version), write_set_flags_ (0), certified_ (false), committed_ (false), exit_loop_ (false), gcs_handle_ (-1), action_ (0), timestamp_ (gu_time_calendar()), mac_ (), annotation_ (), write_set_buffer_ (0, 0), cert_keys_ () { } void lock() const { mutex_.lock(); } void unlock() const { mutex_.unlock(); } int version() const { return version_; } const wsrep_uuid_t& source_id() const { return source_id_; } wsrep_trx_id_t trx_id() const { return trx_id_; } void set_conn_id(wsrep_conn_id_t conn_id) { conn_id_ = conn_id; } wsrep_conn_id_t conn_id() const { return conn_id_; } bool is_local() const { return local_; } bool is_certified() const { return certified_; } void mark_certified() { certified_ = true; } bool is_committed() const { return committed_; } void mark_committed() { committed_ = true; } void set_received (const void* action, wsrep_seqno_t seqno_l, wsrep_seqno_t seqno_g) { action_ = action; local_seqno_ = seqno_l; global_seqno_ = seqno_g; } void set_last_seen_seqno(wsrep_seqno_t last_seen_seqno) { last_seen_seqno_ = last_seen_seqno; } void set_depends_seqno(wsrep_seqno_t seqno_lt) { depends_seqno_ = seqno_lt; } void set_state(State state) { state_.shift_to(state); } State state() const { return state_(); } void set_gcs_handle(long gcs_handle) { gcs_handle_ = gcs_handle; } long gcs_handle() const { return gcs_handle_; } const void* action() const { return action_; } wsrep_seqno_t local_seqno() const { return local_seqno_; } wsrep_seqno_t global_seqno() const { return global_seqno_; } wsrep_seqno_t last_seen_seqno() const { return last_seen_seqno_; } wsrep_seqno_t depends_seqno() const { return depends_seqno_; } void set_flags(uint32_t flags) { write_set_flags_ = flags; } int flags() const { return write_set_flags_; } void append_key(const Key& key) { if (key.version() != version_) { gu_throw_error(EINVAL) << "key version '" << key.version() << "' does not match to trx version' " << version_ << "'"; } write_set_.append_key(key); } void append_data(const void* data, const size_t data_len) { write_set_.append_data(data, data_len); } static const size_t max_annotation_size_ = (1 << 16); void append_annotation(const gu::byte_t* buf, size_t buf_len) { buf_len = std::min(buf_len, max_annotation_size_ - annotation_.size()); annotation_.insert(annotation_.end(), buf, buf + buf_len); } const gu::Buffer& annotation() const { return annotation_; } const WriteSet& write_set() const { return write_set_; } size_t prepare_write_set_collection() { size_t offset; if (write_set_collection_.empty() == true) { offset = serial_size(*this); write_set_collection_.resize(offset); } else { offset = write_set_collection_.size(); } (void)serialize(*this, &write_set_collection_[0], offset, 0); return offset; } void append_write_set(const void* data, size_t data_len) { const size_t offset(prepare_write_set_collection()); write_set_collection_.resize(offset + data_len); std::copy(reinterpret_cast(data), reinterpret_cast(data) + data_len, &write_set_collection_[0] + offset); } void append_write_set(const gu::Buffer& ws) { const size_t offset(prepare_write_set_collection()); write_set_collection_.resize(offset + ws.size()); std::copy(ws.begin(), ws.end(), &write_set_collection_[0] + offset); } const MappedBuffer& write_set_collection() const { return write_set_collection_; } void set_write_set_buffer(const gu::byte_t* buf, size_t buf_len) { write_set_buffer_.first = buf; write_set_buffer_.second = buf_len; } std::pair write_set_buffer() const { // If external write set buffer location not specified, // return location from write_set_colletion_. This is still // needed for unit tests and IST which don't use GCache // storage. if (write_set_buffer_.first == 0) { size_t off(serial_size(*this)); if (write_set_collection_.size() < off) { gu_throw_fatal << "Write set buffer not populated"; } return std::make_pair(&write_set_collection_[0] + off, write_set_collection_.size() - off); } return write_set_buffer_; } bool empty() const { return (write_set_.empty() == true && write_set_collection_.size() <= serial_size(*this)); } void flush(size_t mem_limit) { if (write_set_.get_key_buf().size() + write_set_.get_data().size() > mem_limit || mem_limit == 0) { gu::Buffer buf(serial_size(write_set_)); (void)serialize(write_set_, &buf[0], buf.size(), 0); append_write_set(buf); write_set_.clear(); } } void clear() { write_set_.clear(); write_set_collection_.clear(); } void ref() { ++refcnt_; } void unref() { if (refcnt_.sub_and_fetch(1) == 0) delete this; } size_t refcnt() const { return refcnt_(); } bool exit_loop() const { return exit_loop_; } void set_exit_loop(bool x) { exit_loop_ |= x; } private: ~TrxHandle() { } TrxHandle(const TrxHandle&); void operator=(const TrxHandle& other); int version_; wsrep_uuid_t source_id_; wsrep_conn_id_t conn_id_; wsrep_trx_id_t trx_id_; bool local_; mutable gu::Mutex mutex_; MappedBuffer write_set_collection_; FSM state_; wsrep_seqno_t local_seqno_; wsrep_seqno_t global_seqno_; wsrep_seqno_t last_seen_seqno_; wsrep_seqno_t depends_seqno_; gu::Atomic refcnt_; WriteSet write_set_; uint32_t write_set_flags_; bool certified_; bool committed_; bool exit_loop_; long gcs_handle_; const void* action_; int64_t timestamp_; Mac mac_; gu::Buffer annotation_; // Write set buffer location if stored outside TrxHandle. std::pair write_set_buffer_; // friend class Wsdb; friend class Certification; // typedef std::list > > CertKeySet; public: typedef gu::UnorderedMap, KeyEntryPtrHash, KeyEntryPtrEqualAll> CertKeySet; private: CertKeySet cert_keys_; friend size_t serialize(const TrxHandle&, gu::byte_t* buf, size_t buflen, size_t offset); friend size_t unserialize(const gu::byte_t*, size_t, size_t, TrxHandle&); friend size_t serial_size(const TrxHandle&); friend std::ostream& operator<<(std::ostream& os, const TrxHandle& trx); }; std::ostream& operator<<(std::ostream& os, TrxHandle::State s); size_t serialize(const TrxHandle&, gu::byte_t* buf, size_t buflen, size_t offset); size_t unserialize(const gu::byte_t*, size_t, size_t, TrxHandle&); size_t serial_size(const TrxHandle&); class TrxHandleLock { public: TrxHandleLock(TrxHandle& trx) : trx_(trx) { trx_.lock(); } ~TrxHandleLock() { trx_.unlock(); } private: TrxHandle& trx_; }; template class Unref2nd { public: void operator()(T& t) const { t.second->unref(); } }; } #endif // GALERA_TRX_HANDLE_HPP percona-xtradb-cluster-galera/galera/src/uuid.hpp0000644000000000000000000000435612247075736022363 0ustar rootroot00000000000000// // Copyright (C) 2010 Codership Oy // #ifndef GALERA_UUID_HPP #define GALERA_UUID_HPP #include "wsrep_api.h" #include "gu_uuid.h" #include "gu_assert.hpp" #include "gu_buffer.hpp" #include inline bool operator==(const wsrep_uuid_t& a, const wsrep_uuid_t& b) { return (memcmp(&a, &b, sizeof(a)) == 0); } inline bool operator!=(const wsrep_uuid_t& a, const wsrep_uuid_t& b) { return !(a == b); } namespace galera { inline std::ostream& operator<<(std::ostream& os, const wsrep_uuid_t& uuid) { char uuid_buf[GU_UUID_STR_LEN + 1]; ssize_t ret(gu_uuid_print(reinterpret_cast(&uuid), uuid_buf, sizeof(uuid_buf))); (void)ret; assert(ret == GU_UUID_STR_LEN); uuid_buf[GU_UUID_STR_LEN] = '\0'; return (os << uuid_buf); } inline void string2uuid (const std::string& str, wsrep_uuid_t& uuid) { ssize_t ret(gu_uuid_scan(str.c_str(), str.length(), reinterpret_cast(&uuid))); if (ret == -1) gu_throw_error(EINVAL) << "could not parse UUID from '" << str << '\'' ; } inline std::istream& operator>>(std::istream& is, wsrep_uuid_t& uuid) { // @todo char cstr[GU_UUID_STR_LEN + 1]; is.width(GU_UUID_STR_LEN + 1); is >> cstr; string2uuid(cstr, uuid); return is; } inline size_t serial_size(const wsrep_uuid_t& uuid) { return sizeof(uuid.data); } inline size_t serialize(const wsrep_uuid_t& uuid, gu::byte_t* buf, size_t buflen, size_t offset) { if (offset + serial_size(uuid) > buflen) gu_throw_fatal; memcpy(buf + offset, uuid.data, serial_size(uuid)); offset += serial_size(uuid); return offset; } inline size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset, wsrep_uuid_t& uuid) { if (offset + serial_size(uuid) > buflen) gu_throw_fatal; memcpy(uuid.data, buf + offset, serial_size(uuid)); offset += serial_size(uuid); return offset; } } #endif // GALERA_UUID_HPP percona-xtradb-cluster-galera/galera/src/write_set.cpp0000644000000000000000000000567612247075736023423 0ustar rootroot00000000000000// // Copyright (C) 2010-2012 Codership Oy // #include "write_set.hpp" #include "gu_serialize.hpp" #include "gu_logger.hpp" size_t galera::serialize(const WriteSet& ws, gu::byte_t* buf, size_t buf_len, size_t offset) { offset = gu::serialize4(ws.keys_, buf, buf_len, offset); offset = gu::serialize4(ws.data_, buf, buf_len, offset); return offset; } size_t galera::unserialize(const gu::byte_t* buf, size_t buf_len, size_t offset, WriteSet& ws) { ws.keys_.clear(); offset = gu::unserialize4(buf, buf_len, offset, ws.keys_); offset = gu::unserialize4(buf, buf_len, offset, ws.data_); return offset; } size_t galera::serial_size(const WriteSet& ws) { return (gu::serial_size4(ws.keys_) + gu::serial_size4(ws.data_)); } std::pair galera::WriteSet::segment(const gu::byte_t* buf, size_t buf_len, size_t offset) { uint32_t data_len; offset = gu::unserialize4(buf, buf_len, offset, data_len); if (offset + data_len > buf_len) gu_throw_error(EMSGSIZE); return std::pair(offset, data_len); } size_t galera::WriteSet::keys(const gu::byte_t* buf, size_t buf_len, size_t offset, int version, KeySequence& ks) { std::pair seg(segment(buf, buf_len, offset)); offset = seg.first; const size_t seg_end(seg.first + seg.second); assert(seg_end <= buf_len); while (offset < seg_end) { Key key(version); if ((offset = unserialize(buf, buf_len, offset, key)) == 0) { gu_throw_fatal << "failed to unserialize key"; } ks.push_back(key); } assert(offset == seg_end); return offset; } void galera::WriteSet::append_key(const Key& key) { const size_t hash(key.hash()); std::pair range(key_refs_.equal_range(hash)); for (KeyRefMap::const_iterator i(range.first); i != range.second; ++i) { Key cmp(version_); (void)unserialize(&keys_[0], keys_.size(), i->second, cmp); if (key == cmp && key.flags() == cmp.flags()) return; } size_t key_size(serial_size(key)); size_t offset(keys_.size()); keys_.resize(offset + key_size); (void)galera::serialize(key, &keys_[0], keys_.size(), offset); (void)key_refs_.insert(std::make_pair(hash, offset)); } void galera::WriteSet::get_keys(KeySequence& s) const { size_t offset(0); while (offset < keys_.size()) { Key key(version_); if ((offset = unserialize(&keys_[0], keys_.size(), offset, key)) == 0) { gu_throw_fatal << "failed to unserialize key"; } s.push_back(key); } assert(offset == keys_.size()); } percona-xtradb-cluster-galera/galera/src/write_set.hpp0000644000000000000000000000421512247075736023414 0ustar rootroot00000000000000// // Copyright (C) 2010 Codership Oy // #ifndef GALERA_WRITE_SET_HPP #define GALERA_WRITE_SET_HPP #include "key.hpp" #include "wsrep_api.h" #include "gu_buffer.hpp" #include "gu_logger.hpp" #include "gu_unordered.hpp" #include #include #include namespace galera { class WriteSet { public: typedef std::deque KeySequence; WriteSet(int version) : version_(version), keys_(), key_refs_(), data_() { } void set_version(int version) { version_ = version; } const gu::Buffer& get_data() const { return data_; } void append_key(const Key&); void append_data(const void*data, size_t data_len) { data_.reserve(data_.size() + data_len); data_.insert(data_.end(), reinterpret_cast(data), reinterpret_cast(data) + data_len); } void get_keys(KeySequence&) const; const gu::Buffer& get_key_buf() const { return keys_; } bool empty() const { return (data_.size() == 0 && keys_.size() == 0); } void clear() { keys_.clear(), key_refs_.clear(), data_.clear(); } // Return offset to beginning of key or data segment and length // of that segment static std::pair segment(const gu::byte_t*, size_t, size_t); // Scan key sequence from buffer, return offset from the beginning of // buffer after scan. static size_t keys(const gu::byte_t*, size_t, size_t, int, KeySequence&); private: friend size_t serialize(const WriteSet&, gu::byte_t*, size_t, size_t); friend size_t unserialize(const gu::byte_t*, size_t, size_t, WriteSet&); friend size_t serial_size(const WriteSet&); typedef gu::UnorderedMultimap KeyRefMap; int version_; gu::Buffer keys_; KeyRefMap key_refs_; gu::Buffer data_; }; } #endif // GALERA_WRITE_SET_HPP percona-xtradb-cluster-galera/galera/src/wsdb.cpp0000644000000000000000000001002712247075736022337 0ustar rootroot00000000000000/* * Copyright (C) 2010-2013 Codership Oy */ #include "wsdb.hpp" #include "trx_handle.hpp" #include "write_set.hpp" #include "gu_lock.hpp" #include "gu_throw.hpp" std::ostream& galera::operator<<(std::ostream& os, const galera::Wsdb& wsdb) { os << "trx map:\n"; for (galera::Wsdb::TrxMap::const_iterator i = wsdb.trx_map_.begin(); i != wsdb.trx_map_.end(); ++i) { os << i->first << " " << *i->second << "\n"; } os << "conn query map:\n"; for (galera::Wsdb::ConnMap::const_iterator i = wsdb.conn_map_.begin(); i != wsdb.conn_map_.end(); ++i) { os << i->first << " "; } os << "\n"; return os; } galera::Wsdb::Wsdb() : trx_map_(), trx_mutex_(), conn_map_(), conn_mutex_() { } galera::Wsdb::~Wsdb() { log_info << "wsdb trx map usage " << trx_map_.size() << " conn query map usage " << conn_map_.size(); // With debug builds just print trx and query maps to stderr // and don't clean up to let valgrind etc to detect leaks. #ifndef NDEBUG std::cerr << *this; #else for_each(trx_map_.begin(), trx_map_.end(), Unref2nd()); #endif // !NDEBUG } inline galera::TrxHandle* galera::Wsdb::find_trx(wsrep_trx_id_t const trx_id) { gu::Lock lock(trx_mutex_); TrxMap::iterator const i(trx_map_.find(trx_id)); return (trx_map_.end() == i ? 0 : i->second); } inline galera::TrxHandle* galera::Wsdb::create_trx(int const version, const wsrep_uuid_t& source_id, wsrep_trx_id_t const trx_id) { TrxHandle* trx(new TrxHandle(version, source_id, -1, trx_id, true)); gu::Lock lock(trx_mutex_); std::pair i (trx_map_.insert(std::make_pair(trx_id, trx))); if (gu_unlikely(i.second == false)) gu_throw_fatal; return i.first->second; } galera::TrxHandle* galera::Wsdb::get_trx(int const version, const wsrep_uuid_t& source_id, wsrep_trx_id_t const trx_id, bool const create) { TrxHandle* retval(find_trx(trx_id)); if (0 == retval && create) retval = create_trx(version, source_id, trx_id); if (retval != 0) retval->ref(); return retval; } galera::Wsdb::Conn* galera::Wsdb::get_conn(wsrep_conn_id_t const conn_id, bool const create) { gu::Lock lock(conn_mutex_); ConnMap::iterator i(conn_map_.find(conn_id)); if (conn_map_.end() == i) { if (create == true) { std::pair p (conn_map_.insert(std::make_pair(conn_id, Conn(conn_id)))); if (gu_unlikely(p.second == false)) gu_throw_fatal; return &p.first->second; } return 0; } return &(i->second); } galera::TrxHandle* galera::Wsdb::get_conn_query(int const version, const wsrep_uuid_t& source_id, wsrep_trx_id_t const conn_id, bool const create) { Conn* const conn(get_conn(conn_id, create)); if (0 == conn) return 0; if (conn->get_trx() == 0 && create == true) { TrxHandle* trx(new TrxHandle(version, source_id, conn_id, -1, true)); conn->assign_trx(trx); } return conn->get_trx(); } void galera::Wsdb::discard_trx(wsrep_trx_id_t trx_id) { gu::Lock lock(trx_mutex_); TrxMap::iterator i; if ((i = trx_map_.find(trx_id)) != trx_map_.end()) { i->second->unref(); trx_map_.erase(i); } } void galera::Wsdb::discard_conn_query(wsrep_conn_id_t conn_id) { gu::Lock lock(conn_mutex_); ConnMap::iterator i; if ((i = conn_map_.find(conn_id)) != conn_map_.end()) { i->second.assign_trx(0); } } void galera::Wsdb::discard_conn(wsrep_conn_id_t conn_id) { gu::Lock lock(conn_mutex_); ConnMap::iterator i; if ((i = conn_map_.find(conn_id)) != conn_map_.end()) { conn_map_.erase(i); } } percona-xtradb-cluster-galera/galera/src/wsdb.hpp0000644000000000000000000000511112247075736022342 0ustar rootroot00000000000000// // Copyright (C) 2010-2013 Codership Oy // #ifndef GALERA_WSDB_HPP #define GALERA_WSDB_HPP #include "trx_handle.hpp" #include "wsrep_api.h" #include "gu_unordered.hpp" namespace galera { class Wsdb { class Conn { public: Conn(wsrep_conn_id_t conn_id) : conn_id_(conn_id), trx_(0) { } Conn(const Conn& other) : conn_id_(other.conn_id_), trx_(other.trx_) { } ~Conn() { if (trx_ != 0) trx_->unref(); } void assign_trx(TrxHandle* trx) { if (trx_ != 0) trx_->unref(); trx_ = trx; } TrxHandle* get_trx() { return trx_; } private: void operator=(const Conn&); wsrep_conn_id_t conn_id_; TrxHandle* trx_; }; class TrxHash { public: size_t operator()(const wsrep_trx_id_t& key) const { return key; } }; typedef gu::UnorderedMap TrxMap; class ConnHash { public: size_t operator()(const wsrep_conn_id_t& key) const { return key; } }; typedef gu::UnorderedMap ConnMap; public: TrxHandle* get_trx(int version, const wsrep_uuid_t& source_id, wsrep_trx_id_t trx_id, bool create = false); void discard_trx(wsrep_trx_id_t trx_id); TrxHandle* get_conn_query(int version, const wsrep_uuid_t&, wsrep_conn_id_t conn_id, bool create = false); void discard_conn(wsrep_conn_id_t conn_id); void discard_conn_query(wsrep_conn_id_t conn_id); Wsdb(); ~Wsdb(); private: friend std::ostream& galera::operator<<(std::ostream& os, const Wsdb&); // Find existing trx handle in the map TrxHandle* find_trx(wsrep_trx_id_t trx_id); // Create new trx handle and add to map TrxHandle* create_trx(int, const wsrep_uuid_t&, wsrep_trx_id_t trx_id); Conn* get_conn(wsrep_conn_id_t conn_id, bool create); static const size_t trx_mem_limit_ = 1 << 20; TrxMap trx_map_; gu::Mutex trx_mutex_; ConnMap conn_map_; gu::Mutex conn_mutex_; }; } #endif // GALERA_WSDB_HPP percona-xtradb-cluster-galera/galera/src/wsrep_api.h0000644000000000000000000011513312247075736023042 0ustar rootroot00000000000000/* Copyright (C) 2009-2013 Codership Oy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /*! @file wsrep API declaration. HOW TO READ THIS FILE. Due to C language rules this header layout doesn't lend itself to intuitive reading. So here's the scoop: in the end this header declares two main types: * struct wsrep_init_args and * struct wsrep wsrep_init_args contains initialization parameters for wsrep provider like names, addresses, etc. and pointers to callbacks. The callbacks will be called by provider when it needs to do something application-specific, like log a message or apply a writeset. It should be passed to init() call from wsrep API. It is an application part of wsrep API contract. struct wsrep is the interface to wsrep provider. It contains all wsrep API calls. It is a provider part of wsrep API contract. Finally, wsrep_load() method loads (dlopens) wsrep provider library. It is defined in wsrep_loader.c unit and is part of libwsrep.a (which is not a wsrep provider, but a convenience library). wsrep_unload() does the reverse. */ #ifndef WSREP_H #define WSREP_H #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif /************************************************************************** * * * wsrep replication API * * * **************************************************************************/ #define WSREP_INTERFACE_VERSION "25" /*! Empty backend spec */ #define WSREP_NONE "none" /*! * @brief log severity levels, passed as first argument to log handler */ typedef enum wsrep_log_level { WSREP_LOG_FATAL, //!< Unrecoverable error, application must quit. WSREP_LOG_ERROR, //!< Operation failed, must be repeated. WSREP_LOG_WARN, //!< Unexpected condition, but no operational failure. WSREP_LOG_INFO, //!< Informational message. WSREP_LOG_DEBUG //!< Debug message. Shows only of compiled with debug. } wsrep_log_level_t; /*! * @brief error log handler * * All messages from wsrep provider are directed to this * handler, if present. * * @param level log level * @param message log message */ typedef void (*wsrep_log_cb_t)(wsrep_log_level_t, const char *); /*! * Certain provider capabilities application may want to know about */ #define WSREP_CAP_MULTI_MASTER ( 1ULL << 0 ) #define WSREP_CAP_CERTIFICATION ( 1ULL << 1 ) #define WSREP_CAP_PARALLEL_APPLYING ( 1ULL << 2 ) #define WSREP_CAP_TRX_REPLAY ( 1ULL << 3 ) #define WSREP_CAP_ISOLATION ( 1ULL << 4 ) #define WSREP_CAP_PAUSE ( 1ULL << 5 ) #define WSREP_CAP_CAUSAL_READS ( 1ULL << 6 ) #define WSREP_CAP_CAUSAL_TRX ( 1ULL << 7 ) #define WSREP_CAP_INCREMENTAL_WRITESET ( 1ULL << 8 ) #define WSREP_CAP_SESSION_LOCKS ( 1ULL << 9 ) #define WSREP_CAP_DISTRIBUTED_LOCKS ( 1ULL << 10 ) #define WSREP_CAP_CONSISTENCY_CHECK ( 1ULL << 11 ) #define WSREP_CAP_UNORDERED ( 1ULL << 12 ) #define WSREP_CAP_ANNOTATION ( 1ULL << 13 ) #define WSREP_CAP_PREORDERED ( 1ULL << 14 ) /*! * Writeset flags * * COMMIT the writeset and all preceding writesets must be committed * ROLLBACK all preceding writesets in a transaction must be rolled back * ISOLATION the writeset must be applied AND committed in isolation * PA_UNSAFE the writeset cannot be applied in parallel * COMMUTATIVE the order in which the writeset is applied does not matter * NATIVE the writeset contains another writeset in this provider format * * Note that some of the flags are mutually exclusive (e.g. COMMIT and * ROLLBACK). */ #define WSREP_FLAG_COMMIT ( 1ULL << 0 ) #define WSREP_FLAG_ROLLBACK ( 1ULL << 1 ) #define WSREP_FLAG_ISOLATION ( 1ULL << 2 ) #define WSREP_FLAG_PA_UNSAFE ( 1ULL << 3 ) #define WSREP_FLAG_COMMUTATIVE ( 1ULL << 4 ) #define WSREP_FLAG_NATIVE ( 1ULL << 5 ) typedef uint64_t wsrep_trx_id_t; //!< application transaction ID typedef uint64_t wsrep_conn_id_t; //!< application connection ID typedef int64_t wsrep_seqno_t; //!< sequence number of a writeset, etc. typedef _Bool wsrep_bool_t; //!< should be the same as standard (C99) bool /*! undefined seqno */ #define WSREP_SEQNO_UNDEFINED (-1) /*! wsrep provider status codes */ typedef enum wsrep_status { WSREP_OK = 0, //!< success WSREP_WARNING, //!< minor warning, error logged WSREP_TRX_MISSING, //!< transaction is not known by wsrep WSREP_TRX_FAIL, //!< transaction aborted, server can continue WSREP_BF_ABORT, //!< trx was victim of brute force abort WSREP_SIZE_EXCEEDED, //!< data exceeded maximum supported size WSREP_CONN_FAIL, //!< error in client connection, must abort WSREP_NODE_FAIL, //!< error in node state, wsrep must reinit WSREP_FATAL, //!< fatal error, server must abort WSREP_NOT_IMPLEMENTED //!< feature not implemented } wsrep_status_t; /*! wsrep callbacks status codes */ typedef enum wsrep_cb_status { WSREP_CB_SUCCESS = 0, //!< success (as in "not critical failure") WSREP_CB_FAILURE //!< critical failure (consistency violation) /* Technically, wsrep provider has no use for specific failure codes since * there is nothing it can do about it but abort execution. Therefore any * positive number shall indicate a critical failure. Optionally that value * may be used by provider to come to a consensus about state consistency * in a group of nodes. */ } wsrep_cb_status_t; /*! * UUID type - for all unique IDs */ typedef struct wsrep_uuid { uint8_t data[16]; } wsrep_uuid_t; /*! Undefined UUID */ static const wsrep_uuid_t WSREP_UUID_UNDEFINED = {{0,}}; /*! UUID string representation length, terminating '\0' not included */ #define WSREP_UUID_STR_LEN 36 /*! * Scan UUID from string * @return length of UUID string representation or negative error code */ extern int wsrep_uuid_scan (const char* str, size_t str_len, wsrep_uuid_t* uuid); /*! * Print UUID to string * @return length of UUID string representation or negative error code */ extern int wsrep_uuid_print (const wsrep_uuid_t* uuid, char* str, size_t str_len); #define WSREP_MEMBER_NAME_LEN 32 //!< maximum logical member name length #define WSREP_INCOMING_LEN 256 //!< max Domain Name length + 0x00 /*! * Global transaction identifier */ typedef struct wsrep_gtid { wsrep_uuid_t uuid; /*!< History UUID */ wsrep_seqno_t seqno; /*!< Sequence number */ } wsrep_gtid_t; /*! Undefined GTID */ static const wsrep_gtid_t WSREP_GTID_UNDEFINED = {{{0, }}, -1}; /*! Minimum number of bytes guaranteed to store GTID string representation, * terminating '\0' not included (36 + 1 + 20) */ #define WSREP_GTID_STR_LEN 57 /*! * Scan GTID from string * @return length of GTID string representation or negative error code */ extern int wsrep_gtid_scan(const char* str, size_t str_len, wsrep_gtid_t* gtid); /*! * Print GTID to string * @return length of GTID string representation or negative error code */ extern int wsrep_gtid_print(const wsrep_gtid_t* gtid, char* str, size_t str_len); /*! * Transaction meta data */ typedef struct wsrep_trx_meta { wsrep_gtid_t gtid; /*!< Global transaction identifier */ wsrep_seqno_t depends_on; /*!< Sequence number part of the last transaction this transaction depends on */ } wsrep_trx_meta_t; /*! * member status */ typedef enum wsrep_member_status { WSREP_MEMBER_UNDEFINED, //!< undefined state WSREP_MEMBER_JOINER, //!< incomplete state, requested state transfer WSREP_MEMBER_DONOR, //!< complete state, donates state transfer WSREP_MEMBER_JOINED, //!< complete state WSREP_MEMBER_SYNCED, //!< complete state, synchronized with group WSREP_MEMBER_ERROR, //!< this and above is provider-specific error code WSREP_MEMBER_MAX } wsrep_member_status_t; /*! * static information about a group member (some fields are tentative yet) */ typedef struct wsrep_member_info { wsrep_uuid_t id; //!< group-wide unique member ID char name[WSREP_MEMBER_NAME_LEN]; //!< human-readable name char incoming[WSREP_INCOMING_LEN]; //!< address for client requests } wsrep_member_info_t; /*! * group status */ typedef enum wsrep_view_status { WSREP_VIEW_PRIMARY, //!< primary group configuration (quorum present) WSREP_VIEW_NON_PRIMARY, //!< non-primary group configuration (quorum lost) WSREP_VIEW_DISCONNECTED, //!< not connected to group, retrying. WSREP_VIEW_MAX } wsrep_view_status_t; /*! * view of the group */ typedef struct wsrep_view_info { wsrep_gtid_t state_id; //!< global state ID wsrep_seqno_t view; //!< global view number wsrep_view_status_t status; //!< view status wsrep_bool_t state_gap; //!< gap between global and local states int my_idx; //!< index of this member in the view int memb_num; //!< number of members in the view int proto_ver; //!< application protocol agreed on the view wsrep_member_info_t members[1];//!< array of member information } wsrep_view_info_t; /*! * Magic string to tell provider to engage into trivial (empty) state transfer. * No data will be passed, but the node shall be considered JOINED. * Should be passed in sst_req parameter of wsrep_view_cb_t. */ #define WSREP_STATE_TRANSFER_TRIVIAL "trivial" /*! * Magic string to tell provider not to engage in state transfer at all. * The member will stay in WSREP_MEMBER_UNDEFINED state but will keep on * receiving all writesets. * Should be passed in sst_req parameter of wsrep_view_cb_t. */ #define WSREP_STATE_TRANSFER_NONE "none" /*! * @brief group view handler * * This handler is called in total order corresponding to the group * configuration change. It is to provide a vital information about * new group view. If view info indicates existence of discontinuity * between group and member states, state transfer request message * should be filled in by the callback implementation. * * @note Currently it is assumed that sst_req is allocated using * malloc()/calloc()/realloc() and it will be freed by * wsrep implementation. * * @param app_ctx application context * @param recv_ctx receiver context * @param view new view on the group * @param state current state * @param state_len lenght of current state * @param sst_req location to store SST request * @param sst_req_len location to store SST request length or error code, * value of 0 means no SST. */ typedef enum wsrep_cb_status (*wsrep_view_cb_t) ( void* app_ctx, void* recv_ctx, const wsrep_view_info_t* view, const char* state, size_t state_len, void** sst_req, size_t* sst_req_len ); /*! * @brief apply callback * * This handler is called from wsrep library to apply replicated writeset * Must support brute force applying for multi-master operation * * @param recv_ctx receiver context pointer provided by the application * @param data data buffer containing the writeset * @param size data buffer size * @param flags WSREP_FLAG_... flags * @param meta transaction meta data of the writeset to be applied * * @return success code: * @retval WSREP_OK * @retval WSREP_NOT_IMPLEMENTED appl. does not support the writeset format * @retval WSREP_ERROR failed to apply the writeset */ typedef enum wsrep_cb_status (*wsrep_apply_cb_t) ( void* recv_ctx, const void* data, size_t size, uint32_t flags, const wsrep_trx_meta_t* meta ); /*! * @brief commit callback * * This handler is called to commit the changes made by apply callback. * * @param recv_ctx receiver context pointer provided by the application * @param flags WSREP_FLAG_... flags * @param meta transaction meta data of the writeset to be committed * @param exit set to true to exit recv loop * @param commit true - commit writeset, false - rollback writeset * * @return success code: * @retval WSREP_OK * @retval WSREP_ERROR call failed */ typedef enum wsrep_cb_status (*wsrep_commit_cb_t) ( void* recv_ctx, uint32_t flags, const wsrep_trx_meta_t* meta, wsrep_bool_t* exit, wsrep_bool_t commit ); /*! * @brief unordered callback * * This handler is called to execute unordered actions (actions that need not * to be executed in any particular order) attached to writeset. * * @param recv_ctx receiver context pointer provided by the application * @param data data buffer containing the writeset * @param size data buffer size */ typedef enum wsrep_cb_status (*wsrep_unordered_cb_t) ( void* recv_ctx, const void* data, size_t size ); /*! * @brief a callback to donate state snapshot * * This handler is called from wsrep library when it needs this node * to deliver state to a new cluster member. * No state changes will be committed for the duration of this call. * Wsrep implementation may provide internal state to be transmitted * to new cluster member for initial state. * * @param app_ctx application context * @param recv_ctx receiver context * @param msg state transfer request message * @param msg_len state transfer request message length * @param gtid current state ID on this node * @param state current wsrep internal state buffer * @param state_len current wsrep internal state buffer len * @param bypass bypass snapshot transfer, only transfer uuid:seqno pair */ typedef enum wsrep_cb_status (*wsrep_sst_donate_cb_t) ( void* app_ctx, void* recv_ctx, const void* msg, size_t msg_len, const wsrep_gtid_t* state_id, const char* state, size_t state_len, wsrep_bool_t bypass ); /*! * @brief a callback to signal application that wsrep state is synced * with cluster * * This callback is called after wsrep library has got in sync with * rest of the cluster. * * @param app_ctx application context */ typedef void (*wsrep_synced_cb_t) (void* app_ctx); /*! * Initialization parameters for wsrep provider. */ struct wsrep_init_args { void* app_ctx; //!< Application context for callbacks /* Configuration parameters */ const char* node_name; //!< Symbolic name of this node (e.g. hostname) const char* node_address; //!< Address to be used by wsrep provider const char* node_incoming; //!< Address for incoming client connections const char* data_dir; //!< Directory where wsrep files are kept if any const char* options; //!< Provider-specific configuration string int proto_ver; //!< Max supported application protocol version /* Application initial state information. */ const wsrep_gtid_t* state_id; //!< Application state GTID const char* state; //!< Initial state for wsrep provider size_t state_len; //!< Length of state buffer /* Application callbacks */ wsrep_log_cb_t logger_cb; //!< logging handler wsrep_view_cb_t view_handler_cb; //!< group view change handler /* Applier callbacks */ wsrep_apply_cb_t apply_cb; //!< apply callback wsrep_commit_cb_t commit_cb; //!< commit callback wsrep_unordered_cb_t unordered_cb; //!< callback for unordered actions /* State Snapshot Transfer callbacks */ wsrep_sst_donate_cb_t sst_donate_cb; //!< starting to donate wsrep_synced_cb_t synced_cb; //!< synced with group }; /*! Type of the stats variable value in struct wsrep_status_var */ typedef enum wsrep_var_type { WSREP_VAR_STRING, //!< pointer to null-terminated string WSREP_VAR_INT64, //!< int64_t WSREP_VAR_DOUBLE //!< double } wsrep_var_type_t; /*! Generalized stats variable representation */ struct wsrep_stats_var { const char* name; //!< variable name wsrep_var_type_t type; //!< variable value type union { int64_t _int64; double _double; const char* _string; } value; //!< variable value }; /*! Abstract data buffer structure */ typedef struct wsrep_buf { const void* ptr; /*!< Pointer to data buffer */ size_t len; /*!< Length of buffer */ } wsrep_buf_t; /*! Key struct used to pass certification keys for transaction handling calls. * A key consists of zero or more key parts. */ typedef struct wsrep_key { const wsrep_buf_t* key_parts; /*!< Array of key parts */ size_t key_parts_num; /*!< Number of key parts */ } wsrep_key_t; /*! Key type: * EXCLUSIVE conflicts with any key type * SEMI reserved. If not supported, should be interpeted as EXCLUSIVE * SHARED conflicts only with EXCLUSIVE keys */ typedef enum wsrep_key_type { WSREP_KEY_SHARED = 0, WSREP_KEY_SEMI, WSREP_KEY_EXCLUSIVE } wsrep_key_type_t; /*! Data type: * ORDERED state modification event that should be applied and committed * in order. * UNORDERED some action that does not modify state and execution of which is * optional and does not need to happen in order. * ANNOTATION (human readable) writeset annotation. */ typedef enum wsrep_data_type { WSREP_DATA_ORDERED = 0, WSREP_DATA_UNORDERED, WSREP_DATA_ANNOTATION } wsrep_data_type_t; /*! Transaction handle struct passed for wsrep transaction handling calls */ typedef struct wsrep_ws_handle { wsrep_trx_id_t trx_id; //!< transaction ID void* opaque; //!< opaque provider transaction context data } wsrep_ws_handle_t; /*! * @brief Helper method to reset trx writeset handle state when trx id changes * * Instead of passing wsrep_ws_handle_t directly to wsrep calls, * wrapping handle with this call offloads bookkeeping from * application. */ static inline wsrep_ws_handle_t* wsrep_ws_handle_for_trx( wsrep_ws_handle_t* ws_handle, wsrep_trx_id_t trx_id) { if (ws_handle->trx_id != trx_id) { ws_handle->trx_id = trx_id; ws_handle->opaque = NULL; } return ws_handle; } /*! * A handle for processing preordered actions. * Must be initialized to WSREP_PO_INITIALIZER before use. */ typedef struct wsrep_po_handle { void* opaque; } wsrep_po_handle_t; static const wsrep_po_handle_t WSREP_PO_INITIALIZER = { NULL }; typedef struct wsrep wsrep_t; /*! * wsrep interface for dynamically loadable libraries */ struct wsrep { const char *version; //!< interface version string /*! * @brief Initializes wsrep provider * * @param wsrep provider handle * @param args wsrep initialization parameters */ wsrep_status_t (*init) (wsrep_t* wsrep, const struct wsrep_init_args* args); /*! * @brief Returns provider capabilities flag bitmap * * @param wsrep provider handle */ uint64_t (*capabilities) (wsrep_t* wsrep); /*! * @brief Passes provider-specific configuration string to provider. * * @param wsrep provider handle * @param conf configuration string * * @retval WSREP_OK configuration string was parsed successfully * @retval WSREP_WARNING could't not parse conf string, no action taken */ wsrep_status_t (*options_set) (wsrep_t* wsrep, const char* conf); /*! * @brief Returns provider-specific string with current configuration values. * * @param wsrep provider handle * * @return a dynamically allocated string with current configuration * parameter values */ char* (*options_get) (wsrep_t* wsrep); /*! * @brief Opens connection to cluster * * Returns when either node is ready to operate as a part of the clsuter * or fails to reach operating status. * * @param wsrep provider handle * @param cluster_name unique symbolic cluster name * @param cluster_url URL-like cluster address (backend://address) * @param state_donor name of the node to be asked for state transfer. * @param bootstrap a flag to request initialization of a new wsrep * service rather then a connection to the existing one. * clister_url may still carry important initialization * parameters, like backend spec and/or listen address. */ wsrep_status_t (*connect) (wsrep_t* wsrep, const char* cluster_name, const char* cluster_url, const char* state_donor, wsrep_bool_t bootstrap); /*! * @brief Closes connection to cluster. * * If state_uuid and/or state_seqno is not NULL, will store final state * in there. * * @param wsrep this wsrep handler */ wsrep_status_t (*disconnect)(wsrep_t* wsrep); /*! * @brief start receiving replication events * * This function never returns * * @param wsrep provider handle * @param recv_ctx receiver context */ wsrep_status_t (*recv)(wsrep_t* wsrep, void* recv_ctx); /*! * @brief Replicates/logs result of transaction to other nodes and allocates * required resources. * * Must be called before transaction commit. Returns success code, which * caller must check. * In case of WSREP_OK, starts commit critical section, transaction can * commit. Otherwise transaction must rollback. * * @param wsrep provider handle * @param ws_handle writeset of committing transaction * @param conn_id connection ID * @param flags fine tuning the replication WSREP_FLAG_* * @param meta transaction meta data * * @retval WSREP_OK cluster-wide commit succeeded * @retval WSREP_TRX_FAIL must rollback transaction * @retval WSREP_CONN_FAIL must close client connection * @retval WSREP_NODE_FAIL must close all connections and reinit */ wsrep_status_t (*pre_commit)(wsrep_t* wsrep, wsrep_conn_id_t conn_id, wsrep_ws_handle_t* ws_handle, uint32_t flags, wsrep_trx_meta_t* meta); /*! * @brief Releases resources after transaction commit. * * Ends commit critical section. * * @param wsrep provider handle * @param ws_handle writeset of committing transaction * @retval WSREP_OK post_commit succeeded */ wsrep_status_t (*post_commit) (wsrep_t* wsrep, wsrep_ws_handle_t* ws_handle); /*! * @brief Releases resources after transaction rollback. * * @param wsrep provider handle * @param ws_handle writeset of committing transaction * @retval WSREP_OK post_rollback succeeded */ wsrep_status_t (*post_rollback)(wsrep_t* wsrep, wsrep_ws_handle_t* ws_handle); /*! * @brief Replay trx as a slave writeset * * If local trx has been aborted by brute force, and it has already * replicated before this abort, we must try if we can apply it as * slave trx. Note that slave nodes see only trx writesets and certification * test based on write set content can be different to DBMS lock conflicts. * * @param wsrep provider handle * @param ws_handle writeset of committing transaction * @param trx_ctx transaction context * * @retval WSREP_OK cluster commit succeeded * @retval WSREP_TRX_FAIL must rollback transaction * @retval WSREP_BF_ABORT brute force abort happened after trx replicated * must rollback transaction and try to replay * @retval WSREP_CONN_FAIL must close client connection * @retval WSREP_NODE_FAIL must close all connections and reinit */ wsrep_status_t (*replay_trx)(wsrep_t* wsrep, wsrep_ws_handle_t* ws_handle, void* trx_ctx); /*! * @brief Abort pre_commit() call of another thread. * * It is possible, that some high-priority transaction needs to abort * another transaction which is in pre_commit() call waiting for resources. * * The kill routine checks that abort is not attmpted against a transaction * which is front of the caller (in total order). * * @param wsrep provider handle * @param bf_seqno seqno of brute force trx, running this cancel * @param victim_trx transaction to be aborted, and which is committing * * @retval WSREP_OK abort secceded * @retval WSREP_WARNING abort failed */ wsrep_status_t (*abort_pre_commit)(wsrep_t* wsrep, wsrep_seqno_t bf_seqno, wsrep_trx_id_t victim_trx); /*! * @brief Appends a row reference to transaction writeset * * Both copy flag and key_type can be ignored by provider (key type * interpreted as WSREP_KEY_EXCLUSIVE). * * @param wsrep provider handle * @param ws_handle writeset handle * @param keys array of keys * @param count length of the array of keys * @param type type ot the key * @param copy can be set to FALSE if keys persist through commit. */ wsrep_status_t (*append_key)(wsrep_t* wsrep, wsrep_ws_handle_t* ws_handle, const wsrep_key_t* keys, size_t count, enum wsrep_key_type type, wsrep_bool_t copy); /*! * @brief Appends data to transaction writeset * * This method can be called any time before commit and it * appends a number of data buffers to transaction writeset. * * Both copy and unordered flags can be ignored by provider. * * @param wsrep provider handle * @param ws_handle writeset handle * @param data array of data buffers * @param count buffer count * @param type type of data * @param copy can be set to FALSE if data persists through commit. */ wsrep_status_t (*append_data)(wsrep_t* wsrep, wsrep_ws_handle_t* ws_handle, const struct wsrep_buf* data, size_t count, enum wsrep_data_type type, wsrep_bool_t copy); /*! * @brief Get causal ordering for read operation * * This call will block until causal ordering with all possible * preceding writes in the cluster is guaranteed. If pointer to * gtid is non-null, the call stores the global transaction ID * of the last transaction which is guaranteed to be ordered * causally before this call. * * @param wsrep provider handle * @param gtid location to store GTID */ wsrep_status_t (*causal_read)(wsrep_t* wsrep, wsrep_gtid_t* gtid); /*! * @brief Clears allocated connection context. * * Whenever a new connection ID is passed to wsrep provider through * any of the API calls, a connection context is allocated for this * connection. This call is to explicitly notify provider fo connection * closing. * * @param wsrep provider handle * @param conn_id connection ID * @param query the 'set database' query * @param query_len length of query (does not end with 0) */ wsrep_status_t (*free_connection)(wsrep_t* wsrep, wsrep_conn_id_t conn_id); /*! * @brief Replicates a query and starts "total order isolation" section. * * Replicates the action spec and returns success code, which caller must * check. Total order isolation continues until to_execute_end() is called. * * @param wsrep provider handle * @param conn_id connection ID * @param keys array of keys * @param keys_num lenght of the array of keys * @param action action buffer array to be executed * @param count action buffer count * @param meta transaction meta data * * @retval WSREP_OK cluster commit succeeded * @retval WSREP_CONN_FAIL must close client connection * @retval WSREP_NODE_FAIL must close all connections and reinit */ wsrep_status_t (*to_execute_start)(wsrep_t* wsrep, wsrep_conn_id_t conn_id, const wsrep_key_t* keys, size_t keys_num, const struct wsrep_buf* action, size_t count, wsrep_trx_meta_t* meta); /*! * @brief Ends the total order isolation section. * * Marks the end of total order isolation. TO locks are freed * and other transactions are free to commit from this point on. * * @param wsrep provider handle * @param conn_id connection ID * * @retval WSREP_OK cluster commit succeeded * @retval WSREP_CONN_FAIL must close client connection * @retval WSREP_NODE_FAIL must close all connections and reinit */ wsrep_status_t (*to_execute_end)(wsrep_t* wsrep, wsrep_conn_id_t conn_id); /*! * @brief Collects preordered replication events into a writeset. * * @param wsrep wsrep provider handle * @param handle a handle associated with a given writeset * @param data an array of data buffers. * @param count length of data buffer array. * @param copy whether provider needs to make a copy of events. * * @retval WSREP_OK cluster-wide commit succeeded * @retval WSREP_TRX_FAIL operation failed (e.g. trx size exceeded limit) * @retval WSREP_NODE_FAIL must close all connections and reinit */ wsrep_status_t (*preordered_collect) (wsrep_t* wsrep, wsrep_po_handle_t* handle, const struct wsrep_buf* data, size_t count, wsrep_bool_t copy); /*! * @brief "Commits" preordered writeset to cluster. * * The contract is that the writeset will be committed in the same (partial) * order this method was called. Frees resources associated with the writeset * handle and reinitializes the handle. * * @param wsrep wsrep provider handle * @param po_handle a handle associated with a given writeset * @param source_id ID of the event producer, also serves as the partial order * or stream ID - events with different source_ids won't be * ordered with respect to each other. * @param flags WSREP_FLAG_... flags * @param pa_range the number of preceding events this event can be processed * in parallel with. A value of 0 means strict serial * processing. Note: commits always happen in wsrep order. * @param commit 'true' to commit writeset to cluster (replicate) or * 'false' to rollback (cancel) the writeset. * * @retval WSREP_OK cluster-wide commit succeeded * @retval WSREP_TRX_FAIL operation failed (e.g. NON-PRIMARY component) * @retval WSREP_NODE_FAIL must close all connections and reinit */ wsrep_status_t (*preordered_commit) (wsrep_t* wsrep, wsrep_po_handle_t* handle, const wsrep_uuid_t* source_id, uint32_t flags, int pa_range, wsrep_bool_t commit); /*! * @brief Signals to wsrep provider that state snapshot has been sent to * joiner. * * @param wsrep provider handle * @param state_id state ID * @param rcode 0 or negative error code of the operation. */ wsrep_status_t (*sst_sent)(wsrep_t* wsrep, const wsrep_gtid_t* state_id, int rcode); /*! * @brief Signals to wsrep provider that new state snapshot has been received. * May deadlock if called from sst_prepare_cb. * * @param wsrep provider handle * @param state_id state ID * @param state initial state provided by SST donor * @param state_len length of state buffer * @param rcode 0 or negative error code of the operation. */ wsrep_status_t (*sst_received)(wsrep_t* wsrep, const wsrep_gtid_t* state_id, const void* state, size_t state_len, int rcode); /*! * @brief Generate request for consistent snapshot. * * If successfull, this call will generate internally SST request * which in turn triggers calling SST donate callback on the nodes * specified in donor_spec. If donor_spec is null, callback is * called only locally. This call will block until sst_sent is called * from callback. * * @param wsrep provider handle * @param msg context message for SST donate callback * @param msg_len length of context message * @param donor_spec list of snapshot donors */ wsrep_status_t (*snapshot)(wsrep_t* wsrep, const void* msg, size_t msg_len, const char* donor_spec); /*! * @brief Returns an array fo status variables. * Array is terminated by Null variable name. * * @param wsrep provider handle * @return array of struct wsrep_status_var. */ struct wsrep_stats_var* (*stats_get) (wsrep_t* wsrep); /*! * @brief Release resources that might be associated with the array. * * @param wsrep provider handle. * @param var_array array returned by stats_get(). */ void (*stats_free) (wsrep_t* wsrep, struct wsrep_stats_var* var_array); /*! * @brief Reset some stats variables to inital value, provider-dependent. * * @param wsrep provider handle. */ void (*stats_reset) (wsrep_t* wsrep); /*! * @brief Pauses writeset applying/committing. * * @return global sequence number of the paused state or negative error code. */ wsrep_seqno_t (*pause) (wsrep_t* wsrep); /*! * @brief Resumes writeset applying/committing. */ wsrep_status_t (*resume) (wsrep_t* wsrep); /*! * @brief Desynchronize from cluster * * Effectively turns off flow control for this node, allowing it * to fall behind the cluster. */ wsrep_status_t (*desync) (wsrep_t* wsrep); /*! * @brief Request to resynchronize with cluster. * * Effectively turns on flow control. Asynchronous - actual synchronization * event to be deliverred via sync_cb. */ wsrep_status_t (*resync) (wsrep_t* wsrep); /*! * @brief Acquire global named lock * * @param wsrep wsrep provider handle * @param name lock name * @param shared shared or exclusive lock * @param owner 64-bit owner ID * @param tout timeout in nanoseconds. * 0 - return immediately, -1 wait forever. * @return wsrep status or negative error code * @retval -EDEADLK lock was already acquired by this thread * @retval -EBUSY lock was busy */ wsrep_status_t (*lock) (wsrep_t* wsrep, const char* name, wsrep_bool_t shared, uint64_t owner, int64_t tout); /*! * @brief Release global named lock * * @param wsrep wsrep provider handle * @param name lock name * @param owner 64-bit owner ID * @return wsrep status or negative error code * @retval -EPERM lock does not belong to this owner */ wsrep_status_t (*unlock) (wsrep_t* wsrep, const char* name, uint64_t owner); /*! * @brief Check if global named lock is locked * * @param wsrep wsrep provider handle * @param name lock name * @param owner if not NULL will contain 64-bit owner ID * @param node if not NULL will contain owner's node UUID * @return true if lock is locked */ wsrep_bool_t (*is_locked) (wsrep_t* wsrep, const char* name, uint64_t* conn, wsrep_uuid_t* node); /*! * wsrep provider name */ const char* provider_name; /*! * wsrep provider version */ const char* provider_version; /*! * wsrep provider vendor name */ const char* provider_vendor; /*! * @brief Frees allocated resources before unloading the library. * @param wsrep provider handle */ void (*free)(wsrep_t* wsrep); void *dlh; //!< reserved for future use void *ctx; //!< reserved for implemetation private context }; /*! * * @brief Loads wsrep library * * @param spec path to wsrep library. If NULL or WSREP_NONE initialises dummy * pass-through implementation. * @param hptr wsrep handle * @param log_cb callback to handle loader messages. Otherwise writes to stderr. * * @return zero on success, errno on failure */ int wsrep_load(const char* spec, wsrep_t** hptr, wsrep_log_cb_t log_cb); /*! * @brief Unloads wsrep library and frees associated resources * * @param hptr wsrep handler pointer */ void wsrep_unload(wsrep_t* hptr); #ifdef __cplusplus } #endif #endif /* WSREP_H */ percona-xtradb-cluster-galera/galera/src/wsrep_params.cpp0000644000000000000000000000311512247075736024103 0ustar rootroot00000000000000// // Copyright (C) 2010 Codership Oy // #include "wsrep_params.hpp" void wsrep_set_params (galera::Replicator& repl, const char* params) { if (!params) return; gu::Config::param_map_t pm; gu::Config::parse (pm, params); for (gu::Config::param_map_t::const_iterator i = pm.begin(); i != pm.end(); ++i) { try { if (i->first == "debug") { bool val(gu::from_string(i->second)); if (val == true) { log_info << "enabling debug logging"; gu_conf_debug_on(); } else { log_info << "disabling debug logging"; gu_conf_debug_off(); } } else { log_debug << "Setting param '" << i->first << "' = '" << i->second << "'"; repl.param_set(i->first, i->second); } } catch (gu::NotFound&) { log_warn << "Unknown parameter '" << i->first << "'"; gu_throw_error(EINVAL) << "Unknown parameter' " << i->first << "'"; } catch (gu::Exception& e) { log_warn << "Setting parameter '" << i->first << "' to '" << i->second << "' failed: " << e.what(); throw; } } } char* wsrep_get_params(const galera::Replicator& repl) { std::ostringstream os; os << repl.params(); return strdup(os.str().c_str()); } percona-xtradb-cluster-galera/galera/src/wsrep_params.hpp0000644000000000000000000000053412247075736024112 0ustar rootroot00000000000000// // Copyright (C) 2010 Codership Oy // #ifndef WSREP_PARAMS_HPP #define WSREP_PARAMS_HPP #include "wsrep_api.h" #include "galerautils.hpp" #include "replicator.hpp" void wsrep_set_params (galera::Replicator& repl, const char* params); char* wsrep_get_params(const galera::Replicator& repl); #endif /* WSREP_PARAMS_HPP */ percona-xtradb-cluster-galera/galera/src/wsrep_provider.cpp0000644000000000000000000005516712247075736024470 0ustar rootroot00000000000000// // Copyright (C) 2010-2012 Codership Oy // #if defined(GALERA_MULTIMASTER) #include "replicator_smm.hpp" #define REPL_CLASS galera::ReplicatorSMM #else #error "Not implemented" #endif #include "wsrep_params.hpp" #include using galera::Key; using galera::WriteSet; using galera::TrxHandle; using galera::TrxHandleLock; extern "C" wsrep_status_t galera_init(wsrep_t* gh, const struct wsrep_init_args* args) { assert(gh != 0); try { gh->ctx = new REPL_CLASS (args); wsrep_set_params(*reinterpret_cast(gh->ctx), args->options); return WSREP_OK; } catch (gu::Exception& e) { log_error << e.what(); } catch (std::exception& e) { log_error << e.what(); } #ifdef NDEBUG catch (...) { log_fatal << "non-standard exception"; } #endif return WSREP_NODE_FAIL; } extern "C" uint64_t galera_capabilities(wsrep_t* gh) { return (WSREP_CAP_MULTI_MASTER | WSREP_CAP_CERTIFICATION | WSREP_CAP_PARALLEL_APPLYING | WSREP_CAP_TRX_REPLAY | WSREP_CAP_ISOLATION | WSREP_CAP_PAUSE | WSREP_CAP_CAUSAL_READS); } extern "C" void galera_tear_down(wsrep_t* gh) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); if (repl != 0) { delete repl; gh->ctx = 0; } } extern "C" wsrep_status_t galera_parameters_set (wsrep_t* gh, const char* params) { assert(gh != 0); // cppcheck-suppress nullPointer assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); // cppcheck-suppress nullPointer if (gh) { try { wsrep_set_params (*repl, params); return WSREP_OK; } catch (std::exception& e) { log_debug << e.what(); // better logged in wsrep_set_params } } else { log_error << "Attempt to set parameter(s) on uninitialized replicator."; } return WSREP_NODE_FAIL; } extern "C" char* galera_parameters_get (wsrep_t* gh) { assert(gh != 0); assert(gh->ctx != 0); try { REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); return wsrep_get_params(*repl); } catch (std::exception& e) { log_error << e.what(); return 0; } catch (...) { log_fatal << "non-standard exception"; return 0; } } extern "C" wsrep_status_t galera_connect (wsrep_t* gh, const char* cluster_name, const char* cluster_url, const char* state_donor, wsrep_bool_t bootstrap) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); try { return repl->connect(cluster_name, cluster_url, state_donor ? state_donor : "", bootstrap); } catch (std::exception& e) { log_error << e.what(); return WSREP_NODE_FAIL; } catch (...) { log_fatal << "non-standard exception"; return WSREP_FATAL; } } extern "C" wsrep_status_t galera_disconnect(wsrep_t *gh) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); try { return repl->close(); } catch (std::exception& e) { log_error << e.what(); return WSREP_NODE_FAIL; } catch (...) { log_fatal << "non-standard exception"; return WSREP_FATAL; } } extern "C" wsrep_status_t galera_recv(wsrep_t *gh, void *recv_ctx) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); #ifdef NDEBUG try { #endif /* NDEBUG */ return repl->async_recv(recv_ctx); #ifdef NDEBUG } catch (gu::Exception& e) { log_error << e.what(); switch (e.get_errno()) { case ENOTRECOVERABLE: return WSREP_FATAL; default: return WSREP_NODE_FAIL; } } catch (std::exception& e) { log_error << e.what(); } catch (...) { log_fatal << "non-standard exception"; } #endif // NDEBUG return WSREP_FATAL; } extern "C" wsrep_status_t galera_replay_trx(wsrep_t* gh, wsrep_ws_handle_t* trx_handle, void* recv_ctx) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); TrxHandle* trx(repl->local_trx(trx_handle, false)); assert(trx != 0); wsrep_status_t retval; try { TrxHandleLock lock(*trx); retval = repl->replay_trx(trx, recv_ctx); } catch (std::exception& e) { log_warn << "failed to replay trx: " << *trx; log_warn << e.what(); retval = WSREP_CONN_FAIL; } catch (...) { log_fatal << "non-standard exception"; retval = WSREP_FATAL; } repl->unref_local_trx(trx); return retval; } extern "C" wsrep_status_t galera_abort_pre_commit(wsrep_t* gh, wsrep_seqno_t bf_seqno, wsrep_trx_id_t victim_trx) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); wsrep_status_t retval; TrxHandle* trx(repl->local_trx(victim_trx)); if (!trx) return WSREP_OK; try { TrxHandleLock lock(*trx); repl->abort_trx(trx); retval = WSREP_OK; } catch (std::exception& e) { log_error << e.what(); retval = WSREP_NODE_FAIL; } catch (...) { log_fatal << "non-standard exception"; retval = WSREP_FATAL; } repl->unref_local_trx(trx); return retval; } extern "C" wsrep_status_t galera_post_commit (wsrep_t* gh, wsrep_ws_handle_t* trx_handle) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); TrxHandle* trx(repl->local_trx(trx_handle, false)); if (trx == 0) { log_debug << "trx " << trx_handle->trx_id << " not found"; return WSREP_OK; } wsrep_status_t retval; try { TrxHandleLock lock(*trx); retval = repl->post_commit(trx); } catch (std::exception& e) { log_error << e.what(); retval = WSREP_NODE_FAIL; } catch (...) { log_fatal << "non-standard exception"; retval = WSREP_FATAL; } repl->unref_local_trx(trx); repl->discard_local_trx(trx->trx_id()); trx_handle->opaque = 0; return retval; } extern "C" wsrep_status_t galera_post_rollback(wsrep_t* gh, wsrep_ws_handle_t* trx_handle) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); TrxHandle* trx(repl->local_trx(trx_handle, false)); if (trx == 0) { log_debug << "trx " << trx_handle->trx_id << " not found"; return WSREP_OK; } wsrep_status_t retval; try { TrxHandleLock lock(*trx); retval = repl->post_rollback(trx); } catch (std::exception& e) { log_error << e.what(); retval = WSREP_NODE_FAIL; } catch (...) { log_fatal << "non-standard exception"; retval = WSREP_FATAL; } repl->unref_local_trx(trx); repl->discard_local_trx(trx->trx_id()); trx_handle->opaque = 0; return retval; } static inline void append_data_array (TrxHandle* const trx, const struct wsrep_buf* const data, size_t const count, wsrep_data_type_t const type, bool const copy) { for (size_t i(0); i < count; ++i) { trx->append_data(data[i].ptr, data[i].len); } } extern "C" wsrep_status_t galera_pre_commit(wsrep_t* const gh, wsrep_conn_id_t const conn_id, wsrep_ws_handle_t* const trx_handle, uint32_t const flags, wsrep_trx_meta_t* const meta) { assert(gh != 0); assert(gh->ctx != 0); if (meta != 0) { meta->gtid = WSREP_GTID_UNDEFINED; meta->depends_on = WSREP_SEQNO_UNDEFINED; } REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); TrxHandle* trx(repl->local_trx(trx_handle, /*rbr_data != 0*/ false)); if (trx == 0) { // no data to replicate return WSREP_OK; } wsrep_status_t retval; try { TrxHandleLock lock(*trx); trx->set_conn_id(conn_id); // /* rbr_data should clearly persist over pre_commit() call */ // append_data_array (trx, rbr_data, rbr_data_len, false, false); trx->set_flags(TrxHandle::wsrep_flags_to_trx_flags(flags)); retval = repl->replicate(trx, meta); assert((!(retval == WSREP_OK || retval == WSREP_BF_ABORT) || trx->global_seqno() > 0)); if (retval == WSREP_OK) { retval = repl->pre_commit(trx, meta); } assert(retval == WSREP_OK || retval == WSREP_TRX_FAIL || retval == WSREP_BF_ABORT); } catch (std::exception& e) { log_error << e.what(); retval = WSREP_NODE_FAIL; } catch (...) { log_fatal << "non-standard exception"; retval = WSREP_FATAL; } repl->unref_local_trx(trx); return retval; } extern "C" wsrep_status_t galera_append_key(wsrep_t* const gh, wsrep_ws_handle_t* const trx_handle, const wsrep_key_t* const keys, size_t const keys_num, wsrep_key_type_t const key_type, wsrep_bool_t const copy) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); TrxHandle* trx(repl->local_trx(trx_handle, true)); assert(trx != 0); wsrep_status_t retval; try { TrxHandleLock lock(*trx); for (size_t i(0); i < keys_num; ++i) { galera::Key k(repl->trx_proto_ver(), keys[i].key_parts, keys[i].key_parts_num, (WSREP_KEY_SHARED == key_type ? galera::Key::F_SHARED : 0)); trx->append_key(k); } retval = WSREP_OK; } catch (std::exception& e) { log_warn << e.what(); retval = WSREP_CONN_FAIL; } catch (...) { log_fatal << "non-standard exception"; retval = WSREP_FATAL; } repl->unref_local_trx(trx); return retval; } extern "C" wsrep_status_t galera_append_data(wsrep_t* const wsrep, wsrep_ws_handle_t* const trx_handle, const struct wsrep_buf* const data, size_t const count, wsrep_data_type_t const type, wsrep_bool_t const copy) { assert(wsrep != 0); assert(wsrep->ctx != 0); assert(data != NULL); assert(count > 0); if (data == NULL) { // no data to replicate return WSREP_OK; } REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(wsrep->ctx)); TrxHandle* trx(repl->local_trx(trx_handle, true)); assert(trx != 0); wsrep_status_t retval; try { TrxHandleLock lock(*trx); if (WSREP_DATA_ORDERED == type) append_data_array(trx, data, count, type, copy); retval = WSREP_OK; } catch (std::exception& e) { log_warn << e.what(); retval = WSREP_CONN_FAIL; } catch (...) { log_fatal << "non-standard exception"; retval = WSREP_FATAL; } repl->unref_local_trx(trx); return retval; } extern "C" wsrep_status_t galera_causal_read(wsrep_t* const wsrep, wsrep_gtid_t* const gtid) { assert(wsrep != 0); assert(wsrep->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(wsrep->ctx)); wsrep_status_t retval; try { retval = repl->causal_read(gtid); } catch (std::exception& e) { log_warn << e.what(); retval = WSREP_CONN_FAIL; } catch (...) { log_fatal << "non-standard exception"; retval = WSREP_FATAL; } return retval; } extern "C" wsrep_status_t galera_free_connection(wsrep_t* const gh, wsrep_conn_id_t const conn_id) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); try { repl->discard_local_conn(conn_id); return WSREP_OK; } catch (std::exception& e) { log_warn << e.what(); return WSREP_CONN_FAIL; } catch (...) { log_fatal << "non-standard exception"; return WSREP_FATAL; } } extern "C" wsrep_status_t galera_to_execute_start(wsrep_t* const gh, wsrep_conn_id_t const conn_id, const wsrep_key_t* const keys, size_t const keys_num, const struct wsrep_buf* const data, size_t const count, wsrep_trx_meta_t* const meta) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); TrxHandle* trx(repl->local_conn_trx(conn_id, true)); assert(trx != 0); wsrep_status_t retval; try { TrxHandleLock lock(*trx); for (size_t i(0); i < keys_num; ++i) { trx->append_key(Key(repl->trx_proto_ver(), keys[i].key_parts, keys[i].key_parts_num, 0)); } append_data_array(trx, data, count, WSREP_DATA_ORDERED, false); trx->set_flags(TrxHandle::wsrep_flags_to_trx_flags( WSREP_FLAG_COMMIT | WSREP_FLAG_ISOLATION)); retval = repl->replicate(trx, meta); assert((retval == WSREP_OK && trx->global_seqno() > 0) || (retval != WSREP_OK && trx->global_seqno() < 0)); if (retval == WSREP_OK) { retval = repl->to_isolation_begin(trx, meta); } } catch (std::exception& e) { log_warn << e.what(); retval = WSREP_CONN_FAIL; } catch (...) { log_fatal << "non-standard exception"; retval = WSREP_FATAL; } if (retval != WSREP_OK) // galera_to_execute_end() won't be called { repl->discard_local_conn_trx(conn_id); // trx is not needed anymore if (trx->global_seqno() < 0) // no seqno -> no index -> no automatic purging { trx->unref(); // implicit destructor } } return retval; } extern "C" wsrep_status_t galera_to_execute_end(wsrep_t* const gh, wsrep_conn_id_t const conn_id) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); wsrep_status_t retval; TrxHandle* trx(repl->local_conn_trx(conn_id, false)); try { TrxHandleLock lock(*trx); repl->to_isolation_end(trx); repl->discard_local_conn_trx(conn_id); return WSREP_OK; // trx will be unreferenced (destructed) during purge } catch (std::exception& e) { log_warn << e.what(); return WSREP_CONN_FAIL; } catch (...) { log_fatal << "non-standard exception"; return WSREP_FATAL; } return retval; } extern "C" wsrep_status_t galera_preordered_collect (wsrep_t* const gh, wsrep_po_handle_t* const handle, const struct wsrep_buf* const data, size_t const count, wsrep_bool_t const copy) { assert(gh != 0); assert(gh->ctx != 0); assert(data != 0); assert(count > 0); return WSREP_NOT_IMPLEMENTED; } extern "C" wsrep_status_t galera_preordered_commit (wsrep_t* const gh, wsrep_po_handle_t* const handle, const wsrep_uuid_t* const source_id, uint32_t const flags, int const pa_range, wsrep_bool_t const commit) { assert(gh != 0); assert(gh->ctx != 0); assert(source_id != 0 || false == commit); assert(pa_range >= 0 || false == commit); return WSREP_NOT_IMPLEMENTED; } extern "C" wsrep_status_t galera_sst_sent (wsrep_t* const gh, const wsrep_gtid_t* const state_id, int const rcode) { assert(gh != 0); assert(gh->ctx != 0); assert(state_id != 0); assert(rcode <= 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); return repl->sst_sent(*state_id, rcode); } extern "C" wsrep_status_t galera_sst_received (wsrep_t* const gh, const wsrep_gtid_t* const state_id, const void* const state, size_t const state_len, int const rcode) { assert(gh != 0); assert(gh->ctx != 0); assert(state_id != 0); assert(rcode <= 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); if (rcode < 0) { assert(state_id->seqno == WSREP_SEQNO_UNDEFINED); } return repl->sst_received(*state_id, state, state_len, rcode); } extern "C" wsrep_status_t galera_snapshot(wsrep_t* wsrep, const void* msg, size_t msg_len, const char* donor_spec) { return WSREP_NOT_IMPLEMENTED; } extern "C" struct wsrep_stats_var* galera_stats_get (wsrep_t* gh) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); return const_cast(repl->stats_get()); } extern "C" void galera_stats_free (wsrep_t* gh, struct wsrep_stats_var* s) { // REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); REPL_CLASS::stats_free(s); } extern "C" void galera_stats_reset (wsrep_t* gh) { return; // ignore } extern "C" wsrep_seqno_t galera_pause (wsrep_t* gh) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); try { return repl->pause(); } catch (gu::Exception& e) { log_error << e.what(); return -e.get_errno(); } } extern "C" wsrep_status_t galera_resume (wsrep_t* gh) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); try { repl->resume(); return WSREP_OK; } catch (gu::Exception& e) { log_error << e.what(); return WSREP_NODE_FAIL; } } extern "C" wsrep_status_t galera_desync (wsrep_t* gh) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); try { repl->desync(); return WSREP_OK; } catch (gu::Exception& e) { log_error << e.what(); return WSREP_TRX_FAIL; } } extern "C" wsrep_status_t galera_resync (wsrep_t* gh) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); try { repl->resync(); return WSREP_OK; } catch (gu::Exception& e) { log_error << e.what(); return WSREP_NODE_FAIL; } } extern "C" wsrep_status_t galera_lock (wsrep_t* gh, const char* name, wsrep_bool_t shared, uint64_t owner, int64_t timeout) { assert(gh != 0); assert(gh->ctx != 0); return WSREP_NOT_IMPLEMENTED; } extern "C" wsrep_status_t galera_unlock (wsrep_t* gh, const char* name, uint64_t owner) { assert(gh != 0); assert(gh->ctx != 0); return WSREP_OK; } extern "C" bool galera_is_locked (wsrep_t* gh, const char* name, uint64_t* owner, wsrep_uuid_t* node) { assert(gh != 0); assert(gh->ctx != 0); return false; } static wsrep_t galera_str = { WSREP_INTERFACE_VERSION, &galera_init, &galera_capabilities, &galera_parameters_set, &galera_parameters_get, &galera_connect, &galera_disconnect, &galera_recv, &galera_pre_commit, &galera_post_commit, &galera_post_rollback, &galera_replay_trx, &galera_abort_pre_commit, &galera_append_key, &galera_append_data, &galera_causal_read, &galera_free_connection, &galera_to_execute_start, &galera_to_execute_end, &galera_preordered_collect, &galera_preordered_commit, &galera_sst_sent, &galera_sst_received, &galera_snapshot, &galera_stats_get, &galera_stats_free, &galera_stats_reset, &galera_pause, &galera_resume, &galera_desync, &galera_resync, &galera_lock, &galera_unlock, &galera_is_locked, "Galera", GALERA_VER"(r"GALERA_REV")", "Codership Oy ", &galera_tear_down, NULL, NULL }; /* Prototype to make compiler happy */ extern "C" int wsrep_loader(wsrep_t *hptr); extern "C" int wsrep_loader(wsrep_t *hptr) { if (!hptr) return EINVAL; try { *hptr = galera_str; } catch (...) { return ENOTRECOVERABLE; } return WSREP_OK; } percona-xtradb-cluster-galera/galera/tests/SConscript0000644000000000000000000000161412247075736023263 0ustar rootroot00000000000000 Import('check_env') env = check_env.Clone() env.Append(LIBS=File('#/gcache/src/libgcache.a')) env.Append(LIBS=File('#/galera/src/libgalera++.a')) env.Append(LIBS=File('#/galerautils/src/libgalerautils++.a')) env.Append(LIBS=File('#/galerautils/src/libgalerautils.a')) #env.Append(LIBS = ['pthread', # 'rt']) galera_check = env.Program(target='galera_check', source=Split(''' galera_check.cpp write_set_check.cpp trx_handle_check.cpp service_thd_check.cpp ist_check.cpp saved_state_check.cpp ''')) stamp = "galera_check.passed" env.Test(stamp, galera_check) env.Alias("test", stamp) Clean(galera_check, ['#/galera_check.log', 'ist_check.cache']) percona-xtradb-cluster-galera/galera/tests/galera_check.cpp0000644000000000000000000000256612247075736024354 0ustar rootroot00000000000000/* * Copyright (C) 2012 Codership Oy */ #include #include #include #include /* * Suite descriptions: forward-declare and add to array */ typedef Suite* (*suite_creator_t) (void); extern Suite* write_set_suite(); extern Suite* trx_handle_suite(); extern Suite* service_thd_suite(); extern Suite* ist_suite(); extern Suite* saved_state_suite(); static suite_creator_t suites[] = { write_set_suite, trx_handle_suite, service_thd_suite, ist_suite, saved_state_suite, 0 }; extern "C" { #include } #define LOG_FILE "galera_check.log" int main(int argc, char* argv[]) { bool no_fork = (argc >= 2 && std::string(argv[1]) == "nofork"); FILE* log_file = 0; if (!no_fork) { log_file = fopen (LOG_FILE, "w"); if (!log_file) return EXIT_FAILURE; gu_conf_set_log_file (log_file); } gu_conf_debug_on(); int failed = 0; for (int i = 0; suites[i] != 0; ++i) { SRunner* sr = srunner_create(suites[i]()); if (no_fork) srunner_set_fork_status(sr, CK_NOFORK); srunner_run_all(sr, CK_NORMAL); failed += srunner_ntests_failed(sr); srunner_free(sr); } if (log_file != 0) fclose(log_file); printf ("Total tests failed: %d\n", failed); return failed == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } percona-xtradb-cluster-galera/galera/tests/ist_check.cpp0000644000000000000000000001750112247075736023713 0ustar rootroot00000000000000// // Copyright (C) 2011-2012 Codership Oy // #include "ist.hpp" #include "ist_proto.hpp" #include "trx_handle.hpp" #include "uuid.hpp" #include "monitor.hpp" #include "GCache.hpp" #include "gu_arch.h" #include // Message tests START_TEST(test_ist_message) { using namespace galera::ist; Message m3(3, Message::T_HANDSHAKE, 0x2, 3, 1001); #if 0 /* This is a check for the old (broken) format */ #if GU_WORDSIZE == 32 fail_unless(serial_size(m3) == 20, "serial size %zu != 20", serial_size(m3)); #elif GU_WORDSIZE == 64 fail_unless(serial_size(m3) == 24, "serial size %zu != 24", serial_size(m3)); #endif #endif /* 0 */ gu::Buffer buf(serial_size(m3)); serialize(m3, &buf[0], buf.size(), 0); Message mu3(3); unserialize(&buf[0], buf.size(), 0, mu3); fail_unless(mu3.version() == 3); fail_unless(mu3.type() == Message::T_HANDSHAKE); fail_unless(mu3.flags() == 0x2); fail_unless(mu3.ctrl() == 3); fail_unless(mu3.len() == 1001); Message m4(4, Message::T_HANDSHAKE, 0x2, 3, 1001); fail_unless(serial_size(m4) == 12); buf.clear(); buf.resize(serial_size(m4)); serialize(m4, &buf[0], buf.size(), 0); Message mu4(4); unserialize(&buf[0], buf.size(), 0, mu4); fail_unless(mu4.version() == 4); fail_unless(mu4.type() == Message::T_HANDSHAKE); fail_unless(mu4.flags() == 0x2); fail_unless(mu4.ctrl() == 3); fail_unless(mu4.len() == 1001); } END_TEST // IST tests static pthread_barrier_t start_barrier; class TestOrder { public: TestOrder(galera::TrxHandle& trx) : trx_(trx) { } void lock() { } void unlock() { } wsrep_seqno_t seqno() const { return trx_.global_seqno(); } bool condition(wsrep_seqno_t last_entered, wsrep_seqno_t last_left) const { return (last_left >= trx_.depends_seqno()); } private: galera::TrxHandle& trx_; }; struct sender_args { gcache::GCache& gcache_; const std::string& peer_; wsrep_seqno_t first_; wsrep_seqno_t last_; int version_; sender_args(gcache::GCache& gcache, const std::string& peer, wsrep_seqno_t first, wsrep_seqno_t last, int version) : gcache_(gcache), peer_ (peer), first_ (first), last_ (last), version_(version) { } }; struct receiver_args { std::string listen_addr_; wsrep_seqno_t first_; wsrep_seqno_t last_; size_t n_receivers_; int version_; receiver_args(const std::string listen_addr, wsrep_seqno_t first, wsrep_seqno_t last, size_t n_receivers, int version) : listen_addr_(listen_addr), first_(first), last_(last), n_receivers_(n_receivers), version_(version) { } }; struct trx_thread_args { galera::ist::Receiver& receiver_; galera::Monitor monitor_; trx_thread_args(galera::ist::Receiver& receiver) : receiver_(receiver), monitor_() { } }; extern "C" void* sender_thd(void* arg) { const sender_args* sargs(reinterpret_cast(arg)); gu::Config conf; pthread_barrier_wait(&start_barrier); galera::ist::Sender sender(conf, sargs->gcache_, sargs->peer_, sargs->version_); sender.send(sargs->first_, sargs->last_); return 0; } extern "C" void* trx_thread(void* arg) { trx_thread_args* targs(reinterpret_cast(arg)); pthread_barrier_wait(&start_barrier); targs->receiver_.ready(); while (true) { galera::TrxHandle* trx(0); int err; if ((err = targs->receiver_.recv(&trx)) != 0) { assert(trx == 0); log_info << "terminated with " << err; return 0; } TestOrder to(*trx); targs->monitor_.enter(to); targs->monitor_.leave(to); trx->unref(); } return 0; } extern "C" void* receiver_thd(void* arg) { receiver_args* rargs(reinterpret_cast(arg)); gu::Config conf; conf.set(galera::ist::Receiver::RECV_ADDR, rargs->listen_addr_); galera::ist::Receiver receiver(conf, 0); rargs->listen_addr_ = receiver.prepare(rargs->first_, rargs->last_, rargs->version_); std::vector threads(rargs->n_receivers_); trx_thread_args trx_thd_args(receiver); for (size_t i(0); i < threads.size(); ++i) { log_info << "starting trx thread " << i; pthread_create(&threads[0] + i, 0, &trx_thread, &trx_thd_args); } trx_thd_args.monitor_.set_initial_position(rargs->first_ - 1); pthread_barrier_wait(&start_barrier); trx_thd_args.monitor_.wait(rargs->last_); for (size_t i(0); i < threads.size(); ++i) { log_info << "joining trx thread " << i; pthread_join(threads[i], 0); } receiver.finished(); return 0; } static int select_trx_version(int protocol_version) { // see protocol version table in replicator_smm.hpp switch (protocol_version) { case 1: case 2: return 1; case 3: case 4: return 2; } fail("unknown protocol version %i", protocol_version); return -1; } static void test_ist_common(int version) { using galera::TrxHandle; using galera::Key; int trx_version(select_trx_version(version)); gu::Config conf; std::string gcache_file("ist_check.cache"); conf.set("gcache.name", gcache_file); std::string dir("."); std::string receiver_addr("tcp://127.0.0.1:0"); wsrep_uuid_t uuid; gu_uuid_generate(reinterpret_cast(&uuid), 0, 0); gcache::GCache* gcache = new gcache::GCache(conf, dir); // populate gcache for (size_t i(1); i <= 10; ++i) { TrxHandle* trx(new TrxHandle(trx_version, uuid, 1234, 5678, false)); const wsrep_buf_t key[2] = { {"key1", 4}, {"key2", 4} }; trx->append_key(Key(trx_version, key, 2, 0)); trx->append_data("bar", 3); size_t trx_size(serial_size(*trx)); gu::byte_t* ptr(reinterpret_cast(gcache->malloc(trx_size))); serialize(*trx, ptr, trx_size, 0); gcache->seqno_assign(ptr, i, i - 1, false); trx->unref(); } receiver_args rargs(receiver_addr, 1, 10, 1, version); sender_args sargs(*gcache, rargs.listen_addr_, 1, 10, version); pthread_barrier_init(&start_barrier, 0, 1 + 1 + rargs.n_receivers_); pthread_t sender_thread, receiver_thread; pthread_create(&sender_thread, 0, &sender_thd, &sargs); pthread_create(&receiver_thread, 0, &receiver_thd, &rargs); pthread_join(sender_thread, 0); pthread_join(receiver_thread, 0); mark_point(); delete gcache; mark_point(); unlink(gcache_file.c_str()); } START_TEST(test_ist_v1) { test_ist_common(1); } END_TEST START_TEST(test_ist_v2) { test_ist_common(2); } END_TEST START_TEST(test_ist_v3) { test_ist_common(3); } END_TEST START_TEST(test_ist_v4) { test_ist_common(4); } END_TEST Suite* ist_suite() { Suite* s = suite_create("ist"); TCase* tc; tc = tcase_create("test_ist_message"); tcase_add_test(tc, test_ist_message); suite_add_tcase(s, tc); tc = tcase_create("test_ist_v1"); tcase_set_timeout(tc, 60); tcase_add_test(tc, test_ist_v1); suite_add_tcase(s, tc); tc = tcase_create("test_ist_v2"); tcase_set_timeout(tc, 60); tcase_add_test(tc, test_ist_v2); suite_add_tcase(s, tc); tc = tcase_create("test_ist_v3"); tcase_set_timeout(tc, 60); tcase_add_test(tc, test_ist_v3); suite_add_tcase(s, tc); tc = tcase_create("test_ist_v4"); tcase_set_timeout(tc, 60); tcase_add_test(tc, test_ist_v4); suite_add_tcase(s, tc); return s; } percona-xtradb-cluster-galera/galera/tests/saved_state_check.cpp0000644000000000000000000001071212247075736025413 0ustar rootroot00000000000000/* * Copyright (C) 2012 Codership Oy */ #include "../src/saved_state.hpp" #include "../src/uuid.hpp" #include #include #include #define __STDC_FORMAT_MACROS #include static volatile bool stop(false); using namespace galera; static void* thread_routine (void* arg) { SavedState* st(reinterpret_cast(arg)); do { st->mark_unsafe(); st->mark_safe(); } while (!stop); return NULL; } static const int max_threads(16); static pthread_t threads[max_threads]; static void start_threads(void* arg) { stop = false; for (int ret = 0; ret < max_threads; ++ret) { pthread_t t; int err = pthread_create (&t, NULL, thread_routine, arg); fail_if (err, "Failed to start thread %d: %d (%s)", ret, err, strerror(err)); threads[ret] = t; } } static void stop_threads() { stop = true; for (int t = 0; t < max_threads; ++t) { pthread_join(threads[t], NULL); } } static const char* fname("grastate.dat"); START_TEST(test_basic) { unlink (fname); wsrep_uuid_t uuid; wsrep_seqno_t seqno; { SavedState st(fname); st.get(uuid, seqno); fail_if (uuid != WSREP_UUID_UNDEFINED); fail_if (seqno != WSREP_SEQNO_UNDEFINED); string2uuid("b2c01654-8dfe-11e1-0800-a834d641cfb5", uuid); seqno = 2345234LL; st.set(uuid, seqno); } { SavedState st(fname); wsrep_uuid_t u; wsrep_seqno_t s; st.get(u, s); fail_if (u != uuid); fail_if (s != seqno); } } END_TEST #define TEST_USLEEP 2500 // 2.5ms START_TEST(test_unsafe) { SavedState st(fname); wsrep_uuid_t uuid; wsrep_seqno_t seqno; st.get(uuid, seqno); fail_if (uuid == WSREP_UUID_UNDEFINED); fail_if (seqno == WSREP_SEQNO_UNDEFINED); st.set(uuid, WSREP_SEQNO_UNDEFINED); for (int i = 0; i < 100; ++i) { start_threads(&st); mark_point(); usleep (TEST_USLEEP); st.set(uuid, i); // make sure that state is not lost if set concurrently mark_point(); usleep (TEST_USLEEP); stop_threads(); mark_point(); st.get(uuid, seqno); fail_if (uuid == WSREP_UUID_UNDEFINED); fail_if (seqno != i); } long marks, locks, writes; st.stats(marks, locks, writes); log_info << "Total marks: " << marks << ", total writes: " << writes << ", total locks: " << locks << "\nlocks ratio: " << (double(locks)/marks) << "\nwrites ratio: " << (double(writes)/locks); } END_TEST START_TEST(test_corrupt) { wsrep_uuid_t uuid; wsrep_seqno_t seqno; { SavedState st(fname); st.get(uuid, seqno); fail_if (uuid == WSREP_UUID_UNDEFINED); fail_if (seqno == WSREP_SEQNO_UNDEFINED); st.set(uuid, WSREP_SEQNO_UNDEFINED); } long marks(0), locks(0), writes(0); for (int i = 0; i < 100; ++i) { SavedState st(fname); // explicitly overwrite corruption mark. st.set (uuid, seqno); start_threads(&st); mark_point(); usleep (TEST_USLEEP); st.mark_corrupt(); st.set (uuid, seqno); // make sure that corrupt stays usleep (TEST_USLEEP); mark_point(); stop_threads(); mark_point(); wsrep_uuid_t u; wsrep_seqno_t s; st.get(u, s); // make sure that mark_corrupt() stays fail_if (u != WSREP_UUID_UNDEFINED); fail_if (s != WSREP_SEQNO_UNDEFINED); long m, l, w; st.stats(m, l, w); marks += m; locks += l; writes += w; } log_info << "Total marks: " << marks << ", total locks: " << locks << ", total writes: " << writes << "\nlocks ratio: " << (double(locks)/marks) << "\nwrites ratio: " << (double(writes)/locks); unlink (fname); } END_TEST #define WAIT_FOR(cond) \ { int count = 1000; while (--count && !(cond)) { usleep (TEST_USLEEP); }} Suite* saved_state_suite() { Suite* s = suite_create ("saved_state"); TCase* tc; tc = tcase_create ("saved_state"); tcase_add_test (tc, test_basic); tcase_add_test (tc, test_unsafe); tcase_add_test (tc, test_corrupt); tcase_set_timeout(tc, 120); suite_add_tcase (s, tc); return s; } percona-xtradb-cluster-galera/galera/tests/service_thd_check.cpp0000644000000000000000000000353512247075736025415 0ustar rootroot00000000000000/* * Copyright (C) 2010 Codership Oy */ #include "../src/galera_service_thd.hpp" #include #include #define __STDC_FORMAT_MACROS #include using namespace galera; START_TEST(service_thd1) { DummyGcs conn; ServiceThd* thd = new ServiceThd(conn); fail_if (thd == 0); delete thd; } END_TEST #define TEST_USLEEP 1000 // 1ms #define WAIT_FOR(cond) \ { int count = 1000; while (--count && !(cond)) { usleep (TEST_USLEEP); }} START_TEST(service_thd2) { DummyGcs conn; ServiceThd* thd = new ServiceThd(conn); fail_if (thd == 0); conn.set_last_applied(0); gcs_seqno_t seqno = 1; thd->report_last_committed (seqno); WAIT_FOR(conn.last_applied() == seqno); fail_if (conn.last_applied() != seqno, "seqno = %"PRId64", expected %"PRId64, conn.last_applied(), seqno); seqno = 5; thd->report_last_committed (seqno); WAIT_FOR(conn.last_applied() == seqno); fail_if (conn.last_applied() != seqno, "seqno = %"PRId64", expected %"PRId64, conn.last_applied(), seqno); thd->report_last_committed (3); WAIT_FOR(conn.last_applied() == seqno); fail_if (conn.last_applied() != seqno, "seqno = %"PRId64", expected %"PRId64, conn.last_applied(), seqno); thd->reset(); seqno = 3; thd->report_last_committed (seqno); WAIT_FOR(conn.last_applied() == seqno); fail_if (conn.last_applied() != seqno, "seqno = %"PRId64", expected %"PRId64, conn.last_applied(), seqno); delete thd; } END_TEST Suite* service_thd_suite() { Suite* s = suite_create ("service_thd"); TCase* tc; tc = tcase_create ("service_thd"); tcase_add_test (tc, service_thd1); tcase_add_test (tc, service_thd2); suite_add_tcase (s, tc); return s; } percona-xtradb-cluster-galera/galera/tests/trx_handle_check.cpp0000644000000000000000000001464312247075736025250 0ustar rootroot00000000000000// // Copyright (C) 2010 Codership Oy // #include "trx_handle.hpp" #include "uuid.hpp" #include using namespace std; using namespace galera; START_TEST(test_states) { wsrep_uuid_t uuid = {{1, }}; // first check basic stuff // 1) initial state is executing // 2) invalid state changes are caught // 3) valid state changes change state TrxHandle* trx(new TrxHandle(0, uuid, -1, 1, true)); log_info << *trx; fail_unless(trx->state() == TrxHandle::S_EXECUTING); #if 0 // now setting wrong state results in abort try { trx->set_state(TrxHandle::S_COMMITTED); fail(""); } catch (...) { fail_unless(trx->state() == TrxHandle::S_EXECUTING); } #endif trx->set_state(TrxHandle::S_REPLICATING); fail_unless(trx->state() == TrxHandle::S_REPLICATING); trx->unref(); // abort before replication trx = new TrxHandle(0, uuid, -1, 1, true); trx->set_state(TrxHandle::S_MUST_ABORT); trx->set_state(TrxHandle::S_ABORTING); trx->set_state(TrxHandle::S_ROLLED_BACK); trx->unref(); // aborted during replication and does not certify trx = new TrxHandle(0, uuid, -1, 1, true); trx->set_state(TrxHandle::S_REPLICATING); trx->set_state(TrxHandle::S_MUST_ABORT); trx->set_state(TrxHandle::S_ABORTING); trx->set_state(TrxHandle::S_ROLLED_BACK); trx->unref(); // aborted during replication and certifies but does not certify // during replay (is this even possible?) trx = new TrxHandle(0, uuid, -1, 1, true); trx->set_state(TrxHandle::S_REPLICATING); trx->set_state(TrxHandle::S_MUST_ABORT); trx->set_state(TrxHandle::S_MUST_CERT_AND_REPLAY); trx->set_state(TrxHandle::S_CERTIFYING); trx->set_state(TrxHandle::S_MUST_ABORT); trx->set_state(TrxHandle::S_ABORTING); trx->set_state(TrxHandle::S_ROLLED_BACK); trx->unref(); // aborted during replication, certifies and commits trx = new TrxHandle(0, uuid, -1, 1, true); trx->set_state(TrxHandle::S_REPLICATING); trx->set_state(TrxHandle::S_MUST_ABORT); trx->set_state(TrxHandle::S_MUST_CERT_AND_REPLAY); trx->set_state(TrxHandle::S_CERTIFYING); trx->set_state(TrxHandle::S_MUST_REPLAY_AM); trx->set_state(TrxHandle::S_MUST_REPLAY_CM); trx->set_state(TrxHandle::S_MUST_REPLAY); trx->set_state(TrxHandle::S_REPLAYING); trx->set_state(TrxHandle::S_COMMITTED); trx->unref(); // aborted during certification, replays and commits trx = new TrxHandle(0, uuid, -1, 1, true); trx->set_state(TrxHandle::S_REPLICATING); trx->set_state(TrxHandle::S_CERTIFYING); trx->set_state(TrxHandle::S_MUST_ABORT); trx->set_state(TrxHandle::S_MUST_CERT_AND_REPLAY); trx->set_state(TrxHandle::S_CERTIFYING); trx->set_state(TrxHandle::S_MUST_REPLAY_AM); trx->set_state(TrxHandle::S_MUST_REPLAY_CM); trx->set_state(TrxHandle::S_MUST_REPLAY); trx->set_state(TrxHandle::S_REPLAYING); trx->set_state(TrxHandle::S_COMMITTED); trx->unref(); // aborted while waiting applying, replays and commits trx = new TrxHandle(0, uuid, -1, 1, true); trx->set_state(TrxHandle::S_REPLICATING); trx->set_state(TrxHandle::S_CERTIFYING); trx->set_state(TrxHandle::S_APPLYING); trx->set_state(TrxHandle::S_MUST_ABORT); trx->set_state(TrxHandle::S_MUST_REPLAY_AM); trx->set_state(TrxHandle::S_MUST_REPLAY_CM); trx->set_state(TrxHandle::S_MUST_REPLAY); trx->set_state(TrxHandle::S_REPLAYING); trx->set_state(TrxHandle::S_COMMITTED); trx->unref(); // aborted while waiting for commit order, replays and commits trx = new TrxHandle(0, uuid, -1, 1, true); trx->set_state(TrxHandle::S_REPLICATING); trx->set_state(TrxHandle::S_CERTIFYING); trx->set_state(TrxHandle::S_APPLYING); trx->set_state(TrxHandle::S_COMMITTING); trx->set_state(TrxHandle::S_MUST_ABORT); trx->set_state(TrxHandle::S_MUST_REPLAY_CM); trx->set_state(TrxHandle::S_MUST_REPLAY); trx->set_state(TrxHandle::S_REPLAYING); trx->set_state(TrxHandle::S_COMMITTED); trx->unref(); // smooth operation trx = new TrxHandle(0, uuid, -1, 1, true); trx->set_state(TrxHandle::S_REPLICATING); trx->set_state(TrxHandle::S_CERTIFYING); trx->set_state(TrxHandle::S_APPLYING); trx->set_state(TrxHandle::S_COMMITTING); trx->set_state(TrxHandle::S_COMMITTED); trx->unref(); } END_TEST START_TEST(test_serialization) { wsrep_uuid_t uuid; gu_uuid_generate(reinterpret_cast(&uuid), 0, 0); TrxHandle* trx(new TrxHandle(0, uuid, 4567, 8910, true)); fail_unless(serial_size(*trx) == 4 + 16 + 8 + 8 + 8 + 8); trx->set_flags(trx->flags() | TrxHandle::F_MAC_HEADER); fail_unless(serial_size(*trx) == 4 + 16 + 8 + 8 + 8 + 8 + 2); trx->set_flags(trx->flags() & ~TrxHandle::F_MAC_HEADER); fail_unless(serial_size(*trx) == 4 + 16 + 8 + 8 + 8 + 8); trx->append_annotation(reinterpret_cast("foobar"), strlen("foobar")); trx->set_flags(trx->flags() | TrxHandle::F_ANNOTATION); fail_unless(serial_size(*trx) == 4 + 16 + 8 + 8 + 8 + 8 + 4 + 6); trx->set_flags(trx->flags() & ~TrxHandle::F_ANNOTATION); fail_unless(serial_size(*trx) == 4 + 16 + 8 + 8 + 8 + 8); TrxHandle* trx2(new TrxHandle()); std::vector buf(serial_size(*trx)); fail_unless(serialize(*trx, &buf[0], buf.size(), 0) > 0); fail_unless(unserialize(&buf[0], buf.size(), 0, *trx2) > 0); trx->set_flags(trx->flags() | TrxHandle::F_MAC_PAYLOAD); buf.resize(serial_size(*trx)); fail_unless(serialize(*trx, &buf[0], buf.size(), 0) > 0); fail_unless(unserialize(&buf[0], buf.size(), 0, *trx2) > 0); trx->set_flags(trx->flags() | TrxHandle::F_ANNOTATION); buf.resize(serial_size(*trx)); fail_unless(serialize(*trx, &buf[0], buf.size(), 0) > 0); fail_unless(unserialize(&buf[0], buf.size(), 0, *trx2) > 0); fail_unless(serial_size(*trx2) == serial_size(*trx), "got serial_size(*trx2) = %zu, serial_size(*trx) = %zu", serial_size(*trx2), serial_size(*trx)); trx2->unref(); trx->unref(); } END_TEST Suite* trx_handle_suite() { Suite* s = suite_create("trx_handle"); TCase* tc; tc = tcase_create("test_states"); tcase_add_test(tc, test_states); suite_add_tcase(s, tc); tc = tcase_create("test_serialization"); tcase_add_test(tc, test_serialization); suite_add_tcase(s, tc); return s; } percona-xtradb-cluster-galera/galera/tests/write_set_check.cpp0000644000000000000000000004500712247075736025123 0ustar rootroot00000000000000/* * Copyright (C) 2010-2012 Codership Oy */ #include "write_set.hpp" #include "mapped_buffer.hpp" #include "gu_logger.hpp" #include "certification.hpp" #include "wsdb.cpp" #include "gcs_action_source.hpp" #include #include using namespace std; using namespace galera; typedef std::vector KeyPartSequence; START_TEST(test_key1) { static char k1[16]; static char k2[256]; static char k3[1 << 21]; static char k4[(1 << 22) - 5]; memset(k1, 0xab, sizeof(k1)); memset(k2, 0xcd, sizeof(k2)); memset(k3, 0x9e, sizeof(k3)); memset(k4, 0x8f, sizeof(k4)); const wsrep_buf_t kiovec[4] = { {k1, sizeof k1 }, {k2, sizeof k2 }, {k3, sizeof k3 }, {k4, sizeof k4 } }; Key key(1, kiovec, 4, 0); size_t expected_size(0); #ifndef GALERA_KEY_VLQ expected_size += 1 + std::min(sizeof k1, size_t(0xff)); expected_size += 1 + std::min(sizeof k2, size_t(0xff)); expected_size += 1 + std::min(sizeof k3, size_t(0xff)); expected_size += 1 + std::min(sizeof k4, size_t(0xff)); expected_size += sizeof(uint16_t); #else expected_size += gu::uleb128_size(sizeof k1) + sizeof k1; expected_size += gu::uleb128_size(sizeof k2) + sizeof k2; expected_size += gu::uleb128_size(sizeof k3) + sizeof k3; expected_size += gu::uleb128_size(sizeof k4) + sizeof k4; expected_size += gu::uleb128_size(expected_size); #endif fail_unless(serial_size(key) == expected_size, "%ld <-> %ld", serial_size(key), expected_size); KeyPartSequence kp(key.key_parts()); fail_unless(kp.size() == 4); gu::Buffer buf(galera::serial_size(key)); serialize(key, &buf[0], buf.size(), 0); Key key2(1); unserialize(&buf[0], buf.size(), 0, key2); fail_unless(key2 == key); } END_TEST START_TEST(test_key2) { static char k1[16]; static char k2[256]; static char k3[1 << 21]; static char k4[(1 << 22) - 5]; memset(k1, 0xab, sizeof(k1)); memset(k2, 0xcd, sizeof(k2)); memset(k3, 0x9e, sizeof(k3)); memset(k4, 0x8f, sizeof(k4)); const wsrep_buf_t kiovec[4] = { {k1, sizeof k1 }, {k2, sizeof k2 }, {k3, sizeof k3 }, {k4, sizeof k4 } }; Key key(2, kiovec, 4, 0); size_t expected_size(0); expected_size += 1; // flags #ifndef GALERA_KEY_VLQ expected_size += 1 + std::min(sizeof k1, size_t(0xff)); expected_size += 1 + std::min(sizeof k2, size_t(0xff)); expected_size += 1 + std::min(sizeof k3, size_t(0xff)); expected_size += 1 + std::min(sizeof k4, size_t(0xff)); expected_size += sizeof(uint16_t); #else expected_size += gu::uleb128_size(sizeof k1) + sizeof k1; expected_size += gu::uleb128_size(sizeof k2) + sizeof k2; expected_size += gu::uleb128_size(sizeof k3) + sizeof k3; expected_size += gu::uleb128_size(sizeof k4) + sizeof k4; expected_size += gu::uleb128_size(expected_size); #endif fail_unless(serial_size(key) == expected_size, "%ld <-> %ld", serial_size(key), expected_size); KeyPartSequence kp(key.key_parts()); fail_unless(kp.size() == 4); gu::Buffer buf(serial_size(key)); serialize(key, &buf[0], buf.size(), 0); Key key2(2); unserialize(&buf[0], buf.size(), 0, key2); fail_unless(key2 == key); } END_TEST START_TEST(test_write_set1) { WriteSet ws(1); const wsrep_buf_t key1[2] = { {void_cast("dbt\0t1"), 6}, {void_cast("aaa") , 3} }; const wsrep_buf_t key2[2] = { {void_cast("dbt\0t2"), 6}, {void_cast("bbbb"), 4} }; const char* rbr = "rbrbuf"; size_t rbr_len = 6; log_info << "ws0 " << serial_size(ws); ws.append_key(Key(1, key1, 2, 0)); log_info << "ws1 " << serial_size(ws); ws.append_key(Key(1, key2, 2, 0)); log_info << "ws2 " << serial_size(ws); ws.append_data(rbr, rbr_len); gu::Buffer rbrbuf(rbr, rbr + rbr_len); log_info << "rbrlen " << gu::serial_size4(rbrbuf); log_info << "wsrbr " << serial_size(ws); gu::Buffer buf(serial_size(ws)); serialize(ws, &buf[0], buf.size(), 0); size_t expected_size = 4 // row key sequence size #ifndef GALERA_KEY_VLQ + 2 + 1 + 6 + 1 + 3 // key1 + 2 + 1 + 6 + 1 + 4 // key2 #else + 1 + 1 + 6 + 1 + 3 // key1 + 1 + 1 + 6 + 1 + 4 // key2 #endif + 4 + 6; // rbr fail_unless(buf.size() == expected_size, "%zd <-> %zd <-> %zd", buf.size(), expected_size, serial_size(ws)); WriteSet ws2(0); size_t ret = unserialize(&buf[0], buf.size(), 0, ws2); fail_unless(ret == expected_size); WriteSet::KeySequence rks; ws.get_keys(rks); WriteSet::KeySequence rks2; ws.get_keys(rks2); fail_unless(rks2 == rks); fail_unless(ws2.get_data() == ws.get_data()); } END_TEST START_TEST(test_write_set2) { WriteSet ws(2); const wsrep_buf_t key1[2] = { {void_cast("dbt\0t1"), 6}, {void_cast("aaa") , 3} }; const wsrep_buf_t key2[2] = { {void_cast("dbt\0t2"), 6}, {void_cast("bbbb"), 4} }; const char* rbr = "rbrbuf"; size_t rbr_len = 6; log_info << "ws0 " << serial_size(ws); ws.append_key(Key(2, key1, 2, 0)); log_info << "ws1 " << serial_size(ws); ws.append_key(Key(2, key2, 2, 0)); log_info << "ws2 " << serial_size(ws); ws.append_data(rbr, rbr_len); gu::Buffer rbrbuf(rbr, rbr + rbr_len); log_info << "rbrlen " << gu::serial_size4(rbrbuf); log_info << "wsrbr " << serial_size(ws); gu::Buffer buf(serial_size(ws)); serialize(ws, &buf[0], buf.size(), 0); size_t expected_size = 4 // row key sequence size #ifndef GALERA_KEY_VLQ + 2 + 1 + 1 + 6 + 1 + 3 // key1 + 2 + 1 + 1 + 6 + 1 + 4 // key2 #else + 1 + 1 + 6 + 1 + 3 // key1 + 1 + 1 + 6 + 1 + 4 // key2 #endif + 4 + 6; // rbr fail_unless(buf.size() == expected_size, "%zd <-> %zd <-> %zd", buf.size(), expected_size, serial_size(ws)); WriteSet ws2(2); size_t ret = unserialize(&buf[0], buf.size(), 0, ws2); fail_unless(ret == expected_size); WriteSet::KeySequence rks; ws.get_keys(rks); WriteSet::KeySequence rks2; ws2.get_keys(rks2); fail_unless(rks2 == rks); fail_unless(ws2.get_data() == ws.get_data()); } END_TEST START_TEST(test_mapped_buffer) { string wd("/tmp"); MappedBuffer mb(wd, 1 << 8); mb.resize(16); for (size_t i = 0; i < 16; ++i) { mb[i] = static_cast(i); } mb.resize(1 << 8); for (size_t i = 0; i < 16; ++i) { fail_unless(mb[i] == static_cast(i)); } for (size_t i = 16; i < (1 << 8); ++i) { mb[i] = static_cast(i); } mb.resize(1 << 20); for (size_t i = 0; i < (1 << 8); ++i) { fail_unless(mb[i] == static_cast(i)); } for (size_t i = 0; i < (1 << 20); ++i) { mb[i] = static_cast(i); } } END_TEST START_TEST(test_cert_hierarchical_v1) { log_info << "test_cert_hierarchical_v1"; struct wsinfo_ { wsrep_uuid_t uuid; wsrep_conn_id_t conn_id; wsrep_trx_id_t trx_id; wsrep_buf_t key[3]; size_t iov_len; wsrep_seqno_t local_seqno; wsrep_seqno_t global_seqno; wsrep_seqno_t last_seen_seqno; wsrep_seqno_t expected_depends_seqno; int flags; Certification::TestResult result; } wsi[] = { // 1 - 3, test symmetric case for dependencies // 1: no dependencies { { {1, } }, 1, 1, { {void_cast("1"), 1}, }, 1, 1, 1, 0, 0, 0, Certification::TEST_OK}, // 2: depends on 1, no conflict { { {1, } }, 1, 1, { {void_cast("1"), 1}, {void_cast("1"), 1} }, 2, 2, 2, 0, 1, 0, Certification::TEST_OK}, // 3: depends on 2, no conflict { { {1, } }, 1, 1, { {void_cast("1"), 1}, }, 1, 3, 3, 0, 2, 0, Certification::TEST_OK}, // 4 - 8, test symmetric case for conflicts // 4: depends on 3, no conflict { { {1, } }, 1, 1, { {void_cast("1"), 1}, }, 1, 4, 4, 3, 3, 0, Certification::TEST_OK}, // 5: conflict with 4 { { {2, } }, 1, 1, { {void_cast("1"), 1}, {void_cast("1"), 1} }, 2, 5, 5, 3, -1, 0, Certification::TEST_FAILED}, // 6: depends on 4 (failed 5 not present in index), no conflict { { {2, } }, 1, 1, { {void_cast("1"), 1}, {void_cast("1"), 1} }, 2, 6, 6, 5, 4, 0, Certification::TEST_OK}, // 7: conflicts with 6 { { {1, } }, 1, 1, { {void_cast("1"), 1}, }, 1, 7, 7, 5, -1, 0, Certification::TEST_FAILED}, // 8: to isolation: must not conflict, depends on global_seqno - 1 { { {1, } }, 1, 1, { {void_cast("1"), 1}, }, 1, 8, 8, 5, 7, TrxHandle::F_ISOLATION, Certification::TEST_OK}, // 9: to isolation: must not conflict, depends on global_seqno - 1 { { {2, } }, 1, 1, { {void_cast("1"), 1}, }, 1, 9, 9, 5, 8, TrxHandle::F_ISOLATION, Certification::TEST_OK}, }; size_t nws(sizeof(wsi)/sizeof(wsi[0])); gu::Config conf; galera::Certification cert(conf); cert.assign_initial_position(0, 1); mark_point(); for (size_t i(0); i < nws; ++i) { TrxHandle* trx(new TrxHandle(1, wsi[i].uuid, wsi[i].conn_id, wsi[i].trx_id, false)); trx->append_key(Key(1, wsi[i].key, wsi[i].iov_len, 0)); trx->set_last_seen_seqno(wsi[i].last_seen_seqno); trx->set_flags(trx->flags() | wsi[i].flags); trx->flush(0); // serialize/unserialize to verify that ver1 trx is serializable const galera::MappedBuffer& wc(trx->write_set_collection()); gu::Buffer buf(wc.size()); std::copy(&wc[0], &wc[0] + wc.size(), &buf[0]); trx->unref(); trx = new TrxHandle(); size_t offset(unserialize(&buf[0], buf.size(), 0, *trx)); log_info << "ws[" << i << "]: " << buf.size() - offset; trx->append_write_set(&buf[0] + offset, buf.size() - offset); trx->set_received(0, wsi[i].local_seqno, wsi[i].global_seqno); Certification::TestResult result(cert.append_trx(trx)); fail_unless(result == wsi[i].result, "g: %lld r: %d er: %d", trx->global_seqno(), result, wsi[i].result); fail_unless(trx->depends_seqno() == wsi[i].expected_depends_seqno, "wsi: %zu g: %lld ld: %lld eld: %lld", i, trx->global_seqno(), trx->depends_seqno(), wsi[i].expected_depends_seqno); cert.set_trx_committed(trx); trx->unref(); } } END_TEST START_TEST(test_cert_hierarchical_v2) { log_info << "test_cert_hierarchical_v2"; const int version(2); struct wsinfo_ { wsrep_uuid_t uuid; wsrep_conn_id_t conn_id; wsrep_trx_id_t trx_id; wsrep_buf_t key[3]; size_t iov_len; bool shared; wsrep_seqno_t local_seqno; wsrep_seqno_t global_seqno; wsrep_seqno_t last_seen_seqno; wsrep_seqno_t expected_depends_seqno; int flags; Certification::TestResult result; } wsi[] = { // 1 - 4: shared - shared // First four cases are shared keys, they should not collide or // generate dependency // 1: no dependencies { { {1, } }, 1, 1, { {void_cast("1"), 1}, }, 1, true, 1, 1, 0, 0, 0, Certification::TEST_OK}, // 2: no dependencies { { {1, } }, 1, 2, { {void_cast("1"), 1}, {void_cast("1"), 1}, }, 2, true, 2, 2, 0, 0, 0, Certification::TEST_OK}, // 3: no dependencies { { {2, } }, 1, 3, { {void_cast("1"), 1}, {void_cast("1"), 1}, }, 2, true, 3, 3, 0, 0, 0, Certification::TEST_OK}, // 4: no dependencies { { {3, } }, 1, 4, { {void_cast("1"), 1}, }, 1, true, 4, 4, 0, 0, 0, Certification::TEST_OK}, // 5: shared - exclusive // 5: depends on 4 { { {2, } }, 1, 5, { {void_cast("1"), 1}, {void_cast("1"), 1}, }, 2, false, 5, 5, 0, 4, 0, Certification::TEST_OK}, // 6 - 8: exclusive - shared // 6: collides with 5 { { {1, } }, 1, 6, { {void_cast("1"), 1}, {void_cast("1"), 1}, }, 2, true, 6, 6, 4, -1, 0, Certification::TEST_FAILED}, // 7: collides with 5 { { {1, } }, 1, 7, { {void_cast("1"), 1}, }, 1, true, 7, 7, 4, -1, 0, Certification::TEST_FAILED}, // 8: collides with 5 { { {1, } }, 1, 8, { {void_cast("1"), 1}, {void_cast("1"), 1}, {void_cast("1"), 1}}, 3, true, 8, 8, 4, -1, 0, Certification::TEST_FAILED}, // 9 - 10: shared key shadows dependency to 5 // 9: depends on 5 { { {2, } }, 1, 9, { {void_cast("1"), 1}, {void_cast("1"), 1}, }, 2, true, 9, 9, 0, 5, 0, Certification::TEST_OK}, // 10: depends on 5 { { {2, } }, 1, 10, { {void_cast("1"), 1}, {void_cast("1"), 1}, }, 2, true, 10, 10, 6, 5, 0, Certification::TEST_OK}, // 11 - 13: exclusive - shared - exclusive dependency { { {2, } }, 1, 11, { {void_cast("1"), 1}, {void_cast("1"), 1}, }, 2, false, 11, 11, 10, 10, 0, Certification::TEST_OK}, { { {2, } }, 1, 12, { {void_cast("1"), 1}, {void_cast("1"), 1}, }, 2, true, 12, 12, 10, 11, 0, Certification::TEST_OK}, { { {2, } }, 1, 13, { {void_cast("1"), 1}, {void_cast("1"), 1}, }, 2, false, 13, 13, 10, 12, 0, Certification::TEST_OK}, }; size_t nws(sizeof(wsi)/sizeof(wsi[0])); gu::Config conf; galera::Certification cert(conf); cert.assign_initial_position(0, version); mark_point(); for (size_t i(0); i < nws; ++i) { TrxHandle* trx(new TrxHandle(version, wsi[i].uuid, wsi[i].conn_id, wsi[i].trx_id, false)); trx->append_key(Key(version, wsi[i].key, wsi[i].iov_len, (wsi[i].shared == true ? Key::F_SHARED : 0))); trx->set_last_seen_seqno(wsi[i].last_seen_seqno); trx->set_flags(trx->flags() | wsi[i].flags); trx->flush(0); // serialize/unserialize to verify that ver1 trx is serializable const galera::MappedBuffer& wc(trx->write_set_collection()); gu::Buffer buf(wc.size()); std::copy(&wc[0], &wc[0] + wc.size(), &buf[0]); trx->unref(); trx = new TrxHandle(); size_t offset(unserialize(&buf[0], buf.size(), 0, *trx)); log_info << "ws[" << i << "]: " << buf.size() - offset; trx->append_write_set(&buf[0] + offset, buf.size() - offset); trx->set_received(0, wsi[i].local_seqno, wsi[i].global_seqno); Certification::TestResult result(cert.append_trx(trx)); fail_unless(result == wsi[i].result, "g: %lld res: %d exp: %d", trx->global_seqno(), result, wsi[i].result); fail_unless(trx->depends_seqno() == wsi[i].expected_depends_seqno, "wsi: %zu g: %lld ld: %lld eld: %lld", i, trx->global_seqno(), trx->depends_seqno(), wsi[i].expected_depends_seqno); cert.set_trx_committed(trx); trx->unref(); } } END_TEST START_TEST(test_trac_726) { log_info << "test_trac_726"; const int version(2); gu::Config conf; galera::Certification cert(conf); wsrep_uuid_t uuid1 = {{1, }}; wsrep_uuid_t uuid2 = {{2, }}; cert.assign_initial_position(0, version); mark_point(); wsrep_buf_t key1 = {void_cast("1"), 1}; wsrep_buf_t key2 = {void_cast("2"), 1}; { TrxHandle* trx(new TrxHandle(version, uuid1, 0, 0, false)); trx->append_key(Key(version, &key1, 1, 0)); trx->set_last_seen_seqno(0); trx->flush(0); // serialize/unserialize to verify that ver1 trx is serializable const galera::MappedBuffer& wc(trx->write_set_collection()); gu::Buffer buf(wc.size()); std::copy(&wc[0], &wc[0] + wc.size(), &buf[0]); trx->unref(); trx = new TrxHandle(); size_t offset(unserialize(&buf[0], buf.size(), 0, *trx)); trx->append_write_set(&buf[0] + offset, buf.size() - offset); trx->set_received(0, 1, 1); Certification::TestResult result(cert.append_trx(trx)); fail_unless(result == Certification::TEST_OK); cert.set_trx_committed(trx); trx->unref(); } { TrxHandle* trx(new TrxHandle(version, uuid2, 0, 0, false)); trx->append_key(Key(version, &key2, 1, 0)); trx->append_key(Key(version, &key2, 1, Key::F_SHARED)); trx->append_key(Key(version, &key1, 1, 0)); trx->set_last_seen_seqno(0); trx->flush(0); // serialize/unserialize to verify that ver1 trx is serializable const galera::MappedBuffer& wc(trx->write_set_collection()); gu::Buffer buf(wc.size()); std::copy(&wc[0], &wc[0] + wc.size(), &buf[0]); trx->unref(); trx = new TrxHandle(); size_t offset(unserialize(&buf[0], buf.size(), 0, *trx)); trx->append_write_set(&buf[0] + offset, buf.size() - offset); trx->set_received(0, 2, 2); Certification::TestResult result(cert.append_trx(trx)); fail_unless(result == Certification::TEST_FAILED); cert.set_trx_committed(trx); trx->unref(); } } END_TEST Suite* write_set_suite() { Suite* s = suite_create("write_set"); TCase* tc; tc = tcase_create("test_key1"); tcase_add_test(tc, test_key1); suite_add_tcase(s, tc); tc = tcase_create("test_key2"); tcase_add_test(tc, test_key2); suite_add_tcase(s, tc); tc = tcase_create("test_write_set1"); tcase_add_test(tc, test_write_set1); suite_add_tcase(s, tc); tc = tcase_create("test_write_set2"); tcase_add_test(tc, test_write_set2); suite_add_tcase(s, tc); tc = tcase_create("test_mapped_buffer"); tcase_add_test(tc, test_mapped_buffer); suite_add_tcase(s, tc); tc = tcase_create("test_cert_hierarchical_v1"); tcase_add_test(tc, test_cert_hierarchical_v1); suite_add_tcase(s, tc); tc = tcase_create("test_cert_hierarchical_v2"); tcase_add_test(tc, test_cert_hierarchical_v2); suite_add_tcase(s, tc); tc = tcase_create("test_trac_726"); tcase_add_test(tc, test_trac_726); suite_add_tcase(s, tc); return s; } percona-xtradb-cluster-galera/galerautils/ChangeLog0000644000000000000000000000270212247075736022741 0ustar rootroot000000000000002009-09-20 Alex Added RegEx class for matching strings with POSIX regular expressions. Renamed URL class to URI to better reflect what it does. Added get_host(), get_user() and get_port() methods and a unit test. Modularized galerautils++ unit tests. Version 0.3.5 2009-09-17 Alex Added gu_utils.hpp to hold general-purpose templates and functions (now with to_string() template functions). Logger class cleanups. Exception class cleanups. Added stack tracing macro. New Throw class for composing verbose exception messages. Version 0.3.4 2009-09-01 Alex Added a simple option line parser. Some optimizations and cleanups. Version 0.3.3 2009-07-07 Alex Slightly changed gu_fifo interface. Added gu_lock_step object. Version 0.3.2. 2009-06-21 Alex Moved TO monitor module from GCS to galerautils. Version 0.3.1. 2009-06-08 Alex Started galerautils++ project. Added galerautils.hpp and C++-style logger and assert variants. Version 0.3.0. 2008-11-16 Alex Added gu_fifo_t class for mallocless FIFO queue. Version 0.2.9. 2008-03-23 Alex Added gu_timeval_diff() and gu_clock_diff() functions. Bumped interface version. 2008-02-21 Teemu Made DBUG thread safe. 2007-11-01 Alex Fixed thread safe compilation without MySQL Tagged release 0.2.5 2007-10-18 Alex Fixed compilation. Added gtohl/htogl/gtohs/htogs functions. Tagged release 0.2.4 percona-xtradb-cluster-galera/galerautils/README0000644000000000000000000000112012247075736022040 0ustar rootroot00000000000000libgalerautils is a library of utilities commonly used by Galera project. Current release includes logging, mutex and malloc debug functions and convenience macros. This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY, to the extent permitted by law; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. libgalerautils is free software. Please see the file COPYING for details. For documentation, please see the files in the doc subdirectory. For building and installation instructions please see the INSTALL file. percona-xtradb-cluster-galera/galerautils/SConscript0000644000000000000000000000014012247075736023173 0ustar rootroot00000000000000# SConscript for building galerautils SConscript(Split('''src/SConscript tests/SConscript''')) percona-xtradb-cluster-galera/galerautils/doc/0000755000000000000000000000000012247075736021733 5ustar rootroot00000000000000percona-xtradb-cluster-galera/galerautils/src/0000755000000000000000000000000012247075736021755 5ustar rootroot00000000000000percona-xtradb-cluster-galera/galerautils/tests/0000755000000000000000000000000012247075736022330 5ustar rootroot00000000000000percona-xtradb-cluster-galera/galerautils/doc/Doxyfile0000644000000000000000000014366712247075736023462 0ustar rootroot00000000000000# Doxyfile 1.4.6 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = GCS # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = 0.2.3 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = ./ # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, # Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, # Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, # Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, # Swedish, and Ukrainian. OUTPUT_LANGUAGE = English # This tag can be used to specify the encoding used in the generated output. # The encoding is not always determined by the language that is chosen, # but also whether or not the output is meant for Windows or non-Windows users. # In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES # forces the Windows encoding (this is the default for the Windows binary), # whereas setting the tag to NO uses a Unix-style encoding (the default for # all platforms other than Windows). USE_WINDOWS_ENCODING = NO # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like the Qt-style comments (thus requiring an # explicit @brief command for a brief description. JAVADOC_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the DETAILS_AT_TOP tag is set to YES then Doxygen # will output the detailed description near the top, like JavaDoc. # If set to NO, the detailed description appears after the member # documentation. DETAILS_AT_TOP = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for Java. # For instance, namespaces will be presented as packages, qualified scopes # will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to # include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from the # version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = ../src # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py FILE_PATTERNS = *.c *.h *.hpp # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES (the default) # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES (the default) # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compressed HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # If the GENERATE_TREEVIEW tag is set to YES, a side panel will be # generated containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = YES # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will # generate a call dependency graph for every global function or class method. # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected # functions only using the \callgraph command. CALL_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width # (in pixels) of the graphs generated by dot. If a graph becomes larger than # this value, doxygen will try to truncate the graph, so that it fits within # the specified constraint. Beware that most browsers cannot cope with very # large images. MAX_DOT_GRAPH_WIDTH = 1024 # The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height # (in pixels) of the graphs generated by dot. If a graph becomes larger than # this value, doxygen will try to truncate the graph, so that it fits within # the specified constraint. Beware that most browsers cannot cope with very # large images. MAX_DOT_GRAPH_HEIGHT = 1024 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that a graph may be further truncated if the graph's # image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH # and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), # the graph is not depth-constrained. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, which results in a white background. # Warning: Depending on the platform used, enabling this option may lead to # badly anti-aliased labels on the edges of a graph (i.e. they become hard to # read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO percona-xtradb-cluster-galera/galerautils/src/SConscript0000644000000000000000000000241112247075736023765 0ustar rootroot00000000000000Import('env') libgalerautils_env = env.Clone() # C part libgalerautils_sources = [ 'gu_abort.c', 'gu_dbug.c', 'gu_fifo.c', 'gu_lock_step.c', 'gu_log.c', 'gu_mem.c', 'gu_mmh3.c', 'gu_spooky.c', 'gu_rand.c', 'gu_mutex.c', 'gu_print_buf.c', 'gu_to.c', 'gu_utils.c', 'gu_uuid.c', 'gu_backtrace.c', 'gu_limits.c', 'gu_time.c' ] libgalerautils_env.StaticLibrary('galerautils', libgalerautils_sources) # C++ part libgalerautilsxx_sources = [ 'gu_datetime.cpp', 'gu_exception.cpp', 'gu_logger.cpp', 'gu_prodcons.cpp', 'gu_regex.cpp', 'gu_string.cpp', 'gu_uri.cpp', 'gu_buffer.cpp', 'gu_utils++.cpp', 'gu_config.cpp', 'gu_resolver.cpp' ] #if '-DGALERA_USE_GU_NETWORK' in libgalerautils_env['CPPFLAGS']: # libgalerautilsxx_sources = [libgalerautilsxx_sources, # 'gu_resolver.cpp'] # disable old style cast warnings libgalerautils_env.Append(CXXFLAGS = ' -Wno-old-style-cast') libgalerautils_env.StaticLibrary('galerautils++', libgalerautilsxx_sources) # env.Append(LIBGALERA_OBJS = libgalerautils_env.SharedObject( libgalerautils_sources)) env.Append(LIBGALERA_OBJS = libgalerautils_env.SharedObject( libgalerautilsxx_sources)) percona-xtradb-cluster-galera/galerautils/src/avalanche.c0000644000000000000000000000510112247075736024040 0ustar rootroot00000000000000/* * Copyright (c) 2012 Codership Oy * * This program is to measure avalanche effect of different hash * implementations, for that it uses 1M of random 8-byte keys. * Use #define macro below to define the implementation to test. * * Compilation: g++ -DHAVE_ENDIAN_H -DHAVE_BYTESWAP_H -O3 -Wall -Wno-unused avalanche.c \ gu_mmh3.c gu_spooky.c -o avalanche && time ./avalanche * Visualization in gnuplot: unset cbtics set xrange [-0.5:64.5] set yrange [-0.5:64.5] set cbrange [0.0:1.0] set xlabel 'Hash bit' set ylabel 'Flipped bit in message' set cblabel 'Hash bit flip probability [0.0 - 1.0]' set palette rgbformula 7,7,7 plot 'avalanche.out' matrix with image */ #include "gu_hash.h" #include #include #include uint64_t flip_count[64*64] = { 0, }; //#define HASH gu_mmh128_64 #define HASH gu_fast_hash64 int main (int argc, char* argv[]) { int n_keys = 1 << 20; int i, j, k; /* collect statistics */ for (k = 0; k < n_keys; k++) { uint64_t key_part = rand(); uint64_t const key = (key_part << 32) + rand(); uint64_t const hash = HASH (&key, sizeof(key)); for (j = 0; j < 64; j++) { uint64_t const flipped_key = key ^ (GU_LONG_LONG(0x01) << j); uint64_t const flipped_hash = HASH (&flipped_key, sizeof(flipped_key)); uint64_t flipped_bits = hash ^ flipped_hash; for (i = 0; i < 64; i++) { int const idx = j * 64 + i; flip_count[idx] += flipped_bits & GU_LONG_LONG(0x01); flipped_bits >>= 1; } } } /* print statistics */ char out_name [256] = { 0, }; snprintf(out_name, sizeof(out_name) - 1, "%s.out", argv[0]); FILE* const out = fopen(out_name, "w"); if (!out) { fprintf (stderr, "Could not open file for writing: '%s': %d (%s)", out_name, errno, strerror(errno)); return errno; } uint64_t base = n_keys; double min_stat = 1.0; double max_stat = 0.0; for (j = 0; j < 64; j++) { for (i = 0; i < 64; i++) { int const idx = j * 64 + i; double stat = (((double)(flip_count[idx]))/base); min_stat = min_stat > stat ? stat : min_stat; max_stat = max_stat < stat ? stat : max_stat; fprintf (out, "%6.4f%c", stat, 63 == i ? '\n' : '\t'); } } fclose(out); printf ("%6.4f : %6.4f (delta: %6.4f)\n", min_stat, max_stat, max_stat - min_stat); return 0; } percona-xtradb-cluster-galera/galerautils/src/galerautils.h0000644000000000000000000000120112247075736024434 0ustar rootroot00000000000000// Copyright (C) 2007-2009 Codership Oy /** * @file GaleraUtils main header file * * $Id: galerautils.h 3207 2013-08-21 09:08:51Z vlad $ */ #ifndef _galerautils_h_ #define _galerautils_h_ #include "gu_macros.h" #include "gu_limits.h" #include "gu_byteswap.h" #include "gu_time.h" #include "gu_log.h" #include "gu_conf.h" #include "gu_assert.h" #include "gu_mem.h" #include "gu_mutex.h" #include "gu_dbug.h" #include "gu_fifo.h" #include "gu_uuid.h" #include "gu_to.h" #include "gu_lock_step.h" #include "gu_utils.h" #include "gu_config.h" #include "gu_abort.h" #include "gu_errno.h" #endif /* _galerautils_h_ */ percona-xtradb-cluster-galera/galerautils/src/galerautils.hpp0000644000000000000000000000152412247075736025004 0ustar rootroot00000000000000// Copyright (C) 2009 Codership Oy /*! * @file GaleraUtils main header file * * $Id: galerautils.hpp 3336 2013-10-28 07:41:56Z teemu $ */ #ifndef _galerautils_hpp_ #define _galerautils_hpp_ #include "gu_exception.hpp" #include "gu_throw.hpp" #include "gu_logger.hpp" #include "gu_assert.hpp" #include "gu_mutex.hpp" #include "gu_cond.hpp" #include "gu_lock.hpp" #include "gu_monitor.hpp" #include "gu_macros.hpp" #include "gu_utils.hpp" #include "gu_convert.hpp" #include "gu_string.hpp" #include "gu_config.hpp" #include "gu_prodcons.hpp" extern "C" { #include "gu_macros.h" #include "gu_limits.h" #include "gu_byteswap.h" #include "gu_time.h" #include "gu_conf.h" #include "gu_mem.h" #include "gu_dbug.h" #include "gu_fifo.h" #include "gu_uuid.h" //#include "gu_utils.h" // gu_abort() } #endif /* _galerautils_hpp_ */ percona-xtradb-cluster-galera/galerautils/src/gu_abort.c0000644000000000000000000000133312247075736023723 0ustar rootroot00000000000000// Copyright (C) 2011-2013 Codership Oy /** * @file Clean abort function * * $Id: gu_abort.c 3336 2013-10-28 07:41:56Z teemu $ */ #include "gu_abort.h" #include "gu_system.h" #include "gu_log.h" #include /* for setrlimit() */ #include /* for signal() */ #include /* for abort() */ void gu_abort (void) { /* avoid coredump */ struct rlimit core_limits = { 0, 0 }; setrlimit (RLIMIT_CORE, &core_limits); /* restore default SIGABRT handler */ signal (SIGABRT, SIG_DFL); #if defined(GU_SYS_PROGRAM_NAME) gu_info ("%s: Terminated.", GU_SYS_PROGRAM_NAME); #else gu_info ("Program terminated."); #endif abort(); } percona-xtradb-cluster-galera/galerautils/src/gu_abort.h0000644000000000000000000000072512247075736023734 0ustar rootroot00000000000000// Copyright (C) 2012-2013 Codership Oy /** * @file "Clean" abort function to avoid stack and core dumps * * $Id: gu_abort.h 3336 2013-10-28 07:41:56Z teemu $ */ #ifndef _gu_abort_h_ #define _gu_abort_h_ #ifdef __cplusplus extern "C" { #endif #include "gu_macros.h" /* This function is for clean aborts, when we can't gracefully exit otherwise */ extern void gu_abort() GU_NORETURN; #ifdef __cplusplus } #endif #endif /* _gu_abort_h_ */ percona-xtradb-cluster-galera/galerautils/src/gu_arch.h0000644000000000000000000000266012247075736023542 0ustar rootroot00000000000000// Copyright (C) 2012 Codership Oy /** * @file CPU architecture related functions/macros * * $Id: gu_arch.h 3207 2013-08-21 09:08:51Z vlad $ */ #ifndef _gu_arch_h_ #define _gu_arch_h_ #if defined(HAVE_ENDIAN_H) # include #elif defined(HAVE_SYS_ENDIAN_H) /* FreeBSD */ # include #elif defined(HAVE_SYS_BYTEORDER_H) # include #elif defined(__APPLE__) # include #else # error "No byte order header file detected" #endif #if defined(__BYTE_ORDER) # if __BYTE_ORDER == __LITTLE_ENDIAN # define GU_LITTLE_ENDIAN # endif #elif defined(_BYTE_ORDER) /* FreeBSD */ # if _BYTE_ORDER == _LITTLE_ENDIAN # define GU_LITTLE_ENDIAN # endif #elif defined(__APPLE__) && defined(__DARWIN_BYTE_ORDER) # if __DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN # define GU_LITTLE_ENDIAN # endif #elif defined(__sun__) # if !defined(_BIG_ENDIAN) # define GU_LITTLE_ENDIAN # endif #else # error "Byte order not defined" #endif #if defined(__sun__) # define GU_WORDSIZE 64 /* Solaris 11 is only 64-bit ATM */ #elif defined(__APPLE__) || defined(__FreeBSD__) # include # define GU_WORDSIZE __WORDSIZE #else # include # define GU_WORDSIZE __WORDSIZE #endif #if (GU_WORDSIZE != 32) && (GU_WORDSIZE != 64) # error "Unsupported wordsize" #endif /* I'm not aware of the platforms that don't, but still */ #define GU_ALLOW_UNALIGNED_READS 1 #endif /* _gu_arch_h_ */ percona-xtradb-cluster-galera/galerautils/src/gu_assert.h0000644000000000000000000000111612247075736024121 0ustar rootroot00000000000000// Copyright (C) 2007 Codership Oy /** * @file Assert macro definition * * $Id: gu_assert.h 248 2008-03-23 16:32:00Z alex $ */ #ifndef _gu_assert_h_ #define _gu_assert_h_ #include "gu_log.h" #ifndef DEBUG_ASSERT #include #else #include #undef assert /** Assert that sleeps instead of aborting the program, saving it for gdb */ #define assert(expr) if (!(expr)) { \ gu_fatal ("Assertion (%s) failed", __STRING(expr)); \ while(1) sleep(1); } #endif /* DEBUG_ASSERT */ #endif /* _gu_assert_h_ */ percona-xtradb-cluster-galera/galerautils/src/gu_assert.hpp0000644000000000000000000000135612247075736024467 0ustar rootroot00000000000000// Copyright (C) 2009 Codership Oy /** * @file Assert macro definition * * $Id: gu_assert.hpp 3336 2013-10-28 07:41:56Z teemu $ */ #ifndef _gu_assert_hpp_ #define _gu_assert_hpp_ #ifndef DEBUG_ASSERT #include #else #include #undef assert #include "gu_logger.hpp" /** Assert that sleeps instead of aborting the program, saving it for gdb */ #define assert(expr) \ if (!(expr)) { \ log_fatal << "Assertion (" << __STRING(expr) << ") failed"; \ while(1) sleep(1); \ } #endif /* DEBUG_ASSERT */ #endif /* _gu_assert_hpp_ */ percona-xtradb-cluster-galera/galerautils/src/gu_atomic.hpp0000644000000000000000000000267612247075736024450 0ustar rootroot00000000000000// // Copyright (C) 2010 Codership Oy // // // @todo Check that the at least the following gcc versions are supported // gcc version 4.1.2 20080704 (Red Hat 4.1.2-48) // #ifndef GU_ATOMIC_HPP #define GU_ATOMIC_HPP #include namespace gu { template class Atomic { public: Atomic(const I i = 0) : i_(i) { } I operator()() const { __sync_synchronize(); return i_; } Atomic& operator=(const I i) { i_ = i; __sync_synchronize(); return *this; } I fetch_and_zero() { return __sync_fetch_and_and(&i_, 0); } I fetch_and_add(const I i) { return __sync_fetch_and_add(&i_, i); } I add_and_fetch(const I i) { return __sync_add_and_fetch(&i_, i); } I sub_and_fetch(const I i) { return __sync_sub_and_fetch(&i_, i); } Atomic& operator++() { __sync_fetch_and_add(&i_, 1); return *this; } Atomic& operator--() { __sync_fetch_and_sub(&i_, 1); return *this; } Atomic& operator+=(const I i) { __sync_fetch_and_add(&i_, i); return *this; } private: I i_; }; } #endif // GU_ATOMIC_HPP percona-xtradb-cluster-galera/galerautils/src/gu_backtrace.c0000644000000000000000000000110712247075736024532 0ustar rootroot00000000000000// Copyright (C) 2012 Codership Oy #include "gu_backtrace.h" #include "gu_log.h" #ifdef __GNUC__ #include #include char** gu_backtrace(int* size) { char** strings; void** array = malloc(*size * sizeof(void*)); if (!array) { gu_error("could not allocate memory for %d pointers\n", *size); return NULL; } *size = backtrace(array, *size); strings = backtrace_symbols(array, *size); free(array); return strings; } #else char **gu_backtrace(int* size) { return NULL; } #endif /* */ percona-xtradb-cluster-galera/galerautils/src/gu_backtrace.h0000644000000000000000000000141112247075736024535 0ustar rootroot00000000000000// Copyright (C) 2012 Codership Oy #ifndef GU_BACKTRACE_H #define GU_BACKTRACE_H #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /*! * Get current backtrace. Return buffer will contain backtrace symbols if * available. NULL pointer is returned if getting backtrace is not supported * on current platform. Maximum number of frames in backtrace is passed * in size parameter, number of frames in returned backtrace is assigned * in size parameter on return. * * @param size Pointer to integer containing maximum number of frames * in backtrace * * @return Allocated array of strings containing backtrace symbols */ char** gu_backtrace(int* size); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* GU_BACKTRACE_H */ percona-xtradb-cluster-galera/galerautils/src/gu_backtrace.hpp0000644000000000000000000000256512247075736025110 0ustar rootroot00000000000000// Copyright (C) 2012 Codership Oy #ifndef GU_BACKTRACE_HPP #define GU_BACKTRACE_HPP #include "gu_backtrace.h" #include #include namespace gu { /*! * Utility class to print backtraces. */ class Backtrace { public: /*! * Construct backtrace object. * * @param Maximum number of backtrace symbols resolved (default 50). */ Backtrace(int size = 50) : symbols_size_(size), symbols_(gu_backtrace(&symbols_size_)) { } ~Backtrace() { free(symbols_); } /*! * Print backtrace into ostream. * * @param os Ostream to print backtrace into. * @param delim Delimiter separating backtrace symbols. */ void print(std::ostream& os, char delim = '\n') { if (symbols_ != 0) { for (int i(0); i < symbols_size_; ++i) { os << symbols_[i] << delim; } } else { os << "no backtrace available"; } } private: Backtrace(const Backtrace&); void operator=(const Backtrace&); int symbols_size_; char** symbols_; }; } #endif // GU_BACKTRACE_HPP percona-xtradb-cluster-galera/galerautils/src/gu_buffer.cpp0000644000000000000000000000013112247075736024420 0ustar rootroot00000000000000// // Copyright (C) 2010 Codership Oy // #include "gu_buffer.hpp" percona-xtradb-cluster-galera/galerautils/src/gu_buffer.hpp0000644000000000000000000000061412247075736024433 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ /*! * Byte buffer class. This is thin wrapper to std::vector */ #ifndef GU_BUFFER_HPP #define GU_BUFFER_HPP #include #include namespace gu { typedef unsigned char byte_t; typedef std::vector Buffer; typedef boost::shared_ptr SharedBuffer; } #endif // GU_BUFFER_HPP percona-xtradb-cluster-galera/galerautils/src/gu_byteswap.h0000644000000000000000000000671112247075736024464 0ustar rootroot00000000000000// Copyright (C) 2012 Codership Oy /** * @file Byte swapping functions/macros * * $Id: gu_byteswap.h 3207 2013-08-21 09:08:51Z vlad $ */ #ifndef _gu_byteswap_h_ #define _gu_byteswap_h_ #include "gu_arch.h" #include "gu_types.h" #include "gu_macros.h" /* * Platform-dependent macros */ #if defined(_MSC_VER) #include #define GU_ROTL32(x,y) _rotl(x,y) #define GU_ROTL64(x,y) _rotl64(x,y) #else /* !defined(_MSC_VER) */ static GU_FORCE_INLINE uint32_t GU_ROTL32 (uint32_t x, int8_t r) { return (x << r) | (x >> (32 - r)); } static GU_FORCE_INLINE uint64_t GU_ROTL64 (uint64_t x, int8_t r) { return (x << r) | (x >> (64 - r)); } #endif /* !defined(_MSC_VER) */ /* * End of paltform-dependent macros */ #if defined(HAVE_BYTESWAP_H) # include // for bswap_16(x), bswap_32(x), bswap_64(x) #elif defined(__APPLE__) # include // for OSSwapInt16(x), etc. #endif /* HAVE_BYTESWAP_H */ #if defined(__APPLE__) /* do not use OSSwapIntXX, because gcc44 gives old-style cast warnings */ # define gu_bswap16 _OSSwapInt16 # define gu_bswap32 _OSSwapInt32 # define gu_bswap64 _OSSwapInt64 #elif defined(__FreeBSD__) /* do not use bswapXX, because gcc44 gives old-style cast warnings */ # define gu_bswap16 __bswap16_var # define gu_bswap32 __bswap32_var # define gu_bswap64 __bswap64_var #elif defined(__sun__) /* BSWAP macros inherited from gu_arch.h */ # define gu_bswap16 BSWAP_16 # define gu_bswap32 BSWAP_32 # define gu_bswap64 BSWAP_64 #elif defined(bswap16) # define gu_bswap16 bswap16 # define gu_bswap32 bswap32 # define gu_bswap64 bswap64 #elif defined(bswap_16) # define gu_bswap16 bswap_16 # define gu_bswap32 bswap_32 # define gu_bswap64 bswap_64 #else # error "No byteswap macros are defined" #endif /* @note: there are inline functions behind these macros below, * so typesafety is taken care of... However C++ still has issues: */ #ifdef __cplusplus // To pacify C++. Not loosing much optimization on 2 bytes anyways. #include #undef gu_bswap16 static GU_FORCE_INLINE uint16_t gu_bswap16(uint16_t const x) // Even though x is declared as 'uint16_t', g++-4.4.1 still treats results // of operations with it as 'int' and freaks out on return with -Wconversion. { return static_cast((x >> 8) | (x << 8)); } #endif // __cplusplus #if defined(GU_LITTLE_ENDIAN) /* convert to/from Little Endian representation */ #define gu_le16(x) (x) #define gu_le32(x) (x) #define gu_le64(x) (x) /* convert to/from Big Endian representation */ #define gu_be16(x) gu_bswap16(x) #define gu_be32(x) gu_bswap32(x) #define gu_be64(x) gu_bswap64(x) #else /* Big-Endian */ /* convert to/from Little Endian representation */ #define gu_le16(x) gu_bswap16(x) #define gu_le32(x) gu_bswap32(x) #define gu_le64(x) gu_bswap64(x) /* convert to/from Big Endian representation */ #define gu_be16(x) (x) #define gu_be32(x) (x) #define gu_be64(x) (x) #endif /* Big-Endian */ /* Analogues to htonl and friends. Since we'll be dealing mostly with * little-endian architectures, there is more sense to use little-endian * as default */ #define htogs(x) gu_le16(x) #define gtohs(x) htogs(x) #define htogl(x) gu_le32(x) #define gtohl(x) htogl(x) /* Analogues to htogs() and friends, suffixed with type width */ #define htog16(x) gu_le16(x) #define gtoh16(x) htog16(x) #define htog32(x) gu_le32(x) #define gtoh32(x) htog32(x) #define htog64(x) gu_le64(x) #define gtoh64(x) htog64(x) #endif /* _gu_byteswap_h_ */ percona-xtradb-cluster-galera/galerautils/src/gu_byteswap.hpp0000644000000000000000000000177412247075736025030 0ustar rootroot00000000000000// Copyright (C) 2012 Codership Oy /** * @file Endian conversion templates for serialization * * $Id: gu_byteswap.hpp 3336 2013-10-28 07:41:56Z teemu $ */ #ifndef _gu_byteswap_hpp_ #define _gu_byteswap_hpp_ #include "gu_byteswap.h" #include namespace gu { /* General template: undefined */ template T gtoh (const T& val) { // to generate error on compilation rather then linking return val.this_template_does_not_support_this_type(); } /* Specialized templates */ template <> GU_FORCE_INLINE uint8_t gtoh (const uint8_t& val) { return val; } template <> GU_FORCE_INLINE uint16_t gtoh (const uint16_t& val) { return gtoh16(val); } template <> GU_FORCE_INLINE uint32_t gtoh (const uint32_t& val) { return gtoh32(val); } template <> GU_FORCE_INLINE uint64_t gtoh (const uint64_t& val) { return gtoh64(val); } template T htog (const T& val) { return gtoh(val); } } /* namespace gu */ #endif /* _gu_byteswap_hpp_ */ percona-xtradb-cluster-galera/galerautils/src/gu_cond.hpp0000644000000000000000000000310612247075736024104 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #ifndef __GU_COND__ #define __GU_COND__ #include #include #include #include "gu_macros.h" #include "gu_exception.hpp" // TODO: make exceptions more verbose namespace gu { class Cond { friend class Lock; // non-copyable Cond(const Cond&); void operator=(const Cond&); protected: pthread_cond_t mutable cond; long mutable ref_count; public: Cond () : cond(), ref_count(0) { pthread_cond_init (&cond, NULL); } ~Cond () { int ret; while (EBUSY == (ret = pthread_cond_destroy(&cond))) { usleep (100); } if (gu_unlikely(ret != 0)) { log_fatal << "pthread_cond_destroy() failed: " << ret << " (" << strerror(ret) << ". Aborting."; ::abort(); } } inline void signal () const { if (ref_count > 0) { int ret = pthread_cond_signal (&cond); if (gu_unlikely(ret != 0)) throw Exception("pthread_cond_signal() failed", ret); } } inline void broadcast () const { if (ref_count > 0) { int ret = pthread_cond_broadcast (&cond); if (gu_unlikely(ret != 0)) throw Exception("pthread_cond_broadcast() failed", ret); } } }; } #endif // __GU_COND__ percona-xtradb-cluster-galera/galerautils/src/gu_conf.h0000644000000000000000000000107212247075736023546 0ustar rootroot00000000000000// Copyright (C) 2007 Codership Oy /** * @file * Configuration interface for libgalerautils * * $Id: gu_conf.h 886 2009-06-25 02:08:03Z alex $ */ #ifndef _gu_conf_h_ #define _gu_conf_h_ /* Logging options */ #include #include "gu_log.h" extern int gu_conf_set_log_file (FILE* file); extern int gu_conf_set_log_callback (gu_log_cb_t callback); extern int gu_conf_self_tstamp_on (); extern int gu_conf_self_tstamp_off (); extern int gu_conf_debug_on (); extern int gu_conf_debug_off (); #endif // _gu_conf_h_ percona-xtradb-cluster-galera/galerautils/src/gu_config.cpp0000644000000000000000000002355412247075736024432 0ustar rootroot00000000000000// Copyright (C) 2010-2012 Codership Oy /** * @file * Configuration management implementation * * $Id: gu_config.cpp 3038 2013-04-07 19:00:53Z alex $ */ #include "gu_config.h" #include "gu_config.hpp" #include "gu_logger.hpp" #include "gu_assert.hpp" const char gu::Config::PARAM_SEP = ';'; // parameter separator const char gu::Config::KEY_VALUE_SEP = '='; // key-value separator const char gu::Config::ESCAPE = '\\'; // escape symbol void gu::Config::parse (param_map_t& pmap, const std::string& params) { if (0 == params[0]) return; std::vector pv = gu::tokenize (params, PARAM_SEP, ESCAPE); for (size_t i = 0; i < pv.size(); ++i) { std::vector kvv = gu::tokenize (pv[i], KEY_VALUE_SEP, ESCAPE, true); assert(kvv.size() > 0); gu::trim(kvv[0]); const std::string& key = kvv[0]; if (!key.empty()) { if (kvv.size() == 1) { gu_throw_error(EINVAL) <<"Key without value: '" << key <<"' at position '" << i << "' in parameter list."; } if (kvv.size() > 2) { gu_throw_error(EINVAL) <<"More than one value for key '" << key <<"' at '" << pv[i] << "' in parameter list."; } gu::trim(kvv[1]); std::string& value = kvv[1]; param_map_t::iterator pi = pmap.find(key); if (pi != pmap.end()) { log_warn << "Element " << pv[i] << " overwrites previous value" << " '" << pi->second << "' of '" << key << "' by '" << value << "'"; } log_debug << "Found param '" << key << "' = '" << value << "'"; pmap[key] = value; } else if (kvv.size() > 1) { gu_throw_error(EINVAL) << "Empty key at '" << pv[i] << "' in parameter list."; } } } gu::Config::Config() : params_() {} gu::Config::Config (const std::string& params) : params_() { Config::parse (params_, params); } void gu::Config::set_longlong (const std::string& key, long long val) { const char* num_mod = ""; /* Shift preserves sign! */ if (val != 0) { if (!(val & ((1LL << 40) - 1))) { val >>= 40; num_mod = "T"; } if (!(val & ((1 << 30) - 1))) { val >>= 30; num_mod = "G"; } else if (!(val & ((1 << 20) - 1))) { val >>= 20; num_mod = "M"; } else if (!(val & ((1 << 10) - 1))) { val >>= 10; num_mod = "K"; } } std::ostringstream ost; ost << val << num_mod; set (key, ost.str()); } void gu::Config::check_conversion (const char* str, const char* endptr, const char* type) { if (endptr == str || endptr[0] != '\0') { gu_throw_error(EINVAL) << "Invalid value '" << str << "' for " << type << " type."; } } char gu::Config::overflow_char(long long ret) { if (ret >= CHAR_MIN && ret <= CHAR_MAX) return ret; gu_throw_error(ERANGE) << "Value " << ret << " too large for requested type (char)."; } short gu::Config::overflow_short(long long ret) { if (ret >= SHRT_MIN && ret <= SHRT_MAX) return ret; gu_throw_error(ERANGE) << "Value " << ret << " too large for requested type (short)."; } int gu::Config::overflow_int(long long ret) { if (ret >= INT_MIN && ret <= INT_MAX) return ret; gu_throw_error(ERANGE) << "Value " << ret << " too large for requested type (int)."; } std::ostream& gu::operator<<(std::ostream& ost, const gu::Config& c) { const gu::Config::param_map_t& pmap = c.params(); gu::Config::param_map_t::const_iterator pi = pmap.begin(); if (pi != pmap.end()) { ost << pi->first << " = " << pi->second; ++pi; for (; pi != pmap.end(); ++pi) { // ost << "'" << pi->first << "' = '" << pi->second << "'\n"; ost << "; " << pi->first << " = " << pi->second; } } return ost; } gu_config_t* gu_config_create (const char* params) { try { const std::string& ps(params ? params : ""); return (reinterpret_cast(new gu::Config(ps))); } catch (gu::Exception& e) { log_error << "Failed to create configuration object: " << e.what(); return 0; } } void gu_config_destroy (gu_config_t* cnf) { if (cnf) { gu::Config* conf = reinterpret_cast(cnf); delete conf; } else { log_error << "Null configuration object in " << __FUNCTION__; assert (0); } } static long config_check_set_args (gu_config_t* cnf, const char* key, const char* func) { if (cnf && key && key[0] != '\0') return 0; if (!cnf) { log_fatal << "Null configuration object in " << func; } if (!key) { log_fatal << "Null key in " << func; } else if (key[0] == '\0') { log_fatal << "Empty key in " << func; } assert (0); return -EINVAL; } static long config_check_get_args (gu_config_t* cnf, const char* key, const void* val_ptr, const char* func) { if (cnf && key && key[0] != '\0' && val_ptr) return 0; if (!cnf) { log_error << "Null configuration object in " << func; } if (!key) { log_error << "Null key in " << func; } else if (key[0] == '\0') { log_error << "Empty key in " << func; } if (!val_ptr) { log_error << "Null value pointer in " << func; } assert (0); return -EINVAL; } bool gu_config_has (gu_config_t* cnf, const char* key) { if (config_check_set_args (cnf, key, __FUNCTION__)) return false; gu::Config* conf = reinterpret_cast(cnf); return (conf->has (key)); } long gu_config_get_string (gu_config_t* cnf, const char* key, const char** val) { if (config_check_get_args (cnf, key, val, __FUNCTION__)) return -EINVAL; gu::Config* conf = reinterpret_cast(cnf); try { *val = conf->get(key).c_str(); return 0; } catch (gu::NotFound&) { return 1; } } long gu_config_get_int64 (gu_config_t* cnf, const char* key, int64_t* val) { if (config_check_get_args (cnf, key, val, __FUNCTION__)) return -EINVAL; gu::Config* conf = reinterpret_cast(cnf); try { *val = conf->get(key); return 0; } catch (gu::NotFound&) { return 1; } catch (gu::Exception& e) { log_error << "Failed to parse parameter '" << key << "': " << e.what(); return -e.get_errno(); } } long gu_config_get_double (gu_config_t* cnf, const char* key, double* val) { if (config_check_get_args (cnf, key, val, __FUNCTION__)) return -EINVAL; gu::Config* conf = reinterpret_cast(cnf); try { *val = conf->get(key); return 0; } catch (gu::NotFound&) { return 1; } catch (gu::Exception& e) { log_error << "Failed to parse parameter '" << key << "': " << e.what(); return -e.get_errno(); } } long gu_config_get_ptr (gu_config_t* cnf, const char* key, void** val) { if (config_check_get_args (cnf, key, val, __FUNCTION__)) return -EINVAL; gu::Config* conf = reinterpret_cast(cnf); try { *val = conf->get(key); return 0; } catch (gu::NotFound&) { return 1; } catch (gu::Exception& e) { log_error << "Failed to parse parameter '" << key << "': " << e.what(); return -e.get_errno(); } } long gu_config_get_bool (gu_config_t* cnf, const char* key, bool* val) { if (config_check_get_args (cnf, key, val, __FUNCTION__)) return -EINVAL; gu::Config* conf = reinterpret_cast(cnf); try { *val = conf->get(key); return 0; } catch (gu::NotFound&) { return 1; } catch (gu::Exception& e) { log_error << "Failed to parse parameter '" << key << "': " << e.what(); return -e.get_errno(); } } #include void gu_config_set_string (gu_config_t* cnf, const char* key, const char* val) { if (config_check_set_args (cnf, key, __FUNCTION__)) abort(); assert (cnf); gu::Config* conf = reinterpret_cast(cnf); conf->set (key, val); } void gu_config_set_int64 (gu_config_t* cnf, const char* key, int64_t val) { if (config_check_set_args (cnf, key, __FUNCTION__)) abort(); gu::Config* conf = reinterpret_cast(cnf); conf->set (key, val); } void gu_config_set_double (gu_config_t* cnf, const char* key, double val) { if (config_check_set_args (cnf, key, __FUNCTION__)) abort(); gu::Config* conf = reinterpret_cast(cnf); conf->set(key, val); } void gu_config_set_ptr (gu_config_t* cnf, const char* key, const void* val) { if (config_check_set_args (cnf, key, __FUNCTION__)) abort(); gu::Config* conf = reinterpret_cast(cnf); conf->set(key, val); } void gu_config_set_bool (gu_config_t* cnf, const char* key, bool val) { if (config_check_set_args (cnf, key, __FUNCTION__)) abort(); gu::Config* conf = reinterpret_cast(cnf); conf->set(key, val); } ssize_t gu_config_print (gu_config_t* cnf, char* buf, ssize_t buf_len) { std::ostringstream os; os << *(reinterpret_cast(cnf)); const std::string& str = os.str(); strncpy (buf, str.c_str(), buf_len - 1); buf[buf_len - 1] = '\0'; return str.length(); } percona-xtradb-cluster-galera/galerautils/src/gu_config.h0000644000000000000000000000275712247075736024101 0ustar rootroot00000000000000// Copyright (C) 2010 Codership Oy /** * @file * C-interface for configuration management * * $Id: gu_config.h 3369 2013-11-10 18:49:53Z alex $ */ #ifndef _gu_config_h_ #define _gu_config_h_ #include #include #include // for ssize_t #ifdef __cplusplus extern "C" { #endif typedef struct gu_config gu_config_t; gu_config_t* gu_config_create (const char* params); void gu_config_destroy (gu_config_t* cnf); /* Getters return 0 on success, 1 when key not found, negative error code * in case of other errors (conversion failed and such) */ long gu_config_get_string (gu_config_t* cnf, const char* key, const char** val); long gu_config_get_int64 (gu_config_t* cnf, const char* key, int64_t* val); long gu_config_get_double (gu_config_t* cnf, const char* key, double* val); long gu_config_get_ptr (gu_config_t* cnf, const char* key, void** val); long gu_config_get_bool (gu_config_t* cnf, const char* key, bool* val); void gu_config_set_string (gu_config_t* cnf, const char* key, const char* val); void gu_config_set_int64 (gu_config_t* cnf, const char* key, int64_t val); void gu_config_set_double (gu_config_t* cnf, const char* key, double val); void gu_config_set_ptr (gu_config_t* cnf, const char* key, const void* val); void gu_config_set_bool (gu_config_t* cnf, const char* key, bool val); ssize_t gu_config_print (gu_config_t* cnf, char* buf, ssize_t buf_len); #ifdef __cplusplus } #endif #endif /* _gu_config_h_ */ percona-xtradb-cluster-galera/galerautils/src/gu_config.hpp0000644000000000000000000001144312247075736024431 0ustar rootroot00000000000000// Copyright (C) 2010-2012 Codership Oy /** * @file * Configuration management class * * $Id: gu_config.hpp 3369 2013-11-10 18:49:53Z alex $ */ #ifndef _gu_config_hpp_ #define _gu_config_hpp_ #include "gu_string.hpp" #include "gu_exception.hpp" #include "gu_utils.hpp" #include "gu_throw.hpp" #include #include namespace gu { class Config; } extern "C" const char* gu_str2ll (const char* str, long long* ll); class gu::Config { public: static const char PARAM_SEP; // parameter separator static const char KEY_VALUE_SEP; // key-value separator static const char ESCAPE; // escape symbol typedef std::map param_map_t; static void parse (param_map_t& list, const std::string& params); /*! Convert string configuration values to other types. * General template for integers, specialized templates follow below. */ template static inline T from_config (const std::string& value) { const char* str = value.c_str(); long long ret; const char* endptr = gu_str2ll (str, &ret); check_conversion (str, endptr, "integer"); switch (sizeof(T)) { case 1: return overflow_char (ret); case 2: return overflow_short (ret); case 4: return overflow_int (ret); case 8: ; // can't detect an overflow } return ret; } Config (); Config (const std::string& params); bool has (const std::string& key) const { return (params_.find(key) != params_.end()); } void set (const std::string& key, const std::string& value) { params_[key] = value; } void set (const std::string& key, const char* value) { params_[key] = value; } /* General template for integer types */ template void set (const std::string& key, T val) { set_longlong (key, val); } /*! @throws NotFound */ const std::string& get (const std::string& key) const { param_map_t::const_iterator i = params_.find(key); if (i == params_.end()) throw NotFound(); return i->second; } const std::string& get (const std::string& key, const std::string& def) const { try { return get(key); } catch (NotFound&) { return def ; } } /*! @throws NotFound */ template inline T get (const std::string& key) const { return from_config (get(key)); } template inline T get(const std::string& key, const T& def) const { try { return get(key); } catch (NotFound&) { return def; } } const param_map_t& params () const { return params_; } private: static void check_conversion (const char* ptr, const char* endptr, const char* type); static char overflow_char(long long ret); static short overflow_short(long long ret); static int overflow_int(long long ret); void set_longlong (const std::string& key, long long value); param_map_t params_; }; extern "C" const char* gu_str2dbl (const char* str, double* dbl); extern "C" const char* gu_str2bool (const char* str, bool* bl); extern "C" const char* gu_str2ptr (const char* str, void** ptr); namespace gu { std::ostream& operator<<(std::ostream&, const gu::Config&); /*! Specialized templates for "funny" types */ template <> inline double Config::from_config (const std::string& value) { const char* str = value.c_str(); double ret; const char* endptr = gu_str2dbl (str, &ret); check_conversion (str, endptr, "double"); return ret; } template <> inline bool Config::from_config (const std::string& value) { const char* str = value.c_str(); bool ret; const char* endptr = gu_str2bool (str, &ret); check_conversion (str, endptr, "boolean"); return ret; } template <> inline void* Config::from_config (const std::string& value) { const char* str = value.c_str(); void* ret; const char* endptr = gu_str2ptr (str, &ret); check_conversion (str, endptr, "pointer"); return ret; } template <> inline void Config::set (const std::string& key, const void* value) { set (key, to_string(value)); } template <> inline void Config::set (const std::string& key, double val) { set (key, to_string(val)); } template <> inline void Config::set (const std::string& key, bool val) { const char* val_str(val ? "YES" : "NO"); // YES/NO is most generic set (key, val_str); } } #endif /* _gu_config_hpp_ */ percona-xtradb-cluster-galera/galerautils/src/gu_convert.hpp0000644000000000000000000000703412247075736024645 0ustar rootroot00000000000000// Copyright (C) 2009 Codership Oy /** * @file Routines for safe integer conversion * * $Id: gu_convert.hpp 3336 2013-10-28 07:41:56Z teemu $ */ #ifndef _gu_convert_hpp_ #define _gu_convert_hpp_ #include "gu_macros.h" #include "gu_throw.hpp" #include namespace gu { /*! * Converts from type FROM to type TO with range checking. * Generic template is for the case sizeof(FROM) > sizeof(TO). * * @param from value to convert * @param to destination (provides type TO for template instantiation) * @return value cast to TO */ template inline TO convert (const FROM& from, const TO& to) { if (gu_unlikely(from > std::numeric_limits::max() || from < std::numeric_limits::min())) { // @todo: figure out how to print type name without RTTI gu_throw_error (ERANGE) << from << " is unrepresentable with " << (std::numeric_limits::is_signed ? "signed" : "unsigned") << " " << sizeof(TO) << " bytes."; } return static_cast(from); } /* Specialized templates are for signed conversion */ template <> inline long long convert (const unsigned long long& from, const long long& to) { if (gu_unlikely(from > static_cast (std::numeric_limits::max()))) { gu_throw_error (ERANGE) << from << " is unrepresentable with 'long long'"; } return static_cast(from); } template <> inline unsigned long long convert (const long long& from, const unsigned long long& to) { if (gu_unlikely(from < 0)) { gu_throw_error (ERANGE) << from << " is unrepresentable with 'unsigned long long'"; } return static_cast(from); } template <> inline long convert (const unsigned long& from, const long& to) { if (gu_unlikely(from > static_cast (std::numeric_limits::max()))) { gu_throw_error (ERANGE) << from << " is unrepresentable with 'long'"; } return static_cast(from); } template <> inline unsigned long convert (const long& from, const unsigned long& to) { if (gu_unlikely(from < 0)) { gu_throw_error (ERANGE) << from << " is unrepresentable with 'unsigned long'"; } return static_cast(from); } template <> inline int convert (const unsigned int& from, const int& to) { if (gu_unlikely(from > static_cast (std::numeric_limits::max()))) { gu_throw_error (ERANGE) << from << " is unrepresentable with 'long'"; } return static_cast(from); } template <> inline unsigned int convert (const int& from, const unsigned int& to) { if (gu_unlikely(from < 0)) { gu_throw_error (ERANGE) << from << " is unrepresentable with 'unsigned long'"; } return static_cast(from); } } #endif /* _gu_convert_hpp_ */ percona-xtradb-cluster-galera/galerautils/src/gu_datetime.cpp0000644000000000000000000000550712247075736024757 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy * * $Id$ */ #include "gu_datetime.hpp" #include "gu_logger.hpp" #include "gu_utils.hpp" extern "C" { #include "gu_time.h" } std::ostream& gu::datetime::operator<<(std::ostream& os, const Date& d) { os << d.get_utc(); return os; } std::ostream& gu::datetime::operator<<(std::ostream& os, const Period& p) { os << "P"; int64_t nsecs(p.get_nsecs()); if (nsecs/Year > 0) { os << (nsecs/Year) << "Y"; nsecs %= Year; } if (nsecs/Month > 0) { os << (nsecs/Month) << "M"; nsecs %= Month; } if (nsecs/Day > 0) { os << (nsecs/Day) << "D"; nsecs %= Day; } if (nsecs > 0) { os << "T"; } if (nsecs/Hour > 0) { os << (nsecs/Hour) << "H"; nsecs %= Hour; } if (nsecs/Min > 0) { os << (nsecs/Min) << "M"; nsecs %= Min; } if (double(nsecs)/Sec >= 1.e-9) { os << (double(nsecs)/Sec) << "S"; } return os; } void gu::datetime::Date::parse(const std::string& str) { if (str == "") { return; } gu_throw_fatal << "not implemented"; } const char* const gu::datetime::Period::period_regex = "^(P)(([0-9]+)Y)?(([0-9]+)M)?(([0-9]+)D)?" /* 1 23 45 67 */ "((T)?(([0-9]+)H)?(([0-9]+)M)?(([0-9]+)(\\.([0-9]+))?S)?)?"; /* 89 11 13 15 16 */ enum { GU_P = 1, GU_YEAR = 3, GU_MONTH = 5, GU_DAY = 7, GU_HOUR = 10, GU_MIN = 12, GU_SEC = 15, GU_SEC_D = 16, GU_NUM_PARTS = 17 }; gu::RegEx const gu::datetime::Period::regex(period_regex); void gu::datetime::Period::parse(const std::string& str) { std::vector parts = regex.match(str, GU_NUM_PARTS); if (parts[GU_P].is_set() == false) { if (str == "") { return; } else { gu_throw_error (EINVAL) << "Period " << str << " not valid"; } } if (parts[GU_YEAR].is_set()) { nsecs += from_string(parts[GU_YEAR].str())*Year; } if (parts[GU_MONTH].is_set()) { nsecs += from_string(parts[GU_MONTH].str())*Month; } if (parts[GU_DAY].is_set()) { nsecs += from_string(parts[GU_DAY].str())*Day; } if (parts[GU_HOUR].is_set()) { nsecs += from_string(parts[GU_HOUR].str())*Hour; } if (parts[GU_MIN].is_set()) { nsecs += from_string(parts[GU_MIN].str())*Min; } if (parts[GU_SEC].is_set()) { long long s(from_string(parts[GU_SEC].str())); nsecs += s*Sec; } if (parts[GU_SEC_D].is_set()) { double d(from_string(parts[GU_SEC_D].str())); nsecs += static_cast(d*Sec); } } percona-xtradb-cluster-galera/galerautils/src/gu_datetime.hpp0000644000000000000000000001420612247075736024760 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy * * $Id$ */ /*! * @file Date/time manipulation classes providing nanosecond resolution. */ #ifndef __GU_DATETIME__ #define __GU_DATETIME__ #include "gu_exception.hpp" #include "gu_regex.hpp" #include "gu_time.h" #include #include #include namespace gu { namespace datetime { /* Multiplier constants */ const long long NSec = 1; const long long USec = 1000*NSec; const long long MSec = 1000*USec; const long long Sec = 1000*MSec; const long long Min = 60*Sec; const long long Hour = 60*Min; const long long Day = 24*Hour; const long long Month = 30*Day; const long long Year = 12*Month; /*! * @brief Class representing time periods instead of * system clock time. */ class Period { public: /*! * @brief Constructor * * Duration format is PnYnMnDTnHnMnS where Y is year, M is month, * D is day, T is the time designator separating date and time * parts, H denotes hours, M (after T) is minutes and S seconds. * * All other n:s are expected to be integers except the one * before S which can be decimal to represent fractions of second. * * @param str Time period represented in ISO8601 duration format. */ Period(const std::string& str = "") : nsecs() { if (str != "") parse(str); } Period(const long long nsecs_) : nsecs(nsecs_) { } static Period min() { return 0; } static Period max() { return std::numeric_limits::max();} bool operator==(const Period& cmp) const { return (nsecs == cmp.nsecs); } bool operator<(const Period& cmp) const { return (nsecs < cmp.nsecs); } Period operator+(const long long add) const { return (nsecs + add); } Period operator-(const long long dec) const { return (nsecs - dec); } Period operator*(const long long mul) const { return (nsecs*mul); } Period operator/(const long long div) const { return (nsecs/div); } long long get_nsecs() const { return nsecs; } private: friend class Date; friend std::istream& operator>>(std::istream&, Period&); static const char* const period_regex; /*! regexp string */ static RegEx const regex; /*! period string parser */ /*! * @brief Parse period string. */ void parse(const std::string&); long long nsecs; }; /*! * @brief Date/time representation. * * @todo Parsing date from string is not implemented yet, * only possible to get current system time or * maximum time. */ class Date { public: /*! * @brief Get system time. * @note This call should be deprecated in favor of calendar() * and monotonic(). */ static inline Date now() { return gu_time_monotonic(); } /*! * @brief Get time from system-wide realtime clock. */ static inline Date calendar() { return gu_time_calendar(); } /*! * @brief Get time from monotonic clock. */ static inline Date monotonic() { return gu_time_monotonic(); } /*! * @brief Get maximum representable timestamp. */ static inline Date max() { return std::numeric_limits::max(); } /*! * @brief Get zero time */ static inline Date zero() { return 0; } /*! * Return 64-bit timestamp representing system time in nanosecond * resolution. */ long long get_utc() const { return utc; } /* Standard comparision operators */ bool operator==(const Date cmp) const { return (utc == cmp.utc); } bool operator<(const Date cmp) const { return (utc < cmp.utc); } /*! * @brief Add period to Date */ Date operator+(const Period& add) const { return (utc + add.get_nsecs()); } /*! * @brief Decrement period from Date */ Date operator-(const Period& dec) const { return (utc - dec.get_nsecs()); } Period operator-(const Date& dec) const { return (utc - dec.utc); } Date(const long long utc_ = 0) : utc(utc_) { } /*! convert to timespec - for internal use */ void _timespec(timespec& ts) const { ts.tv_sec = utc / 1000000000L; ts.tv_nsec = utc % 1000000000L; } private: long long utc; /*!< System time in nanosecond precision */ /*! * @brief Parse date from string. * @todo Not implemented yet */ void parse(const std::string& str_); }; /*! * @brief Output operator for Date class. * @todo Not implemented yet */ std::ostream& operator<<(std::ostream&, const Date&); /*! * @brief Output operator for Period type. */ std::ostream& operator<<(std::ostream&, const Period&); inline std::string to_string(const Period& p) { std::ostringstream os; os << p; return os.str(); } inline std::istream& operator>>(std::istream& is, Period& p) { std::string str; is >> str; p.parse(str); return is; } } // namespace datetime } // namespace gu #endif // __GU_DATETIME__ percona-xtradb-cluster-galera/galerautils/src/gu_dbug.c0000644000000000000000000015143512247075736023546 0ustar rootroot00000000000000/****************************************************************************** * * * N O T I C E * * * * Copyright Abandoned, 1987, Fred Fish * * * * * * This previously copyrighted work has been placed into the public * * domain by the author and may be freely used for any purpose, * * private or commercial. * * * * Because of the number of inquiries I was receiving about the use * * of this product in commercially developed works I have decided to * * simply make it public domain to further its unrestricted use. I * * specifically would be most happy to see this material become a * * part of the standard Unix distributions by AT&T and the Berkeley * * Computer Science Research Group, and a standard part of the GNU * * system from the Free Software Foundation. * * * * I would appreciate it, as a courtesy, if this notice is left in * * all copies and derivative works. Thank you. * * * * The author makes no warranty of any kind with respect to this * * product and explicitly disclaims any implied warranties of mer- * * chantability or fitness for any particular purpose. * * * ****************************************************************************** */ /* * FILE * * dbug.c runtime support routines for dbug package * * SCCS * * @(#)dbug.c 1.25 7/25/89 * * DESCRIPTION * * These are the runtime support routines for the dbug package. * The dbug package has two main components; the user include * file containing various macro definitions, and the runtime * support routines which are called from the macro expansions. * * Externally visible functions in the runtime support module * use the naming convention pattern "_db_xx...xx_", thus * they are unlikely to collide with user defined function names. * * AUTHOR(S) * * Fred Fish (base code) * Enhanced Software Technologies, Tempe, AZ * asuvax!mcdphx!estinc!fnf * * Binayak Banerjee (profiling enhancements) * seismo!bpa!sjuvax!bbanerje * * Michael Widenius: * DBUG_DUMP - To dump a pice of memory. * PUSH_FLAG "O" - To be used insted of "o" if we don't * want flushing (for slow systems) * PUSH_FLAG "A" - as 'O', but we will append to the out file instead * of creating a new one. * Check of malloc on entry/exit (option "S") * * Alexey Yurchenko: * - Renamed global symbols for use with galera project to avoid * collisions with other software (notably MySQL) * * Teemu Ollakka: * - Slight cleanups, removed some MySQL dependencies. * - All global variables should now have _gu_db prefix. * - Thread -> state mapping for multithreaded programs. * - Changed initialization so that it is done on the first * call to _gu_db_push(). * * $Id: gu_dbug.c 3348 2013-11-02 10:56:57Z alex $ */ #include #include #include #include #include #include #ifndef GU_DBUG_ON #define GU_DBUG_ON #endif #include "gu_dbug.h" /* Make a new type: bool_t */ typedef enum { FALSE = (0 != 0), TRUE = (!FALSE) } bool_t; #define _VARARGS(X) X #define FN_LIBCHAR 1024 #define FN_REFLEN 1024 #define NullS "" #include #if defined(MSDOS) || defined(__WIN__) #include #endif #ifdef _GU_DBUG_CONDITION_ #define _GU_DBUG_START_CONDITION_ "d:t" #else #define _GU_DBUG_START_CONDITION_ "" #endif /* * Manifest constants that should not require any changes. */ #define EOS '\000' /* End Of String marker */ /* * Manifest constants which may be "tuned" if desired. */ #define PRINTBUF 1024 /* Print buffer size */ #define INDENT 2 /* Indentation per trace level */ #define MAXDEPTH 200 /* Maximum trace depth default */ /* * The following flags are used to determine which * capabilities the user has enabled with the state * push macro. */ #define TRACE_ON 000001 /* Trace enabled */ #define DEBUG_ON 000002 /* Debug enabled */ #define FILE_ON 000004 /* File name print enabled */ #define LINE_ON 000010 /* Line number print enabled */ #define DEPTH_ON 000020 /* Function nest level print enabled */ #define PROCESS_ON 000040 /* Process name print enabled */ #define NUMBER_ON 000100 /* Number each line of output */ #define PROFILE_ON 000200 /* Print out profiling code */ #define PID_ON 000400 /* Identify each line with process id */ #define SANITY_CHECK_ON 001000 /* Check my_malloc on GU_DBUG_ENTER */ #define FLUSH_ON_WRITE 002000 /* Flush on every write */ #define TRACING (_gu_db_stack -> flags & TRACE_ON) #define DEBUGGING (_gu_db_stack -> flags & DEBUG_ON) #define PROFILING (_gu_db_stack -> flags & PROFILE_ON) #define STREQ(a,b) (strcmp(a,b) == 0) #define min(a,b) ((a) < (b) ? (a) : (b)) #define max(a,b) ((a) > (b) ? (a) : (b)) /* * Typedefs to make things more obvious.??? */ #ifndef __WIN__ typedef int BOOLEAN; #else #define BOOLEAN BOOL #endif /* * Make it easy to change storage classes if necessary. */ #define IMPORT extern /* Names defined externally */ #define EXPORT /* Allocated here, available globally */ #define AUTO auto /* Names to be allocated on stack */ #define REGISTER register /* Names to be placed in registers */ /* * The default file for profiling. Could also add another flag * (G?) which allowed the user to specify this. * * If the automatic variables get allocated on the stack in * reverse order from their declarations, then define AUTOS_REVERSE. * This is used by the code that keeps track of stack usage. For * forward allocation, the difference in the dbug frame pointers * represents stack used by the callee function. For reverse allocation, * the difference represents stack used by the caller function. * */ #define PROF_FILE "dbugmon.out" #define PROF_EFMT "E\t%ld\t%s\n" #define PROF_SFMT "S\t%lx\t%lx\t%s\n" #define PROF_XFMT "X\t%ld\t%s\n" #ifdef M_I386 /* predefined by xenix 386 compiler */ #define AUTOS_REVERSE 1 #endif /* * Variables which are available externally but should only * be accessed via the macro package facilities. */ FILE *_gu_db_fp_ = (FILE*) 0; /* Output stream, default stderr */ char *_gu_db_process_ = (char*) "dbug"; /* Pointer to process name; argv[0] */ FILE *_gu_db_pfp_ = (FILE*) 0; /* Profile stream, 'dbugmon.out' */ BOOLEAN _gu_db_on_ = FALSE; /* TRUE if debugging currently on */ BOOLEAN _gu_db_pon_ = FALSE; /* TRUE if profile currently on */ BOOLEAN _gu_no_db_ = TRUE; /* TRUE if no debugging at all */ /* * Externally supplied functions. */ IMPORT int _sanity(const char *file, uint line); /* * The user may specify a list of functions to trace or * debug. These lists are kept in a linear linked list, * a very simple implementation. */ struct link { char *str; /* Pointer to link's contents */ struct link *next_link; /* Pointer to the next link */ }; /* * Debugging states can be pushed or popped off of a * stack which is implemented as a linked list. Note * that the head of the list is the current state and the * stack is pushed by adding a new state to the head of the * list or popped by removing the first link. */ struct state { int flags; /* Current state flags */ int maxdepth; /* Current maximum trace depth */ uint delay; /* Delay after each output line */ int sub_level; /* Sub this from code_state->level */ FILE* out_file; /* Current output stream */ FILE* prof_file; /* Current profiling stream */ char name[FN_REFLEN]; /* Name of output file */ struct link* functions; /* List of functions */ struct link* p_functions; /* List of profiled functions */ struct link* keywords; /* List of debug keywords */ struct link* processes; /* List of process names */ struct state* next_state; /* Next state in the list */ }; /* * Local variables not seen by user. */ static struct state* _gu_db_stack = 0; typedef struct st_code_state { int lineno; /* Current debugger output line number */ int level; /* Current function nesting level */ const char* func; /* Name of current user function */ const char* file; /* Name of current user file */ char** framep; /* Pointer to current frame */ int jmplevel; /* Remember nesting level at setjmp () */ const char* jmpfunc; /* Remember current function for setjmp */ const char* jmpfile; /* Remember current file for setjmp */ /* * The following variables are used to hold the state information * between the call to _gu_db_pargs_() and _gu_db_doprnt_(), during * expansion of the GU_DBUG_PRINT macro. This is the only macro * that currently uses these variables. * * These variables are currently used only by _gu_db_pargs_() and * _gu_db_doprnt_(). */ uint u_line; /* User source code line number */ const char* u_keyword; /* Keyword for current macro */ int locked; /* If locked with _gu_db_lock_file */ } CODE_STATE; /* Parse a debug command string */ static struct link *ListParse(char *ctlp); /* Make a fresh copy of a string */ static char *StrDup(const char *str); /* Open debug output stream */ static void GU_DBUGOpenFile(const char *name, int append); #ifndef THREAD /* Open profile output stream */ static FILE *OpenProfile(const char *name); /* Profile if asked for it */ static BOOLEAN DoProfile(void); /* Return current user time (ms) */ static unsigned long Clock(void); #endif /* Close debug output stream */ static void CloseFile(FILE * fp); /* Push current debug state */ static void PushState(void); /* Test for tracing enabled */ static BOOLEAN DoTrace(CODE_STATE * state); /* Test to see if file is writable */ #if !(!defined(HAVE_ACCESS) || defined(MSDOS)) static BOOLEAN Writable(char *pathname); /* Change file owner and group */ static void ChangeOwner(char *pathname); /* Allocate memory for runtime support */ #endif static char *DbugMalloc(int size); /* Remove leading pathname components */ static char *BaseName(const char *pathname); static void DoPrefix(uint line); static void FreeList(struct link *linkp); static void Indent(int indent); static BOOLEAN InList(struct link *linkp, const char *cp); static void dbug_flush(CODE_STATE *); static void DbugExit(const char *why); static int DelayArg(int value); /* Supplied in Sys V runtime environ */ /* Break string into tokens */ static char *static_strtok(char *s1, char chr); /* * Miscellaneous printf format strings. */ #define ERR_MISSING_RETURN "%s: missing GU_DBUG_RETURN or GU_DBUG_VOID_RETURN macro in function \"%s\"\n" #define ERR_OPEN "%s: can't open debug output stream \"%s\": " #define ERR_CLOSE "%s: can't close debug file: " #define ERR_ABORT "%s: debugger aborting because %s\n" #define ERR_CHOWN "%s: can't change owner/group of \"%s\": " /* * Macros and defines for testing file accessibility under UNIX and MSDOS. */ #undef EXISTS #if !defined(HAVE_ACCESS) || defined(MSDOS) #define EXISTS(pathname) (FALSE) /* Assume no existance */ #define Writable(name) (TRUE) #else #define EXISTS(pathname) (access (pathname, F_OK) == 0) #define WRITABLE(pathname) (access (pathname, W_OK) == 0) #endif #ifndef MSDOS #define ChangeOwner(name) #endif /* * Translate some calls among different systems. */ #if defined(unix) || defined(xenix) || defined(VMS) || defined(__NetBSD__) # define Delay(A) sleep((uint) A) #elif defined(AMIGA) IMPORT int Delay(); /* Pause for given number of ticks */ #else static int Delay(int ticks); #endif /* ** Macros to allow dbugging with threads */ #ifdef THREAD #include pthread_once_t _gu_db_once = PTHREAD_ONCE_INIT; pthread_mutex_t _gu_db_mutex = PTHREAD_MUTEX_INITIALIZER; struct state_map { pthread_t th; CODE_STATE *state; struct state_map *prev; struct state_map *next; }; #define _GU_DB_STATE_MAP_BUCKETS (1 << 7) static struct state_map *_gu_db_state_map[_GU_DB_STATE_MAP_BUCKETS]; /* * This hash is probably good enough. Golden ratio 2654435761U from * http://www.concentric.net/~Ttwang/tech/inthash.htm * * UPDATE: it is good enough for input with significant variation in * 32 lower bits. */ static inline unsigned long pt_hash(const pthread_t th) { unsigned long k = (unsigned long)th; uint64_t ret = 2654435761U * k; // since we're returning a masked hash key, all considerations // for "reversibility" can be dropped. Instead we can help // higher input bits influence lower output bits. XOR rules. return (ret ^ (ret >> 32)) & (_GU_DB_STATE_MAP_BUCKETS - 1); } static CODE_STATE *state_map_find(const pthread_t th) { unsigned int key = pt_hash(th); struct state_map *sm = _gu_db_state_map[key]; while (sm && sm->th != th) sm = sm->next; return sm ? sm->state : NULL; } void state_map_insert(const pthread_t th, CODE_STATE *state) { unsigned int key; struct state_map *sm; assert(state_map_find(th) == NULL); key = pt_hash(th); sm = malloc(sizeof(struct state_map)); sm->state = state; sm->th = th; pthread_mutex_lock(&_gu_db_mutex); sm->prev = NULL; sm->next = _gu_db_state_map[key]; if (sm->next) sm->next->prev = sm; _gu_db_state_map[key] = sm; pthread_mutex_unlock(&_gu_db_mutex); } void state_map_erase(const pthread_t th) { unsigned int key; struct state_map *sm; key = pt_hash(th); sm = _gu_db_state_map[key]; while (sm && sm->th != th) sm = sm->next; assert(sm); pthread_mutex_lock(&_gu_db_mutex); if (sm->prev) { sm->prev->next = sm->next; } else { assert(_gu_db_state_map[key] == sm); _gu_db_state_map[key] = sm->next; } if (sm->next) sm->next->prev = sm->prev; pthread_mutex_unlock(&_gu_db_mutex); free(sm); } static CODE_STATE * code_state(void) { CODE_STATE *state = 0; if ((state = state_map_find(pthread_self())) == NULL) { state = malloc(sizeof(CODE_STATE)); memset(state, 0, sizeof(CODE_STATE)); state->func = "?func"; state->file = "?file"; state->u_keyword = "?"; state_map_insert(pthread_self(), state); } return state; } static void code_state_cleanup(CODE_STATE *state) { if (state->level == 0) { state_map_erase(pthread_self()); free(state); } } static void _gu_db_init() { if (!_gu_db_fp_) _gu_db_fp_ = stderr; /* Output stream, default stderr */ memset(_gu_db_state_map, 0, sizeof(_gu_db_state_map)); } #else /* !THREAD */ #define _gu_db_init() #define code_state() (&static_code_state) #define code_state_cleanup(A) do {} while (0) #define pthread_mutex_lock(A) {} #define pthread_mutex_unlock(A) {} static CODE_STATE static_code_state = { 0, 0, "?func", "?file", NULL, 0, NULL, NULL, 0, "?", 0 }; #endif /* * FUNCTION * * _gu_db_push_ push current debugger state and set up new one * * SYNOPSIS * * VOID _gu_db_push_ (control) * char *control; * * DESCRIPTION * * Given pointer to a debug control string in "control", pushes * the current debug state, parses the control string, and sets * up a new debug state. * * The only attribute of the new state inherited from the previous * state is the current function nesting level. This can be * overridden by using the "r" flag in the control string. * * The debug control string is a sequence of colon separated fields * as follows: * * ::...: * * Each field consists of a mandatory flag character followed by * an optional "," and comma separated list of modifiers: * * flag[,modifier,modifier,...,modifier] * * The currently recognized flag characters are: * * d Enable output from GU_DBUG_ macros for * for the current state. May be followed * by a list of keywords which selects output * only for the GU_DBUG macros with that keyword. * A null list of keywords implies output for * all macros. * * D Delay after each debugger output line. * The argument is the number of tenths of seconds * to delay, subject to machine capabilities. * I.E. -#D,20 is delay two seconds. * * f Limit debugging and/or tracing, and profiling to the * list of named functions. Note that a null list will * disable all functions. The appropriate "d" or "t" * flags must still be given, this flag only limits their * actions if they are enabled. * * F Identify the source file name for each * line of debug or trace output. * * i Identify the process with the pid for each line of * debug or trace output. * * g Enable profiling. Create a file called 'dbugmon.out' * containing information that can be used to profile * the program. May be followed by a list of keywords * that select profiling only for the functions in that * list. A null list implies that all functions are * considered. * * L Identify the source file line number for * each line of debug or trace output. * * n Print the current function nesting depth for * each line of debug or trace output. * * N Number each line of dbug output. * * o Redirect the debugger output stream to the * specified file. The default output is stderr. * * O As O but the file is really flushed between each * write. When neaded the file is closed and reopened * between each write. * * p Limit debugger actions to specified processes. * A process must be identified with the * GU_DBUG_PROCESS macro and match one in the list * for debugger actions to occur. * * P Print the current process name for each * line of debug or trace output. * * r When pushing a new state, do not inherit * the previous state's function nesting level. * Useful when the output is to start at the * left margin. * * S Do function _sanity(_file_,_line_) at each * debugged function until _sanity() returns * something that differs from 0. * (Moustly used with my_malloc) * * t Enable function call/exit trace lines. * May be followed by a list (containing only * one modifier) giving a numeric maximum * trace level, beyond which no output will * occur for either debugging or tracing * macros. The default is a compile time * option. * * Some examples of debug control strings which might appear * on a shell command line (the "-#" is typically used to * introduce a control string to an application program) are: * * -#d:t * -#d:f,main,subr1:F:L:t,20 * -#d,input,output,files:n * * For convenience, any leading "-#" is stripped off. * */ void _gu_db_push_(const char *control) { register char *scan; register struct link *temp; CODE_STATE *state; char *new_str; pthread_once(&_gu_db_once, &_gu_db_init); if (control && *control == '-') { if (*++control == '#') control++; } if (*control) _gu_no_db_ = FALSE; /* We are using dbug after all */ else return; new_str = StrDup(control); PushState(); state = code_state(); scan = static_strtok(new_str, ':'); for (; scan != NULL; scan = static_strtok((char *) NULL, ':')) { switch (*scan++) { case 'd': _gu_db_on_ = TRUE; _gu_db_stack->flags |= DEBUG_ON; if (*scan++ == ',') { _gu_db_stack->keywords = ListParse(scan); } break; case 'D': _gu_db_stack->delay = 0; if (*scan++ == ',') { temp = ListParse(scan); _gu_db_stack->delay = DelayArg(atoi(temp->str)); FreeList(temp); } break; case 'f': if (*scan++ == ',') { _gu_db_stack->functions = ListParse(scan); } break; case 'F': _gu_db_stack->flags |= FILE_ON; break; case 'i': _gu_db_stack->flags |= PID_ON; break; #ifndef THREAD case 'g': _gu_db_pon_ = TRUE; if (OpenProfile(PROF_FILE)) { _gu_db_stack->flags |= PROFILE_ON; if (*scan++ == ',') _gu_db_stack->p_functions = ListParse(scan); } break; #endif case 'L': _gu_db_stack->flags |= LINE_ON; break; case 'n': _gu_db_stack->flags |= DEPTH_ON; break; case 'N': _gu_db_stack->flags |= NUMBER_ON; break; case 'A': case 'O': _gu_db_stack->flags |= FLUSH_ON_WRITE; case 'a': case 'o': if (*scan++ == ',') { temp = ListParse(scan); GU_DBUGOpenFile(temp->str, (int) (scan[-2] == 'A' || scan[-2] == 'a')); FreeList(temp); } else { GU_DBUGOpenFile("-", 0); } break; case 'p': if (*scan++ == ',') { _gu_db_stack->processes = ListParse(scan); } break; case 'P': _gu_db_stack->flags |= PROCESS_ON; break; case 'r': _gu_db_stack->sub_level = state->level; break; case 't': _gu_db_stack->flags |= TRACE_ON; if (*scan++ == ',') { temp = ListParse(scan); _gu_db_stack->maxdepth = atoi(temp->str); FreeList(temp); } break; case 'S': _gu_db_stack->flags |= SANITY_CHECK_ON; break; } } free(new_str); } /* * FUNCTION * * _gu_db_pop_ pop the debug stack * * DESCRIPTION * * Pops the debug stack, returning the debug state to its * condition prior to the most recent _gu_db_push_ invocation. * Note that the pop will fail if it would remove the last * valid state from the stack. This prevents user errors * in the push/pop sequence from screwing up the debugger. * Maybe there should be some kind of warning printed if the * user tries to pop too many states. * */ void _gu_db_pop_() { register struct state *discard; discard = _gu_db_stack; if (discard != NULL && discard->next_state != NULL) { _gu_db_stack = discard->next_state; _gu_db_fp_ = _gu_db_stack->out_file; _gu_db_pfp_ = _gu_db_stack->prof_file; if (discard->keywords != NULL) { FreeList(discard->keywords); } if (discard->functions != NULL) { FreeList(discard->functions); } if (discard->processes != NULL) { FreeList(discard->processes); } if (discard->p_functions != NULL) { FreeList(discard->p_functions); } CloseFile(discard->out_file); if (discard->prof_file) CloseFile(discard->prof_file); free((char *) discard); if (!(_gu_db_stack->flags & DEBUG_ON)) _gu_db_on_ = 0; } else { _gu_db_on_ = 0; } } /* * FUNCTION * * _gu_db_enter_ process entry point to user function * * SYNOPSIS * * VOID _gu_db_enter_ (_func_, _file_, _line_, * _sfunc_, _sfile_, _slevel_, _sframep_) * char *_func_; points to current function name * char *_file_; points to current file name * int _line_; called from source line number * char **_sfunc_; save previous _func_ * char **_sfile_; save previous _file_ * int *_slevel_; save previous nesting level * char ***_sframep_; save previous frame pointer * * DESCRIPTION * * Called at the beginning of each user function to tell * the debugger that a new function has been entered. * Note that the pointers to the previous user function * name and previous user file name are stored on the * caller's stack (this is why the ENTER macro must be * the first "executable" code in a function, since it * allocates these storage locations). The previous nesting * level is also stored on the callers stack for internal * self consistency checks. * * Also prints a trace line if tracing is enabled and * increments the current function nesting depth. * * Note that this mechanism allows the debugger to know * what the current user function is at all times, without * maintaining an internal stack for the function names. * */ void _gu_db_enter_(const char *_func_, const char *_file_, uint _line_, const char **_sfunc_, const char **_sfile_, uint * _slevel_, char ***_sframep_ __attribute__ ((unused))) { register CODE_STATE *state; if (!_gu_no_db_) { int save_errno = errno; state = code_state(); *_sfunc_ = state->func; *_sfile_ = state->file; state->func = (char *) _func_; state->file = (char *) _file_; /* BaseName takes time !! */ *_slevel_ = ++state->level; #ifndef THREAD *_sframep_ = state->framep; state->framep = (char **) _sframep_; if (DoProfile()) { long stackused; if (*state->framep == NULL) { stackused = 0; } else { stackused = ((long) (*state->framep)) - ((long) (state->framep)); stackused = stackused > 0 ? stackused : -stackused; } (void) fprintf(_gu_db_pfp_, PROF_EFMT, Clock(), state->func); #ifdef AUTOS_REVERSE (void) fprintf(_gu_db_pfp_, PROF_SFMT, state->framep, stackused, *_sfunc_); #else (void) fprintf(_gu_db_pfp_, PROF_SFMT, (ulong) state->framep, stackused, state->func); #endif (void) fflush(_gu_db_pfp_); } #endif if (DoTrace(state)) { if (!state->locked) pthread_mutex_lock(&_gu_db_mutex); DoPrefix(_line_); Indent(state->level); (void) fprintf(_gu_db_fp_, ">%s\n", state->func); dbug_flush(state); /* This does a unlock */ } #ifdef SAFEMALLOC if (_gu_db_stack->flags & SANITY_CHECK_ON) if (_sanity(_file_, _line_)) /* Check of my_malloc */ _gu_db_stack->flags &= ~SANITY_CHECK_ON; #endif errno = save_errno; } } /* * FUNCTION * * _gu_db_return_ process exit from user function * * SYNOPSIS * * VOID _gu_db_return_ (_line_, _sfunc_, _sfile_, _slevel_) * int _line_; current source line number * char **_sfunc_; where previous _func_ is to be retrieved * char **_sfile_; where previous _file_ is to be retrieved * int *_slevel_; where previous level was stashed * * DESCRIPTION * * Called just before user function executes an explicit or implicit * return. Prints a trace line if trace is enabled, decrements * the current nesting level, and restores the current function and * file names from the defunct function's stack. * */ void _gu_db_return_(uint _line_, const char **_sfunc_, const char **_sfile_, uint * _slevel_) { CODE_STATE *state; if (!_gu_no_db_) { int save_errno = errno; if (!(state = code_state())) return; /* Only happens at end of program */ if (_gu_db_stack->flags & (TRACE_ON | DEBUG_ON | PROFILE_ON)) { if (!state->locked) pthread_mutex_lock(&_gu_db_mutex); if (state->level != (int) *_slevel_) (void) fprintf(_gu_db_fp_, ERR_MISSING_RETURN, _gu_db_process_, state->func); else { #ifdef SAFEMALLOC if (_gu_db_stack->flags & SANITY_CHECK_ON) if (_sanity(*_sfile_, _line_)) _gu_db_stack->flags &= ~SANITY_CHECK_ON; #endif #ifndef THREAD if (DoProfile()) (void) fprintf(_gu_db_pfp_, PROF_XFMT, Clock(), state->func); #endif if (DoTrace(state)) { DoPrefix(_line_); Indent(state->level); (void) fprintf(_gu_db_fp_, "<%s\n", state->func); } } dbug_flush(state); } state->level = *_slevel_ - 1; state->func = *_sfunc_; state->file = *_sfile_; #ifndef THREAD if (state->framep != NULL) state->framep = (char **) *state->framep; #endif errno = save_errno; code_state_cleanup(state); } } /* * FUNCTION * * _gu_db_pargs_ log arguments for subsequent use by _gu_db_doprnt_() * * SYNOPSIS * * VOID _gu_db_pargs_ (_line_, keyword) * int _line_; * char *keyword; * * DESCRIPTION * * The new universal printing macro GU_DBUG_PRINT, which replaces * all forms of the GU_DBUG_N macros, needs two calls to runtime * support routines. The first, this function, remembers arguments * that are used by the subsequent call to _gu_db_doprnt_(). * */ void _gu_db_pargs_(uint _line_, const char *keyword) { CODE_STATE *state = code_state(); state->u_line = _line_; state->u_keyword = (char *) keyword; } /* * FUNCTION * * _gu_db_doprnt_ handle print of debug lines * * SYNOPSIS * * VOID _gu_db_doprnt_ (format, va_alist) * char *format; * va_dcl; * * DESCRIPTION * * When invoked via one of the GU_DBUG macros, tests the current keyword * set by calling _gu_db_pargs_() to see if that macro has been selected * for processing via the debugger control string, and if so, handles * printing of the arguments via the format string. The line number * of the GU_DBUG macro in the source is found in u_line. * * Note that the format string SHOULD NOT include a terminating * newline, this is supplied automatically. * */ #include void _gu_db_doprnt_(const char *format, ...) { va_list args; CODE_STATE *state; state = code_state(); va_start(args, format); if (_gu_db_keyword_(state->u_keyword)) { int save_errno = errno; if (!state->locked) pthread_mutex_lock(&_gu_db_mutex); DoPrefix(state->u_line); if (TRACING) { Indent(state->level + 1); } else { (void) fprintf(_gu_db_fp_, "%s: ", state->func); } (void) fprintf(_gu_db_fp_, "%s: ", state->u_keyword); (void) vfprintf(_gu_db_fp_, format, args); va_end(args); (void) fputc('\n', _gu_db_fp_); dbug_flush(state); errno = save_errno; } va_end(args); code_state_cleanup(state); } /* * FUNCTION * * _gu_db_dump_ dump a string until '\0' is found * * SYNOPSIS * * void _gu_db_dump_ (_line_,keyword,memory,length) * int _line_; current source line number * char *keyword; * char *memory; Memory to print * int length; Bytes to print * * DESCRIPTION * Dump N characters in a binary array. * Is used to examine corrputed memory or arrays. */ void _gu_db_dump_(uint _line_, const char *keyword, const char *memory, uint length) { int pos; char dbuff[90]; CODE_STATE *state; state = code_state(); if (_gu_db_keyword_((char *) keyword)) { if (!state->locked) pthread_mutex_lock(&_gu_db_mutex); DoPrefix(_line_); if (TRACING) { Indent(state->level + 1); pos = min(max(state->level - _gu_db_stack->sub_level, 0) * INDENT, 80); } else { fprintf(_gu_db_fp_, "%s: ", state->func); } sprintf(dbuff, "%s: Memory: %lx Bytes: (%d)\n", keyword, (ulong) memory, length); (void) fputs(dbuff, _gu_db_fp_); pos = 0; while (length-- > 0) { uint tmp = *((unsigned char *) memory++); if ((pos += 3) >= 80) { fputc('\n', _gu_db_fp_); pos = 3; } fputc(_gu_dig_vec[((tmp >> 4) & 15)], _gu_db_fp_); fputc(_gu_dig_vec[tmp & 15], _gu_db_fp_); fputc(' ', _gu_db_fp_); } (void) fputc('\n', _gu_db_fp_); dbug_flush(state); } code_state_cleanup(state); } /* * FUNCTION * * ListParse parse list of modifiers in debug control string * * SYNOPSIS * * static struct link *ListParse (ctlp) * char *ctlp; * * DESCRIPTION * * Given pointer to a comma separated list of strings in "cltp", * parses the list, building a list and returning a pointer to it. * The original comma separated list is destroyed in the process of * building the linked list, thus it had better be a duplicate * if it is important. * * Note that since each link is added at the head of the list, * the final list will be in "reverse order", which is not * significant for our usage here. * */ static struct link * ListParse(char *ctlp) { REGISTER char *start; REGISTER struct link *new_malloc; REGISTER struct link *head; head = NULL; while (*ctlp != EOS) { start = ctlp; while (*ctlp != EOS && *ctlp != ',') { ctlp++; } if (*ctlp == ',') { *ctlp++ = EOS; } new_malloc = (struct link *) DbugMalloc(sizeof(struct link)); new_malloc->str = StrDup(start); new_malloc->next_link = head; head = new_malloc; } return (head); } /* * FUNCTION * * InList test a given string for member of a given list * * SYNOPSIS * * static BOOLEAN InList (linkp, cp) * struct link *linkp; * char *cp; * * DESCRIPTION * * Tests the string pointed to by "cp" to determine if it is in * the list pointed to by "linkp". Linkp points to the first * link in the list. If linkp is NULL then the string is treated * as if it is in the list (I.E all strings are in the null list). * This may seem rather strange at first but leads to the desired * operation if no list is given. The net effect is that all * strings will be accepted when there is no list, and when there * is a list, only those strings in the list will be accepted. * */ static BOOLEAN InList(struct link *linkp, const char *cp) { REGISTER struct link *scan; REGISTER BOOLEAN result; if (linkp == NULL) { result = TRUE; } else { result = FALSE; for (scan = linkp; scan != NULL; scan = scan->next_link) { if (STREQ(scan->str, cp)) { result = TRUE; break; } } } return (result); } /* * FUNCTION * * PushState push current state onto stack and set up new one * * SYNOPSIS * * static VOID PushState () * * DESCRIPTION * * Pushes the current state on the state stack, and inits * a new state. The only parameter inherited from the previous * state is the function nesting level. This action can be * inhibited if desired, via the "r" flag. * * The state stack is a linked list of states, with the new * state added at the head. This allows the stack to grow * to the limits of memory if necessary. * */ static void PushState() { REGISTER struct state *new_malloc; new_malloc = (struct state *) DbugMalloc(sizeof(struct state)); new_malloc->flags = 0; new_malloc->delay = 0; new_malloc->maxdepth = MAXDEPTH; new_malloc->sub_level = 0; new_malloc->out_file = stderr; new_malloc->prof_file = (FILE *) 0; new_malloc->functions = NULL; new_malloc->p_functions = NULL; new_malloc->keywords = NULL; new_malloc->processes = NULL; new_malloc->next_state = _gu_db_stack; _gu_db_stack = new_malloc; } /* * FUNCTION * * DoTrace check to see if tracing is current enabled * * SYNOPSIS * * static BOOLEAN DoTrace (stack) * * DESCRIPTION * * Checks to see if tracing is enabled based on whether the * user has specified tracing, the maximum trace depth has * not yet been reached, the current function is selected, * and the current process is selected. Returns TRUE if * tracing is enabled, FALSE otherwise. * */ static BOOLEAN DoTrace(CODE_STATE * state) { register BOOLEAN trace = FALSE; if (TRACING && state->level <= _gu_db_stack->maxdepth && InList(_gu_db_stack->functions, state->func) && InList(_gu_db_stack->processes, _gu_db_process_)) trace = TRUE; return (trace); } /* * FUNCTION * * DoProfile check to see if profiling is current enabled * * SYNOPSIS * * static BOOLEAN DoProfile () * * DESCRIPTION * * Checks to see if profiling is enabled based on whether the * user has specified profiling, the maximum trace depth has * not yet been reached, the current function is selected, * and the current process is selected. Returns TRUE if * profiling is enabled, FALSE otherwise. * */ #ifndef THREAD static BOOLEAN DoProfile() { REGISTER BOOLEAN profile; CODE_STATE *state; state = code_state(); profile = FALSE; if (PROFILING && state->level <= _gu_db_stack->maxdepth && InList(_gu_db_stack->p_functions, state->func) && InList(_gu_db_stack->processes, _gu_db_process_)) profile = TRUE; return (profile); } #endif /* * FUNCTION * * _gu_db_keyword_ test keyword for member of keyword list * * SYNOPSIS * * BOOLEAN _gu_db_keyword_ (keyword) * char *keyword; * * DESCRIPTION * * Test a keyword to determine if it is in the currently active * keyword list. As with the function list, a keyword is accepted * if the list is null, otherwise it must match one of the list * members. When debugging is not on, no keywords are accepted. * After the maximum trace level is exceeded, no keywords are * accepted (this behavior subject to change). Additionally, * the current function and process must be accepted based on * their respective lists. * * Returns TRUE if keyword accepted, FALSE otherwise. * */ BOOLEAN _gu_db_keyword_(const char *keyword) { REGISTER BOOLEAN result; CODE_STATE *state; state = code_state(); result = FALSE; if (DEBUGGING && state->level <= _gu_db_stack->maxdepth && InList(_gu_db_stack->functions, state->func) && InList(_gu_db_stack->keywords, keyword) && InList(_gu_db_stack->processes, _gu_db_process_)) result = TRUE; return (result); } /* * FUNCTION * * Indent indent a line to the given indentation level * * SYNOPSIS * * static VOID Indent (indent) * int indent; * * DESCRIPTION * * Indent a line to the given level. Note that this is * a simple minded but portable implementation. * There are better ways. * * Also, the indent must be scaled by the compile time option * of character positions per nesting level. * */ static void Indent(int indent) { REGISTER int count; indent = max(indent - 1 - _gu_db_stack->sub_level, 0) * INDENT; for (count = 0; count < indent; count++) { if ((count % INDENT) == 0) fputc('|', _gu_db_fp_); else fputc(' ', _gu_db_fp_); } } /* * FUNCTION * * FreeList free all memory associated with a linked list * * SYNOPSIS * * static VOID FreeList (linkp) * struct link *linkp; * * DESCRIPTION * * Given pointer to the head of a linked list, frees all * memory held by the list and the members of the list. * */ static void FreeList(struct link *linkp) { REGISTER struct link *old; while (linkp != NULL) { old = linkp; linkp = linkp->next_link; if (old->str != NULL) { free(old->str); } free((char *) old); } } /* * FUNCTION * * StrDup make a duplicate of a string in new memory * * SYNOPSIS * * static char *StrDup (my_string) * char *string; * * DESCRIPTION * * Given pointer to a string, allocates sufficient memory to make * a duplicate copy, and copies the string to the newly allocated * memory. Failure to allocated sufficient memory is immediately * fatal. * */ static char * StrDup(const char *str) { register char *new_malloc; new_malloc = DbugMalloc((int) strlen(str) + 1); (void) strcpy(new_malloc, str); return (new_malloc); } /* * FUNCTION * * DoPrefix print debugger line prefix prior to indentation * * SYNOPSIS * * static VOID DoPrefix (_line_) * int _line_; * * DESCRIPTION * * Print prefix common to all debugger output lines, prior to * doing indentation if necessary. Print such information as * current process name, current source file name and line number, * and current function nesting depth. * */ static void DoPrefix(uint _line_) { CODE_STATE *state; state = code_state(); state->lineno++; if (_gu_db_stack->flags & PID_ON) { #ifdef THREAD (void) fprintf(_gu_db_fp_, "%5d:(thread %lu):", (int)getpid(), (unsigned long)pthread_self()); #else (void) fprintf(_gu_db_fp_, "%5d: ", (int) getpid()); #endif /* THREAD */ } if (_gu_db_stack->flags & NUMBER_ON) { (void) fprintf(_gu_db_fp_, "%5d: ", state->lineno); } if (_gu_db_stack->flags & PROCESS_ON) { (void) fprintf(_gu_db_fp_, "%s: ", _gu_db_process_); } if (_gu_db_stack->flags & FILE_ON) { (void) fprintf(_gu_db_fp_, "%14s: ", BaseName(state->file)); } if (_gu_db_stack->flags & LINE_ON) { (void) fprintf(_gu_db_fp_, "%5d: ", _line_); } if (_gu_db_stack->flags & DEPTH_ON) { (void) fprintf(_gu_db_fp_, "%4d: ", state->level); } } /* * FUNCTION * * GU_DBUGOpenFile open new output stream for debugger output * * SYNOPSIS * * static VOID GU_DBUGOpenFile (name) * char *name; * * DESCRIPTION * * Given name of a new file (or "-" for stdout) opens the file * and sets the output stream to the new file. * */ static void GU_DBUGOpenFile(const char *name, int append) { REGISTER FILE *fp; REGISTER BOOLEAN newfile; if (name != NULL) { strcpy(_gu_db_stack->name, name); if (strcmp(name, "-") == 0) { _gu_db_fp_ = stdout; _gu_db_stack->out_file = _gu_db_fp_; _gu_db_stack->flags |= FLUSH_ON_WRITE; } else { if (!Writable((char *) name)) { (void) fprintf(stderr, ERR_OPEN, _gu_db_process_, name); perror(""); fflush(stderr); } else { newfile = !EXISTS(name); if (!(fp = fopen(name, append ? "a+" : "w"))) { (void) fprintf(stderr, ERR_OPEN, _gu_db_process_, name); perror(""); fflush(stderr); } else { _gu_db_fp_ = fp; _gu_db_stack->out_file = fp; if (newfile) { ChangeOwner(name); } } } } } } /* * FUNCTION * * OpenProfile open new output stream for profiler output * * SYNOPSIS * * static FILE *OpenProfile (name) * char *name; * * DESCRIPTION * * Given name of a new file, opens the file * and sets the profiler output stream to the new file. * * It is currently unclear whether the prefered behavior is * to truncate any existing file, or simply append to it. * The latter behavior would be desirable for collecting * accumulated runtime history over a number of separate * runs. It might take some changes to the analyzer program * though, and the notes that Binayak sent with the profiling * diffs indicated that append was the normal mode, but this * does not appear to agree with the actual code. I haven't * investigated at this time [fnf; 24-Jul-87]. */ #ifndef THREAD static FILE * OpenProfile(const char *name) { REGISTER FILE *fp; REGISTER BOOLEAN newfile; fp = 0; if (!Writable(name)) { (void) fprintf(_gu_db_fp_, ERR_OPEN, _gu_db_process_, name); perror(""); dbug_flush(0); (void) Delay(_gu_db_stack->delay); } else { newfile = !EXISTS(name); if (!(fp = fopen(name, "w"))) { (void) fprintf(_gu_db_fp_, ERR_OPEN, _gu_db_process_, name); perror(""); dbug_flush(0); } else { _gu_db_pfp_ = fp; _gu_db_stack->prof_file = fp; if (newfile) { ChangeOwner(name); } } } return fp; } #endif /* * FUNCTION * * CloseFile close the debug output stream * * SYNOPSIS * * static VOID CloseFile (fp) * FILE *fp; * * DESCRIPTION * * Closes the debug output stream unless it is standard output * or standard error. * */ static void CloseFile(FILE * fp) { if (fp != stderr && fp != stdout) { if (fclose(fp) == EOF) { pthread_mutex_lock(&_gu_db_mutex); (void) fprintf(_gu_db_fp_, ERR_CLOSE, _gu_db_process_); perror(""); dbug_flush(0); } } } /* * FUNCTION * * DbugExit print error message and exit * * SYNOPSIS * * static VOID DbugExit (why) * char *why; * * DESCRIPTION * * Prints error message using current process name, the reason for * aborting (typically out of memory), and exits with status 1. * This should probably be changed to use a status code * defined in the user's debugger include file. * */ static void DbugExit(const char *why) { (void) fprintf(stderr, ERR_ABORT, _gu_db_process_, why); (void) fflush(stderr); exit(1); } /* * FUNCTION * * DbugMalloc allocate memory for debugger runtime support * * SYNOPSIS * * static long *DbugMalloc (size) * int size; * * DESCRIPTION * * Allocate more memory for debugger runtime support functions. * Failure to to allocate the requested number of bytes is * immediately fatal to the current process. This may be * rather unfriendly behavior. It might be better to simply * print a warning message, freeze the current debugger state, * and continue execution. * */ static char * DbugMalloc(int size) { register char *new_malloc; if (!(new_malloc = (char *) malloc((unsigned int) size))) DbugExit("out of memory"); return (new_malloc); } /* * As strtok but two separators in a row are changed to one * separator (to allow directory-paths in dos). */ static char * static_strtok(char *s1, char separator) { static char *end = NULL; register char *rtnval, *cpy; rtnval = NULL; if (s1 != NULL) end = s1; if (end != NULL && *end != EOS) { rtnval = cpy = end; do { if ((*cpy++ = *end++) == separator) { if (*end != separator) { cpy--; /* Point at separator */ break; } end++; /* Two separators in a row, skipp one */ } } while (*end != EOS); *cpy = EOS; /* Replace last separator */ } return (rtnval); } /* * FUNCTION * * BaseName strip leading pathname components from name * * SYNOPSIS * * static char *BaseName (pathname) * char *pathname; * * DESCRIPTION * * Given pointer to a complete pathname, locates the base file * name at the end of the pathname and returns a pointer to * it. * */ static char * BaseName(const char *pathname) { register const char *base; base = strrchr(pathname, FN_LIBCHAR); // if (base++ == NullS) - this doesn't make sense if (NULL == base || '\0' == base[1]) base = pathname; return ((char *) base); } /* * FUNCTION * * Writable test to see if a pathname is writable/creatable * * SYNOPSIS * * static BOOLEAN Writable (pathname) * char *pathname; * * DESCRIPTION * * Because the debugger might be linked in with a program that * runs with the set-uid-bit (suid) set, we have to be careful * about opening a user named file for debug output. This consists * of checking the file for write access with the real user id, * or checking the directory where the file will be created. * * Returns TRUE if the user would normally be allowed write or * create access to the named file. Returns FALSE otherwise. * */ #ifndef Writable static BOOLEAN Writable(char *pathname) { REGISTER BOOLEAN granted; REGISTER char *lastslash; granted = FALSE; if (EXISTS(pathname)) { if (WRITABLE(pathname)) { granted = TRUE; } } else { lastslash = strrchr(pathname, '/'); if (lastslash != NULL) { *lastslash = EOS; } else { pathname = "."; } if (WRITABLE(pathname)) { granted = TRUE; } if (lastslash != NULL) { *lastslash = '/'; } } return (granted); } #endif /* * FUNCTION * * ChangeOwner change owner to real user for suid programs * * SYNOPSIS * * static VOID ChangeOwner (pathname) * * DESCRIPTION * * For unix systems, change the owner of the newly created debug * file to the real owner. This is strictly for the benefit of * programs that are running with the set-user-id bit set. * * Note that at this point, the fact that pathname represents * a newly created file has already been established. If the * program that the debugger is linked to is not running with * the suid bit set, then this operation is redundant (but * harmless). * */ #ifndef ChangeOwner static void ChangeOwner(char *pathname) { if (chown(pathname, getuid(), getgid()) == -1) { (void) fprintf(stderr, ERR_CHOWN, _gu_db_process_, pathname); perror(""); (void) fflush(stderr); } } #endif /* * FUNCTION * * _gu_db_setjmp_ save debugger environment * * SYNOPSIS * * VOID _gu_db_setjmp_ () * * DESCRIPTION * * Invoked as part of the user's GU_DBUG_SETJMP macro to save * the debugger environment in parallel with saving the user's * environment. * */ #ifdef HAVE_LONGJMP void _gu_db_setjmp_() { CODE_STATE *state; state = code_state(); state->jmplevel = state->level; state->jmpfunc = state->func; state->jmpfile = state->file; } /* * FUNCTION * * _gu_db_longjmp_ restore previously saved debugger environment * * SYNOPSIS * * VOID _gu_db_longjmp_ () * * DESCRIPTION * * Invoked as part of the user's GU_DBUG_LONGJMP macro to restore * the debugger environment in parallel with restoring the user's * previously saved environment. * */ void _gu_db_longjmp_() { CODE_STATE *state; state = code_state(); state->level = state->jmplevel; if (state->jmpfunc) { state->func = state->jmpfunc; } if (state->jmpfile) { state->file = state->jmpfile; } } #endif /* * FUNCTION * * DelayArg convert D flag argument to appropriate value * * SYNOPSIS * * static int DelayArg (value) * int value; * * DESCRIPTION * * Converts delay argument, given in tenths of a second, to the * appropriate numerical argument used by the system to delay * that that many tenths of a second. For example, on the * amiga, there is a system call "Delay()" which takes an * argument in ticks (50 per second). On unix, the sleep * command takes seconds. Thus a value of "10", for one * second of delay, gets converted to 50 on the amiga, and 1 * on unix. Other systems will need to use a timing loop. * */ #ifdef AMIGA #define HZ (50) /* Probably in some header somewhere */ #endif static int DelayArg(int value) { uint delayarg = 0; #if (unix || xenix) delayarg = value / 10; /* Delay is in seconds for sleep () */ #endif #ifdef AMIGA delayarg = (HZ * value) / 10; /* Delay in ticks for Delay () */ #endif return (delayarg); } /* * A dummy delay stub for systems that do not support delays. * With a little work, this can be turned into a timing loop. */ #if ! defined(Delay) && ! defined(AMIGA) static int Delay(int ticks) { return ticks; } #endif /* * FUNCTION * * perror perror simulation for systems that don't have it * * SYNOPSIS * * static VOID perror (s) * char *s; * * DESCRIPTION * * Perror produces a message on the standard error stream which * provides more information about the library or system error * just encountered. The argument string s is printed, followed * by a ':', a blank, and then a message and a newline. * * An undocumented feature of the unix perror is that if the string * 's' is a null string (NOT a NULL pointer!), then the ':' and * blank are not printed. * * This version just complains about an "unknown system error". * */ /* flush dbug-stream, free mutex lock & wait delay */ /* This is because some systems (MSDOS!!) dosn't flush fileheader */ /* and dbug-file isn't readable after a system crash !! */ static void dbug_flush(CODE_STATE * state) { #ifndef THREAD if (_gu_db_stack->flags & FLUSH_ON_WRITE) #endif { #if defined(MSDOS) || defined(__WIN__) if (_gu_db_fp_ != stdout && _gu_db_fp_ != stderr) { if (!(freopen(_gu_db_stack->name, "a", _gu_db_fp_))) { (void) fprintf(stderr, ERR_OPEN, _gu_db_process_,_gu_db_stack->name); fflush(stderr); _gu_db_fp_ = stdout; _gu_db_stack->out_file = _gu_db_fp_; _gu_db_stack->flags |= FLUSH_ON_WRITE; } } else #endif { (void) fflush(_gu_db_fp_); if (_gu_db_stack->delay) (void) Delay(_gu_db_stack->delay); } } if (!state || !state->locked) pthread_mutex_unlock(&_gu_db_mutex); } /* dbug_flush */ void _gu_db_lock_file() { CODE_STATE *state; state = code_state(); pthread_mutex_lock(&_gu_db_mutex); state->locked = 1; } void _gu_db_unlock_file() { CODE_STATE *state; state = code_state(); state->locked = 0; pthread_mutex_unlock(&_gu_db_mutex); } /* * Here we need the definitions of the clock routine. Add your * own for whatever system that you have. */ #ifndef THREAD #if defined(HAVE_GETRUSAGE) #include #include /* extern int getrusage(int, struct rusage *); */ /* * Returns the user time in milliseconds used by this process so * far. */ static unsigned long Clock() { struct rusage ru; (void) getrusage(RUSAGE_SELF, &ru); return ((ru.ru_utime.tv_sec * 1000) + (ru.ru_utime.tv_usec / 1000)); } #elif defined(MSDOS) || defined(__WIN__) || defined(OS2) static ulong Clock() { return clock() * (1000 / Cmy_pthread_mutex_lockS_PER_SEC); } #elif defined (amiga) struct DateStamp { /* Yes, this is a hack, but doing it right */ long ds_Days; /* is incredibly ugly without splitting this */ long ds_Minute; /* off into a separate file */ long ds_Tick; }; static int first_clock = TRUE; static struct DateStamp begin; static struct DateStamp elapsed; static unsigned long Clock() { register struct DateStamp *now; register unsigned long millisec = 0; extern VOID *AllocMem(); now = (struct DateStamp *) AllocMem((long) sizeof(struct DateStamp), 0L); if (now != NULL) { if (first_clock == TRUE) { first_clock = FALSE; (void) DateStamp(now); begin = *now; } (void) DateStamp(now); millisec = 24 * 3600 * (1000 / HZ) * (now->ds_Days - begin.ds_Days); millisec += 60 * (1000 / HZ) * (now->ds_Minute - begin.ds_Minute); millisec += (1000 / HZ) * (now->ds_Tick - begin.ds_Tick); (void) FreeMem(now, (long) sizeof(struct DateStamp)); } return (millisec); } #else static unsigned long Clock() { return (0); } #endif /* RUSAGE */ #endif /* THREADS */ #ifdef NO_VARARGS /* * Fake vfprintf for systems that don't support it. If this * doesn't work, you are probably SOL... */ static int vfprintf(stream, format, ap) FILE *stream; char *format; va_list ap; { int rtnval; ARGS_DCL; ARG0 = va_arg(ap, ARGS_TYPE); ARG1 = va_arg(ap, ARGS_TYPE); ARG2 = va_arg(ap, ARGS_TYPE); ARG3 = va_arg(ap, ARGS_TYPE); ARG4 = va_arg(ap, ARGS_TYPE); ARG5 = va_arg(ap, ARGS_TYPE); ARG6 = va_arg(ap, ARGS_TYPE); ARG7 = va_arg(ap, ARGS_TYPE); ARG8 = va_arg(ap, ARGS_TYPE); ARG9 = va_arg(ap, ARGS_TYPE); rtnval = fprintf(stream, format, ARGS_LIST); return (rtnval); } #endif /* NO_VARARGS */ char _gu_dig_vec[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; percona-xtradb-cluster-galera/galerautils/src/gu_dbug.h0000644000000000000000000001460512247075736023550 0ustar rootroot00000000000000/****************************************************************************** * * * N O T I C E * * * * Copyright Abandoned, 1987, Fred Fish * * * * * * This previously copyrighted work has been placed into the public * * domain by the author and may be freely used for any purpose, * * private or commercial. * * * * Because of the number of inquiries I was receiving about the use * * of this product in commercially developed works I have decided to * * simply make it public domain to further its unrestricted use. I * * specifically would be most happy to see this material become a * * part of the standard Unix distributions by AT&T and the Berkeley * * Computer Science Research Group, and a standard part of the GNU * * system from the Free Software Foundation. * * * * I would appreciate it, as a courtesy, if this notice is left in * * all copies and derivative works. Thank you. * * * * The author makes no warranty of any kind with respect to this * * product and explicitly disclaims any implied warranties of mer- * * chantability or fitness for any particular purpose. * * * ****************************************************************************** */ /* * FILE * * dbug.c runtime support routines for dbug package * * SCCS * * @(#)dbug.c 1.25 7/25/89 * * DESCRIPTION * * These are the runtime support routines for the dbug package. * The dbug package has two main components; the user include * file containing various macro definitions, and the runtime * support routines which are called from the macro expansions. * * Externally visible functions in the runtime support module * use the naming convention pattern "_db_xx...xx_", thus * they are unlikely to collide with user defined function names. * * AUTHOR(S) * * Fred Fish (base code) * Enhanced Software Technologies, Tempe, AZ * asuvax!mcdphx!estinc!fnf * * Binayak Banerjee (profiling enhancements) * seismo!bpa!sjuvax!bbanerje * * Michael Widenius: * DBUG_DUMP - To dump a pice of memory. * PUSH_FLAG "O" - To be used instead of "o" if we don't * want flushing (for slow systems) * PUSH_FLAG "A" - as 'O', but we will append to the out file instead * of creating a new one. * Check of malloc on entry/exit (option "S") * * Alexey Yurchenko: * Renamed global symbols for use with galera project to avoid * collisions with other software (notably MySQL) * * $Id: gu_dbug.h 2745 2012-03-17 00:00:23Z alex $ */ #ifndef _dbug_h #define _dbug_h #include #include typedef unsigned int uint; typedef unsigned long ulong; #define THREAD 1 #ifdef __cplusplus extern "C" { #endif extern char _gu_dig_vec[]; extern FILE* _gu_db_fp_; #define GU_DBUG_FILE _gu_db_fp_ #if defined(GU_DBUG_ON) && !defined(_lint) extern int _gu_db_on_; extern int _gu_no_db_; extern char* _gu_db_process_; extern int _gu_db_keyword_(const char* keyword); extern void _gu_db_setjmp_ (void); extern void _gu_db_longjmp_(void); extern void _gu_db_push_ (const char* control); extern void _gu_db_pop_ (void); extern void _gu_db_enter_ (const char* _func_, const char* _file_, uint _line_, const char** _sfunc_, const char** _sfile_, uint* _slevel_, char***); extern void _gu_db_return_ (uint _line_, const char** _sfunc_, const char** _sfile_, uint* _slevel_); extern void _gu_db_pargs_ (uint _line_, const char* keyword); extern void _gu_db_doprnt_ (const char* format, ...); extern void _gu_db_dump_ (uint _line_, const char *keyword, const char *memory, uint length); extern void _gu_db_lock_file (void); extern void _gu_db_unlock_file(void); #define GU_DBUG_ENTER(a) \ const char *_gu_db_func_, *_gu_db_file_; \ uint _gu_db_level_; \ char **_gu_db_framep_; \ _gu_db_enter_ (a, __FILE__, __LINE__, &_gu_db_func_, &_gu_db_file_, \ &_gu_db_level_, &_gu_db_framep_) #define GU_DBUG_LEAVE \ (_gu_db_return_ (__LINE__, &_gu_db_func_, &_gu_db_file_, \ &_gu_db_level_)) #define GU_DBUG_RETURN(a1) {GU_DBUG_LEAVE; return(a1);} #define GU_DBUG_VOID_RETURN {GU_DBUG_LEAVE; return; } #define GU_DBUG_EXECUTE(keyword,a1) \ {if (_gu_db_on_) {if (_gu_db_keyword_ (keyword)) { a1 }}} #define GU_DBUG_PRINT(keyword,arglist) \ {if (_gu_db_on_) {_gu_db_pargs_(__LINE__,keyword); \ _gu_db_doprnt_ arglist;}} #define GU_DBUG_PUSH(a1) _gu_db_push_ (a1) #define GU_DBUG_POP() _gu_db_pop_ () #define GU_DBUG_PROCESS(a1) (_gu_db_process_ = a1) #define GU_DBUG_SETJMP(a1) (_gu_db_setjmp_ (), setjmp (a1)) #define GU_DBUG_LONGJMP(a1,a2) (_gu_db_longjmp_ (), longjmp (a1, a2)) #define GU_DBUG_DUMP(keyword,a1,a2)\ {if (_gu_db_on_) {_gu_db_dump_(__LINE__,keyword,a1,a2);}} #define GU_DBUG_IN_USE (_gu_db_fp_ && _gu_db_fp_ != stderr) #define GU_DEBUGGER_OFF _no_gu_db_=1;_gu_db_on_=0; #define GU_DEBUGGER_ON _no_gu_db_=0 #define GU_DBUG_my_pthread_mutex_lock_FILE { _gu_db_lock_file(); } #define GU_DBUG_my_pthread_mutex_unlock_FILE { _gu_db_unlock_file(); } #define GU_DBUG_ASSERT(A) assert(A) #else /* No debugger */ #define GU_DBUG_ENTER(a1) #define GU_DBUG_RETURN(a1) return(a1) #define GU_DBUG_VOID_RETURN return #define GU_DBUG_EXECUTE(keyword,a1) {} #define GU_DBUG_PRINT(keyword,arglist) {} #define GU_DBUG_PUSH(a1) {} #define GU_DBUG_POP() {} #define GU_DBUG_PROCESS(a1) {} #define GU_DBUG_SETJMP setjmp #define GU_DBUG_LONGJMP longjmp #define GU_DBUG_DUMP(keyword,a1,a2) {} #define GU_DBUG_IN_USE 0 #define GU_DEBUGGER_OFF #define GU_DEBUGGER_ON #define GU_DBUG_my_pthread_mutex_lock_FILE #define GU_DBUG_my_pthread_mutex_unlock_FILE #define GU_DBUG_ASSERT(A) {} #endif #ifdef __cplusplus } #endif #endif percona-xtradb-cluster-galera/galerautils/src/gu_errno.h0000644000000000000000000000073612247075736023754 0ustar rootroot00000000000000/* * Copyright (C) 2013 Codership Oy */ #ifndef GU_ERRNO_H #define GU_ERRNO_H #include #if defined(__APPLE__) || defined(__FreeBSD__) # define EBADFD (ELAST+1) /* the largest errno + 1 */ # define EREMCHG (ELAST+2) # define ENOTUNIQ (ELAST+3) # define ERESTART (ELAST+4) # if defined(__FreeBSD__) # define ENOTRECOVERABLE (ELAST+5) # define ENODATA (ELAST+6) # endif #endif #endif /* GU_STR_H */ percona-xtradb-cluster-galera/galerautils/src/gu_exception.cpp0000644000000000000000000000071412247075736025154 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy * */ #include #include "gu_utils.hpp" #include "gu_exception.hpp" namespace gu { void Exception::trace (const char* file, const char* func, int line) { msg.reserve (msg.length() + ::strlen(file) + ::strlen(func) + 15); msg += "\n\t at "; msg += file; msg += ':'; msg += func; msg += "():"; msg += to_string(line); } } percona-xtradb-cluster-galera/galerautils/src/gu_exception.hpp0000644000000000000000000000231212247075736025155 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy * */ #ifndef __GU_EXCEPTION__ #define __GU_EXCEPTION__ #include #include #include "gu_errno.h" namespace gu { /*! Some utility exceptions to indicate special conditions. */ class NotSet {}; class NotFound {}; class Exception: public std::exception { public: Exception (const std::string& msg_, int err_) : msg (msg_), err (err_) {} virtual ~Exception () throw() {} const char* what () const throw() { return msg.c_str(); } int get_errno () const { return err; } void trace (const char* file, const char* func, int line); private: std::string msg; const int err; }; } /* to mark a place where exception was caught */ #define GU_TRACE(_exception_) _exception_.trace(__FILE__, __FUNCTION__, __LINE__) #ifndef NDEBUG /* enabled together with assert() */ #define gu_trace(_expr_) \ try { _expr_; } catch (gu::Exception& e) { GU_TRACE(e); throw; } #else #define gu_trace(_expr_) _expr_ #endif // NDEBUG #endif // __GU_EXCEPTION__ percona-xtradb-cluster-galera/galerautils/src/gu_fifo.c0000644000000000000000000003217512247075736023547 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * Queue (FIFO) class implementation * * The driving idea behind this class is avoiding mallocs * at all costs on one hand, on the other - make it almost * as infinite as an ordinary linked list. FIFO properties * help achieving that. * * When needed this FIFO can be made very big, holding * millions or even billions of items while taking up * minimum space when there are few items in the queue. */ #define _BSD_SOURCE #include #include #include #include "gu_assert.h" #include "gu_limits.h" #include "gu_mem.h" #include "gu_mutex.h" #include "gu_log.h" #include "gu_fifo.h" #include "galerautils.h" struct gu_fifo { ulong col_shift; ulong col_mask; ulong rows_num; ulong item_size; ulong head; ulong tail; ulong row_size; ulong length; ulong length_mask; ulong used; ulong alloc; long get_wait; long put_wait; long q_len; long q_len_samples; bool closed; int get_err; gu_mutex_t lock; gu_cond_t get_cond; gu_cond_t put_cond; void* rows[]; }; /* Don't make rows less than 1K */ #define GCS_FIFO_MIN_ROW_POWER 10 typedef unsigned long long ull; /* constructor */ gu_fifo_t *gu_fifo_create (size_t length, size_t item_size) { int row_pwr = GCS_FIFO_MIN_ROW_POWER; ull row_len = 1 << row_pwr; ull row_size = row_len * item_size; int array_pwr = 1; // need at least 2 rows for alteration ull array_len = 1 << array_pwr; ull array_size = array_len * sizeof(void*); gu_fifo_t *ret = NULL; if (length > 0 && item_size > 0) { /* find the best ratio of width and height: * the size of a row array must be equal to that of the row */ while (array_len * row_len < length) { if (array_size < row_size) { array_pwr++; array_len = 1 << array_pwr; array_size = array_len * sizeof(void*); } else { row_pwr++; row_len = 1 << row_pwr; row_size = row_len * item_size; } } ull alloc_size = array_size + sizeof (gu_fifo_t); if (alloc_size > (size_t)-1) { gu_error ("Initial FIFO size %llu exceeds size_t range %zu", alloc_size, (size_t)-1); return NULL; } ull max_size = array_len * row_size + alloc_size; if (max_size > (size_t)-1) { gu_error ("Maximum FIFO size %llu exceeds size_t range %zu", max_size, (size_t)-1); return NULL; } if (max_size > gu_avphys_bytes()) { gu_error ("Maximum FIFO size %llu exceeds available memory " "limit %llu", max_size, gu_avphys_bytes()); return NULL; } if ((array_len * row_len) > (ull)GU_LONG_MAX) { gu_error ("Resulting queue length %llu exceeds max allowed %ld", array_len * row_len, GU_LONG_MAX); return NULL; } gu_debug ("Creating FIFO buffer of %llu elements of size %llu, " "memory min used: %zu, max used: %zu", array_len * row_len, item_size, alloc_size, alloc_size + array_len*row_size); ret = gu_malloc (alloc_size); if (ret) { memset (ret, 0, alloc_size); ret->col_shift = row_pwr; ret->col_mask = row_len - 1; ret->rows_num = array_len; ret->length = row_len * array_len; ret->length_mask = ret->length - 1; ret->item_size = item_size; ret->row_size = row_size; ret->alloc = alloc_size; gu_mutex_init (&ret->lock, NULL); gu_cond_init (&ret->get_cond, NULL); gu_cond_init (&ret->put_cond, NULL); } else { gu_error ("Failed to allocate %zu bytes for FIFO", alloc_size); } } return ret; } // defined as macro for proper line reporting #define fifo_lock(q) \ if (gu_likely (0 == gu_mutex_lock (&q->lock))) {} \ else { \ gu_fatal ("Failed to lock queue"); \ abort(); \ } static inline int fifo_unlock (gu_fifo_t* q) { return -gu_mutex_unlock (&q->lock); } /* lock the queue */ void gu_fifo_lock (gu_fifo_t *q) { fifo_lock(q); } /* unlock the queue */ void gu_fifo_release (gu_fifo_t *q) { fifo_unlock(q); } static int fifo_flush (gu_fifo_t* q) { int ret = 0; /* if there are items in the queue, wait until they are all fetched */ while (q->used > 0 && 0 == ret) { /* will make getters to signal every time item is removed */ gu_warn ("Waiting for %lu items to be fetched.", q->used); q->put_wait++; ret = gu_cond_wait (&q->put_cond, &q->lock); } return ret; } static void fifo_close (gu_fifo_t* q) { if (!q->closed) { q->closed = true; /* force putters to quit */ /* don't overwrite existing get_err status, see gu_fifo_resume_gets() */ if (!q->get_err) q->get_err = -ENODATA; // signal all the idle waiting threads gu_cond_broadcast (&q->put_cond); q->put_wait = 0; gu_cond_broadcast (&q->get_cond); q->get_wait = 0; #if 0 (void) fifo_flush (q); #endif } } void gu_fifo_close (gu_fifo_t* q) { fifo_lock (q); fifo_close (q); fifo_unlock (q); } void gu_fifo_open (gu_fifo_t* q) { fifo_lock (q); q->closed = false; q->get_err = 0; fifo_unlock (q); } /* lock the queue and wait if it is empty */ static inline int fifo_lock_get (gu_fifo_t *q) { register int ret = 0; fifo_lock(q); while (0 == ret && !(ret = q->get_err) && 0 == q->used) { q->get_wait++; ret = -gu_cond_wait (&q->get_cond, &q->lock); } return ret; } /* unlock the queue after getting item */ static inline int fifo_unlock_get (gu_fifo_t *q) { assert (q->used < q->length || 0 == q->length); if (q->put_wait > 0) { q->put_wait--; gu_cond_signal (&q->put_cond); } return fifo_unlock(q); } /* lock the queue and wait if it is full */ static inline int fifo_lock_put (gu_fifo_t *q) { register int ret = 0; fifo_lock(q); while (0 == ret && q->used == q->length && !q->closed) { q->put_wait++; ret = -gu_cond_wait (&q->put_cond, &q->lock); } return ret; } /* unlock the queue after putting an item */ static inline int fifo_unlock_put (gu_fifo_t *q) { assert (q->used > 0); if (q->get_wait > 0) { q->get_wait--; gu_cond_signal (&q->get_cond); } return fifo_unlock(q); } #define FIFO_ROW(q,x) ((x) >> q->col_shift) /* div by row width */ #define FIFO_COL(q,x) ((x) & q->col_mask) /* remnant */ #define FIFO_PTR(q,x) \ ((uint8_t*)q->rows[FIFO_ROW(q, x)] + FIFO_COL(q, x) * q->item_size) /* Increment and roll over */ #define FIFO_INC(q,x) (((x) + 1) & q->length_mask) /*! If FIFO is not empty, returns pointer to the head item and locks FIFO, * otherwise blocks. Or returns NULL if FIFO is closed. */ void* gu_fifo_get_head (gu_fifo_t* q, int* err) { *err = fifo_lock_get (q); if (gu_likely(-ECANCELED != *err && q->used)) { return (FIFO_PTR(q, q->head)); } else { assert (q->get_err); fifo_unlock (q); return NULL; } } /*! Advances FIFO head and unlocks FIFO. */ void gu_fifo_pop_head (gu_fifo_t* q) { if (FIFO_COL(q, q->head) == q->col_mask) { /* removing last unit from the row */ register ulong row = FIFO_ROW (q, q->head); assert (q->rows[row] != NULL); gu_free (q->rows[row]); q->rows[row] = NULL; q->alloc -= q->row_size; } q->head = FIFO_INC(q, q->head); q->used--; if (fifo_unlock_get(q)) { gu_fatal ("Faled to unlock queue to get item."); abort(); } } /*! If FIFO is not full, returns pointer to the tail item and locks FIFO, * otherwise blocks. Or returns NULL if FIFO is closed. */ void* gu_fifo_get_tail (gu_fifo_t* q) { fifo_lock_put (q); if (gu_likely(!q->closed)) { // stop adding items when closed ulong row = FIFO_ROW(q, q->tail); assert (q->used < q->length); // check if row is allocated and allocate if not. if (NULL == q->rows[row] && NULL == (q->alloc += q->row_size, q->rows[row] = gu_malloc(q->row_size))) { q->alloc -= q->row_size; } else { return ((uint8_t*)q->rows[row] + FIFO_COL(q, q->tail) * q->item_size); } #if 0 // for debugging if (NULL == q->rows[row]) { gu_debug ("Allocating row %lu of queue %p, rows %p", row, q, q->rows); if (NULL == (q->rows[row] = gu_malloc(q->row_size))) { gu_debug ("Allocating row %lu failed", row); fifo_unlock (q); return NULL; } q->alloc += q->row_size; } return (q->rows[row] + FIFO_COL(q, q->tail) * q->item_size); #endif } fifo_unlock (q); return NULL; } /*! Advances FIFO tail and unlocks FIFO. */ void gu_fifo_push_tail (gu_fifo_t* q) { q->tail = FIFO_INC(q, q->tail); q->q_len += q->used; q->used++; q->q_len_samples++; if (fifo_unlock_put(q)) { gu_fatal ("Faled to unlock queue to put item."); abort(); } } /*! returns how many items are in the queue */ long gu_fifo_length (gu_fifo_t* q) { return q->used; } /*! returns how many items were in the queue per push_tail() */ void gu_fifo_stats (gu_fifo_t* q, long* q_len, double* q_len_avg) { fifo_lock (q); *q_len = q->used; long len = q->q_len; long samples = q->q_len_samples; q->q_len = 0; q->q_len_samples = 0; fifo_unlock (q); if (len >= 0 && samples >= 0) { if (samples > 0) { *q_len_avg = ((double)len) / samples; } else { assert (0 == len); *q_len_avg = 0.0; } } else { *q_len_avg = -1.0; } } /* destructor - would block until all members are dequeued */ void gu_fifo_destroy (gu_fifo_t *queue) { fifo_lock (queue); { if (!queue->closed) fifo_close(queue); fifo_flush (queue); } fifo_unlock (queue); assert (queue->tail == queue->head); while (gu_cond_destroy (&queue->put_cond)) { fifo_lock (queue); gu_cond_signal (&queue->put_cond); fifo_unlock (queue); /* when thread sees that ret->used == 0, it must terminate */ } while (gu_cond_destroy (&queue->get_cond)) { fifo_lock (queue); gu_cond_signal (&queue->get_cond); fifo_unlock (queue); /* when thread sees that ret->used == 0, it must terminate */ } while (gu_mutex_destroy (&queue->lock)) continue; /* only one row migth be left */ { ulong row = FIFO_ROW(queue, queue->tail); if (queue->rows[row]) { assert (FIFO_COL(queue, queue->tail) != 0); gu_free (queue->rows[row]); queue->alloc -= queue->row_size; } else { assert (FIFO_COL(queue, queue->tail) == 0); } gu_free (queue); } } char *gu_fifo_print (gu_fifo_t *queue) { size_t tmp_len = 4096; char tmp[tmp_len]; char *ret; snprintf (tmp, tmp_len, "Queue (%p):\n" "\tlength = %lu\n" "\trows = %lu\n" "\tcolumns = %lu\n" "\tused = %lu (%lu bytes)\n" "\talloctd = %lu bytes\n" "\thead = %lu, tail = %lu\n" "\tavg.len = %f" //", next = %lu" , (void*)queue, queue->length, queue->rows_num, queue->col_mask + 1, queue->used, queue->used * queue->item_size, queue->alloc, queue->head, queue->tail, queue->q_len_samples > 0 ? ((double)queue->q_len)/queue->q_len_samples : 0.0 //, queue->next ); ret = strdup (tmp); return ret; } int gu_fifo_cancel_gets (gu_fifo_t* q) { if (q->get_err && -ENODATA != q->get_err) { gu_error ("Attempt to cancel FIFO gets in state: %d (%s)", q->get_err, strerror(-q->get_err)); return -EBADFD; } assert (!q->get_err || q->closed); q->get_err = -ECANCELED; /* force getters to quit with specific error */ if (q->get_wait) { gu_cond_broadcast (&q->get_cond); q->get_wait = 0; } return 0; } int gu_fifo_resume_gets (gu_fifo_t* q) { int ret = -1; fifo_lock(q); if (-ECANCELED == q->get_err) { q->get_err = q->closed ? -ENODATA : 0; ret = 0; } else { gu_error ("Attempt to resume FIFO gets in state: %d (%s)", q->get_err, strerror(-q->get_err)); ret = -EBADFD; } fifo_unlock(q); return ret; } percona-xtradb-cluster-galera/galerautils/src/gu_fifo.h0000644000000000000000000000450512247075736023550 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * Queue (FIFO) class definition * * The driving idea behind this class is avoiding malloc()'s * at all costs on one hand, on the other - make it almost * as infinite as an ordinary linked list. FIFO properties * help to achieve that. * * When needed this FIFO can be made very big, holding * millions or even billions of items while taking up * minimum space when there are few items in the queue. * malloc()'s do happen, but once per thousand of pushes and * allocate multiples of pages, thus reducing memory fragmentation. */ #ifndef _gu_fifo_h_ #define _gu_fifo_h_ #include typedef struct gu_fifo gu_fifo_t; /*! constructor */ extern gu_fifo_t* gu_fifo_create (size_t length, size_t unit); /*! puts FIFO into closed state, waking up waiting threads */ extern void gu_fifo_close (gu_fifo_t *queue); /*! (re)opens FIFO */ extern void gu_fifo_open (gu_fifo_t *queue); /*! destructor - would block until all members are dequeued */ extern void gu_fifo_destroy (gu_fifo_t *queue); /*! for logging purposes */ extern char* gu_fifo_print (gu_fifo_t *queue); /*! Lock FIFO */ extern void gu_fifo_lock (gu_fifo_t *q); /*! Release FIFO */ extern void gu_fifo_release (gu_fifo_t *q); /*! Lock FIFO and get pointer to head item * @param err contains error code if retval is NULL (otherwise - undefined): -ENODATA - queue closed, -ECANCELED - gets were canceled on the queue * @retval pointer to head item or NULL if error occured */ extern void* gu_fifo_get_head (gu_fifo_t* q, int* err); /*! Advance FIFO head pointer and release FIFO. */ extern void gu_fifo_pop_head (gu_fifo_t* q); /*! Lock FIFO and get pointer to tail item */ extern void* gu_fifo_get_tail (gu_fifo_t* q); /*! Advance FIFO tail pointer and release FIFO. */ extern void gu_fifo_push_tail (gu_fifo_t* q); /*! Return how many items are in the queue (unprotected) */ extern long gu_fifo_length (gu_fifo_t* q); /*! Return how many items were in the queue on average per push_tail() */ extern void gu_fifo_stats (gu_fifo_t* q, long* q_len, double* q_len_avg); /*! Cancel getters (must be called while holding a FIFO lock) */ extern int gu_fifo_cancel_gets (gu_fifo_t* q); /*! Resume get operations */ extern int gu_fifo_resume_gets (gu_fifo_t* q); #endif // _gu_fifo_h_ percona-xtradb-cluster-galera/galerautils/src/gu_fnv.h0000644000000000000000000001311612247075736023414 0ustar rootroot00000000000000// Copyright (C) 2012 Codership Oy /*! * @file * * This header file defines FNV hash functions for 3 hash sizes: * 4, 8 and 16 bytes. * * Be wary of bitshift multiplication "optimization" (FNV_BITSHIFT_OPTIMIZATION): * FNV authors used to claim marginal speedup when using it, however on core2 * CPU it has shown no speedup for fnv32a and more than 2x slowdown for fnv64a * and fnv128a. Disabled by default. * * FNV vs. FNVa: FNVa has a better distribution: multiplication happens after * XOR and hence propagates XOR effect to all bytes of the hash. * Hence by default functions perform FNVa. GU_FNV_NORMAL macro * is needed for unit tests. * * gu_fnv*_internal() functions are endian-unsafe, their output should be * converted to little-endian format if it is to be exported to other machines. */ #ifndef _gu_fnv_h_ #define _gu_fnv_h_ #include "gu_int128.h" #include #include #include #include // ssize_t #include #define GU_FNV32_PRIME 16777619UL #define GU_FNV32_SEED 2166136261UL #if !defined(GU_FNVBITSHIFT_OPTIMIZATION) # define GU_FNV32_MUL(_x) _x *= GU_FNV32_PRIME #else /* GU_FNVBITSHIFT_OPTIMIZATION */ # define GU_FNV32_MUL(_x) \ _x += (_x << 1) + (_x << 4) + (_x << 7) + (_x << 8) + (_x << 24) #endif /* GU_FNVBITSHIFT_OPTIMIZATION */ #if !defined(GU_FNV_NORMAL) # define GU_FNV32_ITERATION(_s,_b) _s ^= _b; GU_FNV32_MUL(_s); #else # define GU_FNV32_ITERATION(_s,_b) GU_FNV32_MUL(_s); _s ^= _b; #endif static GU_FORCE_INLINE void gu_fnv32a_internal (const void* buf, ssize_t const len, uint32_t* seed) { const uint8_t* bp = (const uint8_t*)buf; const uint8_t* const be = bp + len; while (bp + 2 <= be) { GU_FNV32_ITERATION(*seed,*bp++); GU_FNV32_ITERATION(*seed,*bp++); } if (bp < be) { GU_FNV32_ITERATION(*seed,*bp++); } assert(be == bp); } #define GU_FNV64_PRIME 1099511628211ULL #define GU_FNV64_SEED 14695981039346656037ULL #if !defined(GU_FNVBITSHIFT_OPTIMIZATION) # define GU_FNV64_MUL(_x) _x *= GU_FNV64_PRIME #else /* GU_FNVBITSHIFT_OPTIMIZATION */ # define GU_FNV64_MUL(_x) \ _x +=(_x << 1) + (_x << 4) + (_x << 5) + (_x << 7) + (_x << 8) + (_x << 40); #endif /* GU_FNVBITSHIFT_OPTIMIZATION */ #if !defined(GU_FNV_NORMAL) # define GU_FNV64_ITERATION(_s,_b) _s ^= _b; GU_FNV64_MUL(_s); #else # define GU_FNV64_ITERATION(_s,_b) GU_FNV64_MUL(_s); _s ^= _b; #endif static GU_FORCE_INLINE void gu_fnv64a_internal (const void* buf, ssize_t const len, uint64_t* seed) { const uint8_t* bp = (const uint8_t*)buf; const uint8_t* const be = bp + len; while (bp + 2 <= be) { GU_FNV64_ITERATION(*seed,*bp++); GU_FNV64_ITERATION(*seed,*bp++); } if (bp < be) { GU_FNV64_ITERATION(*seed,*bp++); } assert(be == bp); } static gu_uint128_t const GU_SET128(GU_FNV128_PRIME, 0x0000000001000000ULL, 0x000000000000013BULL); static gu_uint128_t const GU_SET128(GU_FNV128_SEED, 0x6C62272E07BB0142ULL, 0x62B821756295C58DULL); #if defined(__SIZEOF_INT128__) #define GU_FNV128_XOR(_s,_b) _s ^= _b #if !defined(GU_FNVBITSHIFT_OPTIMIZATION) # define GU_FNV128_MUL(_x) _x *= GU_FNV128_PRIME #else /* GU_FNVBITSHIFT_OPTIMIZATION */ # define GU_FNV128_MUL(_x) \ _x +=(_x << 1) + (_x << 3) + (_x << 4) + (_x << 5) + (_x << 8) + (_x << 88); #endif /* GU_FNVBITSHIFT_OPTIMIZATION */ #else /* ! __SIZEOF_INT128__ */ #define GU_FNV128_XOR(_s,_b) (_s).u32[GU_32LO] ^= _b #if defined(GU_FNV128_FULL_MULTIPLICATION) # define GU_FNV128_MUL(_x) GU_MUL128_INPLACE(_x, GU_FNV128_PRIME) #else /* no FULL_MULTIPLICATION */ # define GU_FNV128_MUL(_x) { \ uint32_t carry = \ (((_x).u64[GU_64LO] & 0x00000000ffffffffULL) * 0x013b) >> 32; \ carry = (((_x).u64[GU_64LO] >> 32) * 0x013b + carry) >> 32; \ (_x).u64[GU_64HI] *= 0x013b; \ (_x).u64[GU_64HI] += ((_x).u64[GU_64LO] << 24) + carry; \ (_x).u64[GU_64LO] *= 0x013b; \ } #endif /* FULL_MULTIPLICATION */ #endif /* ! __SIZEOF_INT128__ */ #if !defined(GU_FNV_NORMAL) # define GU_FNV128_ITERATION(_s,_b) GU_FNV128_XOR(_s,_b); GU_FNV128_MUL(_s); #else # define GU_FNV128_ITERATION(_s,_b) GU_FNV128_MUL(_s); GU_FNV128_XOR(_s,_b); #endif inline static void gu_fnv128a_internal (const void* buf, ssize_t const len, gu_uint128_t* seed) { const uint8_t* bp = (const uint8_t*)buf; const uint8_t* const be = bp + len; /* this manual loop unrolling seems to be essential */ while (bp + 8 <= be) { GU_FNV128_ITERATION(*seed, *bp++); GU_FNV128_ITERATION(*seed, *bp++); GU_FNV128_ITERATION(*seed, *bp++); GU_FNV128_ITERATION(*seed, *bp++); GU_FNV128_ITERATION(*seed, *bp++); GU_FNV128_ITERATION(*seed, *bp++); GU_FNV128_ITERATION(*seed, *bp++); GU_FNV128_ITERATION(*seed, *bp++); } if (bp + 4 <= be) { GU_FNV128_ITERATION(*seed, *bp++); GU_FNV128_ITERATION(*seed, *bp++); GU_FNV128_ITERATION(*seed, *bp++); GU_FNV128_ITERATION(*seed, *bp++); } if (bp + 2 <= be) { GU_FNV128_ITERATION(*seed, *bp++); GU_FNV128_ITERATION(*seed, *bp++); } if (bp < be) { GU_FNV128_ITERATION(*seed, *bp++); } assert(be == bp); } #endif /* _gu_fnv_h_ */ percona-xtradb-cluster-galera/galerautils/src/gu_fnv_bench.c0000644000000000000000000001244312247075736024550 0ustar rootroot00000000000000// Copyright (C) 2012 Codership Oy /*! * @file Benchmark for different hash implementations: * fnv32, fnv64, fnv128, mmh3, md5 from libssl and md5 from crypto++ * * To compile: * g++ -DHAVE_ENDIAN_H -DHAVE_BYTESWAP_H -O3 -Wall -Werror -march=core2 \ * gu_fnv_bench.c gu_mmh3.c -lssl/-lcrypto -lcrypto++ * * To run: * gu_fnv_bench */ #include "gu_fnv.h" #include "gu_mmh3.h" #include "gu_spooky.h" #include "gu_hash.h" #include #include #include #include #include #define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1 #include //#include enum algs { FNV32, FNV64, FNV128, MMH32, MMH128, SPOOKYS, SPOOKY, MD5SSL, MD5CPP, FAST128, TABLE }; static int timer (const void* const buf, ssize_t const len, long long const loops, enum algs const type) { double begin, end; struct timeval tv; const char* alg = "undefined"; size_t volatile h; // this variable serves to prevent compiler from // optimizing out the calls gettimeofday (&tv, NULL); begin = (double)tv.tv_sec + 1.e-6 * tv.tv_usec; long long i; #ifdef EXTERNAL_LOOP #define EXTERNAL_LOOP_BEGIN for (i = 0; i < loops; i++) { #define EXTERNAL_LOOP_END } #define INTERNAL_LOOP_BEGIN #define INTERNAL_LOOP_END #else #define EXTERNAL_LOOP_BEGIN #define EXTERNAL_LOOP_END #define INTERNAL_LOOP_BEGIN for (i = 0; i < loops; i++) { #define INTERNAL_LOOP_END } #endif EXTERNAL_LOOP_BEGIN switch (type) { case FNV32: { alg = "fnv32a"; INTERNAL_LOOP_BEGIN uint32_t hash = GU_FNV32_SEED; gu_fnv32a_internal (buf, len, &hash); h = hash; INTERNAL_LOOP_END break; } case FNV64: { alg = "fnv64a"; INTERNAL_LOOP_BEGIN uint64_t hash = GU_FNV64_SEED;; gu_fnv64a_internal (buf, len, &hash); h = hash; INTERNAL_LOOP_END break; } case FNV128: { alg = "fnv128"; INTERNAL_LOOP_BEGIN gu_uint128_t hash = GU_FNV128_SEED; gu_fnv128a_internal (buf, len, &hash); #if defined(__SIZEOF_INT128__) h = hash; #else h = hash.u32[GU_32LO]; #endif INTERNAL_LOOP_END break; } case MMH32: { alg = "mmh32"; INTERNAL_LOOP_BEGIN h = gu_mmh32 (buf, len); INTERNAL_LOOP_END break; } case MMH128: { alg = "mmh128"; INTERNAL_LOOP_BEGIN gu_uint128_t hash; gu_mmh128 (buf, len, &hash); #if defined(__SIZEOF_INT128__) h = hash; #else h = hash.u32[GU_32LO]; #endif INTERNAL_LOOP_END break; } case SPOOKYS: { alg = "SpookyS"; INTERNAL_LOOP_BEGIN uint64_t hash[2]; gu_spooky_short (buf, len, hash); h = hash[0]; INTERNAL_LOOP_END break; } case SPOOKY: { alg = "Spooky"; INTERNAL_LOOP_BEGIN uint64_t hash[2]; gu_spooky_inline (buf, len, hash); h = hash[0]; INTERNAL_LOOP_END break; } case MD5SSL: { alg = "md5ssl"; INTERNAL_LOOP_BEGIN unsigned char md[MD5_DIGEST_LENGTH]; MD5 ((const unsigned char*)buf, len, md); INTERNAL_LOOP_END break; } case MD5CPP: { alg = "md5cpp"; INTERNAL_LOOP_BEGIN unsigned char md[16]; CryptoPP::Weak::MD5().CalculateDigest(md, (const byte*)buf, len); INTERNAL_LOOP_END break; } case FAST128: { alg = "fast128"; INTERNAL_LOOP_BEGIN uint64_t hash[2]; gu_fast_hash128 (buf, len, hash); h = hash[0]; INTERNAL_LOOP_END break; } case TABLE: { alg = "table"; INTERNAL_LOOP_BEGIN h = gu_table_hash (buf, len); INTERNAL_LOOP_END break; } } EXTERNAL_LOOP_END gettimeofday (&tv, NULL); end = (double)tv.tv_sec + 1.e-6 * tv.tv_usec; end -= begin; return printf ("%s: %lld loops, %6.3f seconds, %8.3f Mb/sec%s\n", alg, loops, end, (double)(loops * len)/end/1024/1024, h ? "" : " "); } int main (int argc, char* argv[]) { ssize_t buf_size = (1<<20); // 1Mb long long loops = 10000; if (argc > 1) buf_size = strtoll (argv[1], NULL, 10); if (argc > 2) loops = strtoll (argv[2], NULL, 10); /* initialization of data buffer */ ssize_t buf_size_int = buf_size / sizeof(int) + 1; int* buf = (int*) malloc (buf_size_int * sizeof(int)); if (!buf) return ENOMEM; while (buf_size_int) buf[--buf_size_int] = rand(); timer (buf, buf_size, loops, FNV32); timer (buf, buf_size, loops, FNV64); timer (buf, buf_size, loops, FNV128); timer (buf, buf_size, loops, MMH32); timer (buf, buf_size, loops, MMH128); timer (buf, buf_size, loops, SPOOKYS); timer (buf, buf_size, loops, SPOOKY); timer (buf, buf_size, loops, MD5SSL); timer (buf, buf_size, loops, MD5CPP); timer (buf, buf_size, loops, FAST128); timer (buf, buf_size, loops, TABLE); return 0; } percona-xtradb-cluster-galera/galerautils/src/gu_hash.h0000644000000000000000000001014612247075736023546 0ustar rootroot00000000000000// Copyright (C) 2012 Codership Oy /** * @file Defines 3 families of standard Galera hash methods * * 1) gu_hash - a general use universal hash: 128, 64 and 32-bit variants. * * 2) gu_fast_hash - optimized for 64-bit Intel CPUs, limited to whole message * only, also comes in 128, 64 and 32-bit flavors. * 3) gu_table_hash - possibly even faster, platform-optimized, globally * inconsistent hash functions to be used only in local hash * tables. Only size_t variants defined. * * 128-bit result is returned through void* parameter as a byte array in * canonical order. * 64/32-bit results are returned as uint64_t/uint32_t integers and thus in host * byte order (require conversion to network/Galera byte order for serialization). * * $Id: gu_hash.h 3055 2013-04-19 20:29:20Z alex $ */ #ifndef _gu_hash_h_ #define _gu_hash_h_ #ifdef __cplusplus extern "C" { #endif #include "gu_fnv.h" #include "gu_mmh3.h" #include "gu_spooky.h" /* * General purpose globally consistent _fast_ hash, if in doubt use that. */ /* This is to hash multipart message */ #define gu_hash_t gu_mmh128_ctx_t #define gu_hash_init(_hash) gu_mmh128_init(_hash) #define gu_hash_append(_hash, _msg, _len) gu_mmh128_append(_hash, _msg, _len) #define gu_hash_get128(_hash, _res) gu_mmh128_get(_hash, _res) #define gu_hash_get64(_hash) gu_mmh128_get64(_hash) #define gu_hash_get32(_hash) gu_mmh128_get32(_hash) /* This is to hash a whole message in one go */ #define gu_hash128(_msg, _len, _res) gu_mmh128(_msg, _len, _res) #define gu_hash64(_msg, _len) gu_mmh128_64(_msg, _len) #define gu_hash32(_msg, _len) gu_mmh128_32(_msg, _len) /* * Hash optimized for speed, can't do multipart messages, but should still * be usable as global identifier */ #define GU_SHORT64_LIMIT 16 #define GU_MEDIUM64_LIMIT 512 static GU_INLINE void gu_fast_hash128 (const void* const msg, size_t const len, void* const res) { if (len < GU_MEDIUM64_LIMIT) { gu_mmh128 (msg, len, res); } else { gu_spooky128 (msg, len, res); } } static GU_FORCE_INLINE uint64_t gu_fast_hash64_short (const void* const msg, size_t const len) { uint64_t res = GU_FNV64_SEED; gu_fnv64a_internal (msg, len, &res); /* mix to improve avalanche effect */ res *= GU_ROTL64(res, 56); return res ^ GU_ROTL64(res, 43); } #define gu_fast_hash64_medium gu_mmh128_64 #define gu_fast_hash64_long gu_spooky64 static GU_INLINE uint64_t gu_fast_hash64 (const void* const msg, size_t const len) { if (len < GU_SHORT64_LIMIT) { return gu_fast_hash64_short (msg, len); } else if (len < GU_MEDIUM64_LIMIT) { return gu_fast_hash64_medium (msg, len); } else { return gu_fast_hash64_long (msg, len); } } #define gu_fast_hash32_short gu_mmh32 #define gu_fast_hash32_medium gu_mmh128_32 #define gu_fast_hash32_long gu_spooky32 #define GU_SHORT32_LIMIT 32 #define GU_MEDIUM32_LIMIT 512 static GU_INLINE uint32_t gu_fast_hash32 (const void* const msg, size_t const len) { if (len < GU_SHORT32_LIMIT) { return gu_fast_hash32_short (msg, len); } else if (len < GU_MEDIUM32_LIMIT) { return gu_fast_hash32_medium (msg, len); } else { return gu_fast_hash32_long (msg, len); } } /* * Platform-optimized hashes only for local hash tables, don't produce globally * consistent results. No 128-bit version for obvious reasons. * * Resulting gu_table_hash() will be the fastest hash function returning size_t */ #if GU_WORDSIZE == 64 #define gu_table_hash gu_fast_hash64 /* size_t is normally 64-bit here */ #elif GU_WORDSIZE == 32 /* on 32-bit platform MurmurHash32 is only insignificantly slower than FNV32a * on messages < 10 bytes but produces far better hash. */ #define gu_table_hash gu_mmh32 /* size_t is normally 32-bit here */ #else /* GU_WORDSIZE neither 64 nor 32 bits */ # error Unsupported wordsize! #endif #ifdef __cplusplus } #endif #endif /* _gu_hash_h_ */ percona-xtradb-cluster-galera/galerautils/src/gu_int128.h0000644000000000000000000001260012247075736023645 0ustar rootroot00000000000000// Copyright (C) 2012 Codership Oy /** * @file 128-bit arithmetic macros. This is so far needed only for FNV128 * hash algorithm * * $Id: gu_int128.h 2881 2012-10-12 15:01:04Z alex $ */ #ifndef _gu_int128_h_ #define _gu_int128_h_ #include "gu_arch.h" #include "gu_byteswap.h" #include #if defined(__SIZEOF_INT128__) typedef int __attribute__((__mode__(__TI__))) int128_t; typedef unsigned int __attribute__((__mode__(__TI__))) uint128_t; typedef int128_t gu_int128_t; typedef uint128_t gu_uint128_t; #define GU_SET128(_a, hi64, lo64) _a = (((uint128_t)hi64) << 64) + lo64 #define GU_MUL128_INPLACE(_a, _b) _a *= _b #define GU_IMUL128_INPLACE(_a, _b) GU_MUL128_INPLACE(_a, _b) #define GU_EQ128(_a, _b) (_a == _b) #else /* Uncapable of 16-byte integer arythmetic */ #if defined(GU_LITTLE_ENDIAN) #define GU_64LO 0 #define GU_64HI 1 #define GU_32LO 0 #define GU_32HI 3 #define GU_32_0 0 #define GU_32_1 1 #define GU_32_2 2 #define GU_32_3 3 typedef union gu_int128 { uint64_t u64[2]; uint32_t u32[4]; struct {uint32_t lo; uint64_t mid; int32_t hi;}__attribute__((packed)) m; #ifdef __cplusplus gu_int128() : m() {} gu_int128(int64_t hi, uint64_t lo) : m() { u64[0] = lo; u64[1] = hi; } #endif } gu_int128_t; typedef union gu_uint128 { uint64_t u64[2]; uint32_t u32[4]; struct {uint32_t lo; uint64_t mid; uint32_t hi;}__attribute__((packed)) m; #ifdef __cplusplus gu_uint128() : m() {} gu_uint128(uint64_t hi, uint64_t lo) : m() { u64[0] = lo; u64[1] = hi; } #endif } gu_uint128_t; #ifdef __cplusplus #define GU_SET128(_a, hi64, lo64) _a = gu_uint128(hi64, lo64) #else #define GU_SET128(_a, hi64, lo64) _a = { .u64 = { lo64, hi64 } } #endif #define GU_MUL128_INPLACE(_a,_b) { \ uint64_t m00 = (uint64_t)(_a).u32[0] * (_b).u32[0]; \ uint64_t m10 = (uint64_t)(_a).u32[1] * (_b).u32[0]; \ uint64_t m20 = (uint64_t)(_a).u32[2] * (_b).u32[0]; \ uint64_t m01 = (uint64_t)(_a).u32[0] * (_b).u32[1]; \ uint64_t m02 = (uint64_t)(_a).u32[0] * (_b).u32[2]; \ uint64_t m11 = (uint64_t)(_a).u32[1] * (_b).u32[1]; \ uint32_t m30 = (_a).u32[3] * (_b).u32[0]; \ uint32_t m21 = (_a).u32[2] * (_b).u32[1]; \ uint32_t m12 = (_a).u32[1] * (_b).u32[2]; \ uint32_t m03 = (_a).u32[0] * (_b).u32[3]; \ (_a).u64[GU_64LO] = m00; (_a).u64[GU_64HI] = 0; \ (_a).m.mid += m10; (_a).m.hi += ((_a).m.mid < m10); \ (_a).m.mid += m01; (_a).m.hi += ((_a).m.mid < m01); \ (_a).u64[GU_64HI] += m20 + m11 + m02; \ (_a).u32[GU_32HI] += m30 + m21 + m12 + m03; \ } #else /* Big-Endian */ #define GU_64HI 0 #define GU_64LO 1 #define GU_32HI 0 #define GU_32LO 3 typedef union gu_int128 { uint64_t u64[2]; uint32_t u32[4]; struct {int32_t hi; uint64_t mid; uint32_t lo;}__attribute__((packed)) m; #ifdef __cplusplus gu_int128() {} gu_int128(int64_t hi, uint64_t lo) { u64[0] = hi; u64[1] = lo; } #endif } gu_int128_t; typedef union gu_uint128 { uint64_t u64[2]; uint32_t u32[4]; struct {uint32_t hi; uint64_t mid; uint32_t lo;}__attribute__((packed)) m; #ifdef __cplusplus gu_uint128() {} gu_uint128(uint64_t hi, uint64_t lo) { u64[0] = hi; u64[1] = lo; } #endif } gu_uint128_t; #ifdef __cplusplus #define GU_SET128(_a, hi64, lo64) _a = gu_uint128(hi64, lo64) #else #define GU_SET128(_a, hi64, lo64) _a = { .u64 = { hi64, lo64 } } #endif #define GU_MUL128_INPLACE(_a,_b) { \ uint64_t m33 = (uint64_t)_a.u32[3] * _b.u32[3]; \ uint64_t m23 = (uint64_t)_a.u32[2] * _b.u32[3]; \ uint64_t m13 = (uint64_t)_a.u32[1] * _b.u32[3]; \ uint64_t m32 = (uint64_t)_a.u32[3] * _b.u32[2]; \ uint64_t m31 = (uint64_t)_a.u32[3] * _b.u32[1]; \ uint64_t m22 = (uint64_t)_a.u32[2] * _b.u32[2]; \ uint32_t m30 = _a.u32[3] * _b.u32[0]; \ uint32_t m21 = _a.u32[2] * _b.u32[1]; \ uint32_t m12 = _a.u32[1] * _b.u32[2]; \ uint32_t m03 = _a.u32[0] * _b.u32[3]; \ _a.u64[GU_64LO] = m00; _a.u64[GU_64HI] = 0; \ _a.m.mid += m23; _a.m.hi += (_a.m.mid < m23); \ _a.m.mid += m32; _a.m.hi += (_a.m.mid < m32); \ _a.u64[GU_64HI] += m13 + m22 + m31; \ _a.u32[GU_32HI] += m30 + m21 + m12 + m03; \ } #endif /* Big-Endian */ #define GU_IMUL128_INPLACE(_a, _b) { \ uint32_t sign = ((_a).u32[GU_32HI] ^ (_b).u32[GU_32HI]) & 0x80000000UL; \ GU_MUL128_INPLACE (_a, _b); \ (_a).u32[GU_32HI] |= sign; \ } #define GU_EQ128(_a, _b) (!memcmp(&_a,&_b,sizeof(_a))) #endif /* __SIZEOF_INT128__ */ /* Not sure how to make it both portable, efficient and still follow the * signature of other byteswap functions at the same time. * So this one does inplace conversion. */ #ifdef __cplusplus extern "C" { #endif static inline void gu_bswap128 (gu_uint128_t* const arg) { uint64_t* x = (uint64_t*)arg; uint64_t tmp = gu_bswap64(x[0]); x[0] = gu_bswap64(x[1]); x[1] = tmp; } #ifdef __cplusplus } #endif #ifdef GU_LITTLE_ENDIAN # define gu_le128(x) {} # define gu_be128(x) gu_bswap128(x) #else # define gu_le128(x) gu_bswap128(x) # define gu_be128(x) {} #endif /* GU_LITTLE_ENDIAN */ #define htog128(x) gu_le128(x) #define gtoh128(x) htog128(x) #endif /* _gu_int128_h_ */ percona-xtradb-cluster-galera/galerautils/src/gu_limits.c0000644000000000000000000000465012247075736024122 0ustar rootroot00000000000000// Copyright (C) 2013 Codership Oy /** * @file system limit macros * * $Id:$ */ #include #include #include #include #include "gu_limits.h" #include "gu_log.h" #if defined(__APPLE__) #include long gu_darwin_phys_pages (void) { /* Note: singleton pattern would be useful here */ vm_statistics64_data_t vm_stat; unsigned int count = HOST_VM_INFO64_COUNT; kern_return_t ret = host_statistics64 (mach_host_self (), HOST_VM_INFO64, (host_info64_t) &vm_stat, &count); if (ret != KERN_SUCCESS) { gu_error ("host_statistics64 failed with code %d", ret); return 0; } /* This gives a value a little less than physical memory of computer */ return vm_stat.free_count + vm_stat.active_count + vm_stat.inactive_count + vm_stat.wire_count; /* Exact value may be obtain via sysctl ({CTL_HW, HW_MEMSIZE}) */ /* Note: sysctl is 60% slower compared to host_statistics64 */ } long gu_darwin_avphys_pages (void) { vm_statistics64_data_t vm_stat; unsigned int count = HOST_VM_INFO64_COUNT; kern_return_t ret = host_statistics64 (mach_host_self (), HOST_VM_INFO64, (host_info64_t) &vm_stat, &count); if (ret != KERN_SUCCESS) { gu_error ("host_statistics64 failed with code %d", ret); return 0; } /* Note: vm_stat.free_count == vm_page_free_count + vm_page_speculative_count */ return vm_stat.free_count - vm_stat.speculative_count; } #elif defined(__FreeBSD__) #include // VM_TOTAL #include // struct vmtotal long gu_freebsd_avphys_pages (void) { /* TODO: 1) sysctlnametomib may be called once */ /* 2) vm.stats.vm.v_cache_count is potentially free memory too */ int mib_vm_stats_vm_v_free_count[4]; size_t mib_sz = 4; int rc = sysctlnametomib ("vm.stats.vm.v_free_count", mib_vm_stats_vm_v_free_count, &mib_sz); if (rc != 0) { gu_error ("sysctlnametomib(vm.stats.vm.v_free_count) failed with code %d", rc); return 0; } unsigned int vm_stats_vm_v_free_count; size_t sz = sizeof (vm_stats_vm_v_free_count); rc = sysctl (mib_vm_stats_vm_v_free_count, mib_sz, &vm_stats_vm_v_free_count, &sz, NULL, 0); if (rc != 0) { gu_error ("sysctl(vm.stats.vm.v_free_count) failed with code %d", rc); return 0; } return vm_stats_vm_v_free_count; } #endif /* __FreeBSD__ */ percona-xtradb-cluster-galera/galerautils/src/gu_limits.h0000644000000000000000000000337212247075736024127 0ustar rootroot00000000000000// Copyright (C) 2008 Codership Oy /** * @file system limit macros * * $Id: gu_limits.h 3218 2013-08-23 12:14:28Z alex $ */ #ifndef _gu_limits_h_ #define _gu_limits_h_ #include #ifdef __cplusplus extern "C" { #endif # if defined(__APPLE__) long gu_darwin_phys_pages (void); long gu_darwin_avphys_pages (void); # elif defined(__FreeBSD__) long gu_freebsd_avphys_pages (void); # endif #ifdef __cplusplus } #endif #if defined(__APPLE__) static inline size_t gu_page_size() { return getpagesize(); } static inline size_t gu_phys_pages() { return gu_darwin_phys_pages(); } static inline size_t gu_avphys_pages() { return gu_darwin_avphys_pages(); } #elif defined(__FreeBSD__) static inline size_t gu_page_size() { return sysconf(_SC_PAGESIZE); } static inline size_t gu_phys_pages() { return sysconf(_SC_PHYS_PAGES); } static inline size_t gu_avphys_pages() { return gu_freebsd_avphys_pages(); } #else /* !__APPLE__ && !__FreeBSD__ */ static inline size_t gu_page_size() { return sysconf(_SC_PAGESIZE); } static inline size_t gu_phys_pages() { return sysconf(_SC_PHYS_PAGES); } static inline size_t gu_avphys_pages() { return sysconf(_SC_AVPHYS_PAGES); } #endif /* !__APPLE__ && !__FreeBSD__ */ static inline size_t gu_avphys_bytes() { // to detect overflow on systems with >4G of RAM, see #776 unsigned long long avphys = gu_avphys_pages(); avphys *= gu_page_size(); size_t max = -1; return (avphys < max ? avphys : max); } #include #define GU_ULONG_MAX ULONG_MAX #define GU_LONG_MAX LONG_MAX #define GU_LONG_MIN LONG_MIN #define GU_ULONG_LONG_MAX ULLONG_MAX #define GU_LONG_LONG_MAX LLONG_MAX #define GU_LONG_LONG_MIN LLONG_MIN #endif /* _gu_limits_h_ */ percona-xtradb-cluster-galera/galerautils/src/gu_lock.hpp0000644000000000000000000000323512247075736024114 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy * */ #ifndef __GU_LOCK__ #define __GU_LOCK__ #include #include #include "gu_exception.hpp" #include "gu_logger.hpp" #include "gu_mutex.hpp" #include "gu_cond.hpp" #include "gu_datetime.hpp" namespace gu { class Lock { pthread_mutex_t* const value; Lock (const Lock&); Lock& operator=(const Lock&); public: Lock (const Mutex& mtx) : value(&mtx.value) { int err = pthread_mutex_lock (value); if (gu_unlikely(err)) { std::string msg = "Mutex lock failed: "; msg = msg + strerror(err); throw Exception(msg.c_str(), err); } } virtual ~Lock () { int err = pthread_mutex_unlock (value); if (gu_unlikely(err)) { log_fatal << "Mutex unlock failed: " << err << " (" << strerror(err) << "), Aborting."; ::abort(); } // log_debug << "Unlocked mutex " << value; } inline void wait (const Cond& cond) { cond.ref_count++; pthread_cond_wait (&(cond.cond), value); cond.ref_count--; } inline void wait (const Cond& cond, const datetime::Date& date) { timespec ts; date._timespec(ts); cond.ref_count++; int ret = pthread_cond_timedwait (&(cond.cond), value, &ts); cond.ref_count--; if (gu_unlikely(ret)) gu_throw_error(ret); } }; } #endif /* __GU_LOCK__ */ percona-xtradb-cluster-galera/galerautils/src/gu_lock_step.c0000644000000000000000000000677212247075736024613 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gu_lock_step.c 3336 2013-10-28 07:41:56Z teemu $ */ /* * Universally Unique IDentifier. RFC 4122. * Time-based implementation. * */ #include // abort() #include // error codes #include #include #include // strerror() #include "gu_log.h" #include "gu_assert.h" #include "gu_time.h" #include "gu_lock_step.h" void gu_lock_step_init (gu_lock_step_t* ls) { gu_mutex_init (&ls->mtx, NULL); gu_cond_init (&ls->cond, NULL); ls->wait = 0; ls->cont = 0; ls->enabled = false; } void gu_lock_step_destroy (gu_lock_step_t* ls) { // this is not really fool-proof, but that's not for fools to use while (gu_lock_step_cont(ls, 10)) {}; gu_cond_destroy (&ls->cond); gu_mutex_destroy (&ls->mtx); assert (0 == ls->wait); } void gu_lock_step_enable (gu_lock_step_t* ls, bool enable) { if (!gu_mutex_lock (&ls->mtx)) { ls->enabled = enable; gu_mutex_unlock (&ls->mtx); } else { gu_fatal ("Mutex lock failed"); assert (0); abort(); } } void gu_lock_step_wait (gu_lock_step_t* ls) { if (!gu_mutex_lock (&ls->mtx)) { if (ls->enabled) { if (!ls->cont) { // wait for signal ls->wait++; gu_cond_wait (&ls->cond, &ls->mtx); } else { // signal to signaller gu_cond_signal (&ls->cond); ls->cont--; } } gu_mutex_unlock (&ls->mtx); } else { gu_fatal ("Mutex lock failed"); assert (0); abort(); } } /* returns how many waiters are there */ long gu_lock_step_cont (gu_lock_step_t* ls, long timeout_ms) { long ret = -1; if (!gu_mutex_lock (&ls->mtx)) { if (ls->enabled) { if (ls->wait > 0) { // somebody's waiting ret = ls->wait; gu_cond_signal (&ls->cond); ls->wait--; } else if (timeout_ms > 0) { // wait for waiter // what a royal mess with times! Why timeval exists? struct timeval now; struct timespec timeout; long err; gettimeofday (&now, NULL); gu_timeval_add (&now, timeout_ms * 0.001); timeout.tv_sec = now.tv_sec; timeout.tv_nsec = now.tv_usec * 1000; ls->cont++; do { err = gu_cond_timedwait (&ls->cond, &ls->mtx, &timeout); } while (EINTR == err); assert ((0 == err) || (ETIMEDOUT == err && ls->cont > 0)); ret = (0 == err); // successful rendezvous with waiter ls->cont -= (0 != err); // self-decrement in case of error } else if (timeout_ms < 0) { // wait forever long err; ls->cont++; err = gu_cond_wait (&ls->cond, &ls->mtx); ret = (0 == err); // successful rendezvous with waiter ls->cont -= (0 != err); // self-decrement in case of error } else { // don't wait ret = 0; } } gu_mutex_unlock (&ls->mtx); } else { gu_fatal ("Mutex lock failed"); assert (0); abort(); } return ret; } percona-xtradb-cluster-galera/galerautils/src/gu_lock_step.h0000644000000000000000000000172312247075736024607 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy * * $Id: gu_lock_step.h 3336 2013-10-28 07:41:56Z teemu $ */ // This is a small class to facilitate lock-stepping in multithreaded unit tests #ifndef _gu_lock_step_h_ #define _gu_lock_step_h_ #include #include "gu_mutex.h" typedef struct gu_lock_step { gu_mutex_t mtx; gu_cond_t cond; long wait; long cont; bool enabled; } gu_lock_step_t; extern void gu_lock_step_init (gu_lock_step_t* ls); /* enable or disable lock-stepping */ extern void gu_lock_step_enable (gu_lock_step_t* ls, bool enable); extern void gu_lock_step_wait (gu_lock_step_t* ls); /* returns how many waiters there were, * waits for timeout_ms milliseconds if no waiters, if timeout_ms < 0 waits forever, * if 0 - no wait at all */ extern long gu_lock_step_cont (gu_lock_step_t* ls, long timeout_ms); extern void gu_lock_step_destroy (gu_lock_step_t* ls); #endif /* _gu_lock_step_h_ */ percona-xtradb-cluster-galera/galerautils/src/gu_log.c0000644000000000000000000000777712247075736023417 0ustar rootroot00000000000000// Copyright (C) 2007 Codership Oy /** * @file Logging functions definitions * * $Id: gu_log.c 1412 2010-01-24 19:50:02Z alex $ */ #include #include #include #include #include #include #include #include "gu_log.h" #include "gu_macros.h" /* Global configurable variables */ static FILE* gu_log_file = NULL; bool gu_log_self_tstamp = false; gu_log_severity_t gu_log_max_level = GU_LOG_INFO; int gu_conf_set_log_file (FILE *file) { gu_debug ("Log file changed by application"); if (file) { gu_log_file = file; } else { gu_log_file = stderr; } return 0; } int gu_conf_self_tstamp_on () { gu_debug ("Turning self timestamping on"); gu_log_self_tstamp = true; return 0; } int gu_conf_self_tstamp_off () { gu_debug ("Turning self timestamping off"); gu_log_self_tstamp = false; return 0; } int gu_conf_debug_on () { gu_log_max_level = GU_LOG_DEBUG; gu_debug ("Turning debug on"); return 0; } int gu_conf_debug_off () { gu_debug ("Turning debug off"); gu_log_max_level = GU_LOG_INFO; return 0; } /** Returns current timestamp in the provided buffer */ static inline int log_tstamp (char* tstamp, size_t const len) { int ret = 0; struct tm date; struct timeval time; gettimeofday (&time, NULL); localtime_r (&time.tv_sec, &date); /* 23 symbols */ ret = snprintf (tstamp, len, "%04d-%02d-%02d %02d:%02d:%02d.%03d ", date.tm_year + 1900, date.tm_mon + 1, date.tm_mday, date.tm_hour, date.tm_min, date.tm_sec, (int)time.tv_usec / 1000); return ret; } const char* gu_log_level_str[GU_LOG_DEBUG + 2] = { "FATAL: ", "ERROR: ", " WARN: ", " INFO: ", "DEBUG: ", "XXXXX: " }; /** * @function * Default logging function: simply writes to stderr or gu_log_file if set. */ void gu_log_cb_default (int severity, const char* msg) { FILE* log_file = gu_log_file ? gu_log_file : stderr; fputs (msg, log_file); fputc ('\n', log_file); fflush (log_file); } /** * Log function handle. * Can be changed by application through gu_conf_set_log_callback() */ gu_log_cb_t gu_log_cb = gu_log_cb_default; int gu_conf_set_log_callback (gu_log_cb_t callback) { if (callback) { gu_debug ("Logging function changed by application"); gu_log_cb = callback; } else { gu_debug ("Logging function restored to default"); gu_log_cb = gu_log_cb_default; } return 0; } int gu_log (gu_log_severity_t severity, const char* file, const char* function, const int line, ...) { va_list ap; int max_string = 2048; char string[max_string]; /** @note: this can cause stack overflow * in kernel mode (both Linux and Windows). */ char* str = string; int len; if (gu_log_self_tstamp) { len = log_tstamp (str, max_string); str += len; max_string -= len; } if (gu_likely(max_string > 0)) { const char* log_level_str = gu_log_cb_default == gu_log_cb ? gu_log_level_str[severity] : ""; /* provide file:func():line info only if debug logging is on */ if (gu_likely(!gu_log_debug && severity > GU_LOG_ERROR)) { len = snprintf (str, max_string, "%s", log_level_str); } else { len = snprintf (str, max_string, "%s%s:%s():%d: ", log_level_str, file, function, line); } str += len; max_string -= len; va_start (ap, line); { const char* format = va_arg (ap, const char*); if (gu_likely(max_string > 0 && NULL != format)) { vsnprintf (str, max_string, format, ap); } } va_end (ap); } /* actual logging */ gu_log_cb (severity, string); return 0; } percona-xtradb-cluster-galera/galerautils/src/gu_log.h0000644000000000000000000000541612247075736023410 0ustar rootroot00000000000000// Copyright (C) 2007 Codership Oy /** * @file Logging API * * $Id: gu_log.h 2272 2011-07-28 23:24:41Z alex $ */ #ifndef _gu_log_h_ #define _gu_log_h_ #include /* For NULL */ #if defined(__cplusplus) extern "C" { #endif /** * @typedef * Defines severity classes for log messages: * FATAL - is a fatal malfunction of the library which cannot be recovered * from. Application must close. * error - error condition in the library which prevents further normal * operation but can be recovered from by the application. E.g. EAGAIN. * warn - some abnormal condition which library can recover from by itself. * * info - just an informative log message. * * debug - debugging message. */ typedef enum gu_log_severity { GU_LOG_FATAL, GU_LOG_ERROR, GU_LOG_WARN, GU_LOG_INFO, GU_LOG_DEBUG } gu_log_severity_t; /** * @typedef * Defines a type of callback function that application can provide * to do the logging */ typedef void (*gu_log_cb_t) (int severity, const char* msg); /** Helper for macros defined below. Should not be called directly. */ extern int gu_log (gu_log_severity_t severity, const char* file, const char* function, const int line, ...); /** This variable is made global only for the purpose of using it in * gu_debug() macro and avoid calling gu_log() when debug is off. * Don't use it directly! */ extern gu_log_severity_t gu_log_max_level; #define gu_log_debug (GU_LOG_DEBUG == gu_log_max_level) #if defined(__cplusplus) } #endif #if !defined(__cplusplus) || defined(GALERA_LOG_H_ENABLE_CXX) // NOTE: don't add "\n" here even if you really want to do it #define GU_LOG_C(level, ...)\ gu_log(level, __FILE__, __PRETTY_FUNCTION__, __LINE__,\ ## __VA_ARGS__, NULL) /** * @name Logging macros. * Must be implemented as macros to report the location of the code where * they are called. */ /*@{*/ #define gu_fatal(...)\ GU_LOG_C(GU_LOG_FATAL, ## __VA_ARGS__, NULL) #define gu_error(...)\ GU_LOG_C(GU_LOG_ERROR, ## __VA_ARGS__, NULL) #define gu_warn(...)\ GU_LOG_C(GU_LOG_WARN, ## __VA_ARGS__, NULL) #define gu_info(...)\ GU_LOG_C(GU_LOG_INFO, ## __VA_ARGS__, NULL) #define gu_debug(...)\ (gu_log_max_level < GU_LOG_DEBUG ? 0 : \ GU_LOG_C(GU_LOG_DEBUG, ## __VA_ARGS__, NULL)) /*@}*/ #endif /* __cplusplus */ #endif /* _gu_log_h_ */ #ifdef __GU_LOGGER__ // C++ logger should use the same stuff, so export it #ifndef _gu_log_extra_ #define _gu_log_extra_ extern "C" { extern bool gu_log_self_tstamp; extern gu_log_cb_t gu_log_cb; extern void gu_log_cb_default (int, const char*); extern const char* gu_log_level_str[]; } #endif /* _gu_log_extra_ */ #endif /* __GU_LOGGER__ */ percona-xtradb-cluster-galera/galerautils/src/gu_logger.cpp0000644000000000000000000000660112247075736024436 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy * * This code is based on an excellent article at Dr.Dobb's: * http://www.ddj.com/cpp/201804215?pgno=1 */ #include #include #include #include #include #include "gu_logger.hpp" #include "gu_string.hpp" #include #include #include using std::string; using std::vector; using std::set; namespace gu { class DebugFilter { set filter; public: DebugFilter() : filter() { if (::getenv("LOGGER_DEBUG_FILTER")) { set_filter(::getenv("LOGGER_DEBUG_FILTER")); } } ~DebugFilter() {} void set_filter(const string& str) { vector dvec = gu::strsplit(str, ','); for (vector::const_iterator i = dvec.begin(); i != dvec.end(); ++i) { filter.insert(*i); } } size_t size() const { return filter.size(); } bool is_set(const string& str) const { return filter.find(str) != filter.end() || filter.find(str.substr(0, str.find_first_of(":"))) != filter.end(); } }; static DebugFilter debug_filter; void Logger::set_debug_filter(const string& str) { debug_filter.set_filter(str); } bool Logger::no_debug(const string& file, const string& func, const int line) { return debug_filter.size() > 0 && debug_filter.is_set(func) == false; } #ifndef _gu_log_h_ void Logger::enable_tstamp (bool yes) { do_timestamp = yes; } void Logger::enable_debug (bool yes) { if (yes) { max_level = LOG_DEBUG; } else { max_level = LOG_INFO; } } void Logger::default_logger (int lvl, const char* msg) { fputs (msg, stderr); fputc ('\n', stderr); fflush (stderr); } void Logger::set_logger (LogCallback cb) { if (0 == cb) { logger = default_logger; } else { logger = cb; } } static const char* level_str[LOG_MAX] = { "FATAL: ", "ERROR: ", " WARN: ", " INFO: ", "DEBUG: " }; bool Logger::do_timestamp = false; LogLevel Logger::max_level = LOG_INFO; LogCallback Logger::logger = default_logger; #else #define do_timestamp (gu_log_self_tstamp == true) #define level_str gu_log_level_str #endif // _gu_log_h_ void Logger::prepare_default() { if (do_timestamp) { using namespace std; struct tm date; struct timeval time; gettimeofday (&time, NULL); localtime_r (&time.tv_sec, &date); os << date.tm_year + 1900 << '-' << setw(2) << setfill('0') << (date.tm_mon + 1) << '-' << setw(2) << setfill('0') << date.tm_mday << ' ' << setw(2) << setfill('0') << date.tm_hour << ':' << setw(2) << setfill('0') << date.tm_min << ':' << setw(2) << setfill('0') << date.tm_sec << '.' << setw(3) << setfill('0') << (time.tv_usec / 1000) << ' '; } os << level_str[level]; } } percona-xtradb-cluster-galera/galerautils/src/gu_logger.hpp0000644000000000000000000000662712247075736024453 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy * * This code is based on an excellent article at Dr.Dobb's: * http://www.ddj.com/cpp/201804215?pgno=1 * * It looks ugly because it has to integrate with C logger - * in order to produce identical output */ #ifndef __GU_LOGGER__ #define __GU_LOGGER__ #include extern "C" { #include "gu_log.h" #include "gu_conf.h" } namespace gu { // some portability stuff #ifdef _gu_log_h_ enum LogLevel { LOG_FATAL = GU_LOG_FATAL, LOG_ERROR = GU_LOG_ERROR, LOG_WARN = GU_LOG_WARN, LOG_INFO = GU_LOG_INFO, LOG_DEBUG = GU_LOG_DEBUG, LOG_MAX }; typedef gu_log_cb_t LogCallback; #else enum LogLevel { LOG_FATAL, LOG_ERROR, LOG_WARN, LOG_INFO, LOG_DEBUG, LOG_MAX }; typedef void (*LogCallback) (int, const char*); #endif class Logger { private: Logger(const Logger&); Logger& operator =(const Logger&); void prepare_default (); const LogLevel level; #ifndef _gu_log_h_ static LogLevel max_level; static bool do_timestamp; static LogCallback logger; static void default_logger (int, const char*); #else #define max_level gu_log_max_level #define logger gu_log_cb #define default_logger gu_log_cb_default #endif protected: std::ostringstream os; public: Logger(LogLevel _level = LOG_INFO) : level (_level), os () {} virtual ~Logger() { logger (level, os.str().c_str()); } std::ostringstream& get(const char* file, const char* func, int line) { if (default_logger == logger) { prepare_default(); // prefix with timestamp and log level } /* provide file:func():line info only when debug logging is on */ if (static_cast(LOG_DEBUG) == static_cast(max_level)) { os << file << ':' << func << "():" << line << ": "; } return os; } static bool no_log (LogLevel lvl) { return (static_cast(lvl) > static_cast(max_level)); } static void set_debug_filter(const std::string&); static bool no_debug(const std::string&, const std::string&, const int); #ifndef _gu_log_h_ static void enable_tstamp (bool); static void enable_debug (bool); static void set_logger (LogCallback); #endif }; #define GU_LOG_CPP(level) \ if (gu::Logger::no_log(level) || \ (level == gu::LOG_DEBUG && \ gu::Logger::no_debug(__FILE__, __FUNCTION__, __LINE__))) {} \ else gu::Logger(level).get(__FILE__, __FUNCTION__, __LINE__) // USAGE: LOG(level) << item_1 << item_2 << ... << item_n; #define log_fatal GU_LOG_CPP(gu::LOG_FATAL) #define log_error GU_LOG_CPP(gu::LOG_ERROR) #define log_warn GU_LOG_CPP(gu::LOG_WARN) #define log_info GU_LOG_CPP(gu::LOG_INFO) #define log_debug GU_LOG_CPP(gu::LOG_DEBUG) } #endif // __GU_LOGGER__ percona-xtradb-cluster-galera/galerautils/src/gu_macros.h0000644000000000000000000000337712247075736024117 0ustar rootroot00000000000000// Copyright (C) 2007-2013 Codership Oy /** * @file Miscellaneous macros * * $Id: gu_macros.h 3336 2013-10-28 07:41:56Z teemu $ */ #ifndef _gu_macros_h_ #define _gu_macros_h_ /* * Platform-dependent macros */ #if defined(_MSC_VER) # define GU_NORETURN __declspec(noreturn) # define GU_INLINE __forceinline # define GU_FORCE_INLINE __forceinline # define GU_UNUSED # define GU_LONG(x) (x) # define GU_ULONG(x) (x) # define GU_LONG_LONG(x) (x) # define GU_ULONG_LONG(x) (x) #else /* !defined(_MSC_VER) */ # define GU_NORETURN __attribute__((noreturn)) # define GU_INLINE inline # define GU_FORCE_INLINE inline __attribute__((always_inline)) # define GU_UNUSED __attribute__((unused)) # define GU_LONG(x) (x##L) # define GU_ULONG(x) (x##LU) # define GU_LONG_LONG(x) (x##LL) # define GU_ULONG_LONG(x) (x##LLU) #endif /* !defined(_MSC_VER) */ /* * End of paltform-dependent macros */ /* "Shamelessly stolen" (tm) goods from Linux kernel */ /* * min()/max() macros that also do * strict type-checking.. See the * "unnecessary" pointer comparison. */ #if 0 // typeof() is not in C99 #define GU_MAX(x,y) ({ \ typeof(x) _x = (x); \ typeof(y) _y = (y); \ (void) (&_x == &_y); \ _x > _y ? _x : _y; }) #define GU_MIN(x,y) ({ \ typeof(x) _x = (x); \ typeof(y) _y = (y); \ (void) (&_x == &_y); \ _x < _y ? _x : _y; }) #endif #define gu_offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #if __GNUC__ >= 3 # define gu_likely(x) __builtin_expect((x), 1) # define gu_unlikely(x) __builtin_expect((x), 0) #else # define gu_likely(x) (x) # define gu_unlikely(x) (x) #endif #endif /* _gu_macros_h_ */ percona-xtradb-cluster-galera/galerautils/src/gu_macros.hpp0000644000000000000000000000104212247075736024442 0ustar rootroot00000000000000// Copyright (C) 2009 Codership Oy /** * @file Miscellaneous C++-related macros * * $Id: gu_macros.hpp 3336 2013-10-28 07:41:56Z teemu $ */ #ifndef _gu_macros_hpp_ #define _gu_macros_hpp_ /* To protect against "old-style" casts in libc macros * must be included after respective libc headers */ #if defined(SIG_IGN) extern "C" { static void (* const GU_SIG_IGN)(int) = SIG_IGN; } #endif #if defined(MAP_FAILED) extern "C" { static const void* const GU_MAP_FAILED = MAP_FAILED; } #endif #endif /* _gu_macros_hpp_ */ percona-xtradb-cluster-galera/galerautils/src/gu_mem.c0000644000000000000000000001016512247075736023375 0ustar rootroot00000000000000// Copyright (C) 2007 Codership Oy /** * Debugging versions of memmory functions * * $Id: gu_mem.c 3348 2013-11-02 10:56:57Z alex $ */ #include #include #include #include "gu_mem.h" #include "gu_log.h" /* Some global counters - can be inspected by gdb */ static volatile ssize_t gu_mem_total = 0; static volatile ssize_t gu_mem_allocs = 0; static volatile ssize_t gu_mem_reallocs = 0; static volatile ssize_t gu_mem_frees = 0; typedef struct mem_head { const char* file; unsigned int line; size_t used; size_t allocated; uint32_t signature; } mem_head_t; #define MEM_SIGNATURE 0x13578642 /**< Our special marker */ // Returns pointer to the first byte after the head structure #define TAIL(head) ((void*)((mem_head_t*)(head) + 1)) // Returns pointer to the head preceding tail #define HEAD(tail) ((mem_head_t*)(tail) - 1) void* gu_malloc_dbg (size_t size, const char* file, unsigned int line) { if (size) { size_t const total_size = size + sizeof(mem_head_t); mem_head_t* const ret = (mem_head_t*) malloc (total_size); if (ret) { gu_mem_total += total_size; gu_mem_allocs++; ret->signature = MEM_SIGNATURE; ret->allocated = total_size; ret->used = size; ret->file = file; ret->line = line; // cppcheck-suppress memleak return TAIL(ret); } } return NULL; } void* gu_calloc_dbg (size_t nmemb, size_t size, const char* file, unsigned int line) { if (size != 0 && nmemb != 0) { size_t const total_size = size*nmemb + sizeof(mem_head_t); mem_head_t* const ret = (mem_head_t*) calloc (total_size, 1); if (ret) { size_t const total_size = size*nmemb + sizeof(mem_head_t); gu_mem_total += total_size; gu_mem_allocs++; ret->signature = MEM_SIGNATURE; ret->allocated = total_size; ret->used = size; ret->file = file; ret->line = line; return TAIL(ret); } } return NULL; } void* gu_realloc_dbg (void* ptr, size_t size, const char* file, unsigned int line) { if (ptr) { if (size > 0) { mem_head_t* const old = HEAD(ptr); if (MEM_SIGNATURE != old->signature) { gu_error ("Attempt to realloc uninitialized pointer at " "file: %s, line: %d", file, line); assert (0); } size_t const total_size = size + sizeof(mem_head_t); mem_head_t* const ret = (mem_head_t*) realloc (old, total_size); if (ret) { gu_mem_reallocs++; gu_mem_total -= ret->allocated; // old size ret->allocated = total_size; gu_mem_total += ret->allocated; // new size ret->used = size; ret->file = file; ret->line = line; return TAIL(ret); } else { // realloc failed return NULL; } } else { gu_free_dbg (ptr, file, line); return NULL; } } else { return gu_malloc_dbg (size, file, line); } return NULL; } void gu_free_dbg (void* ptr, const char* file, unsigned int line) { mem_head_t* head; if (NULL == ptr) { gu_debug ("Attempt to free NULL pointer at file: %s, line: %d", file, line); return; /* As per specification - no operation is performed */ } head = HEAD(ptr); if (MEM_SIGNATURE != head->signature) { gu_error ("Attempt to free uninitialized pointer " "at file: %s, line: %d", file, line); assert (0); } if (0 == head->used) { gu_error ("Attempt to free pointer the second time at " "file: %s, line: %d. " "Was allocated at file: %s, line: %d.", file, line, head->file, head->line); assert (0); } gu_mem_total -= head->allocated; gu_mem_frees++; head->allocated = 0; head->used = 0; free (head); } void gu_mem_stats (ssize_t* total, ssize_t* allocs, ssize_t* reallocs, ssize_t* deallocs) { *total = gu_mem_total; *allocs = gu_mem_allocs; *reallocs = gu_mem_reallocs; *deallocs = gu_mem_frees; } percona-xtradb-cluster-galera/galerautils/src/gu_mem.h0000644000000000000000000000405712247075736023405 0ustar rootroot00000000000000// Copyright (C) 2007 Codership Oy /** * @file * Declarations of memory allocation functions and macros * * $Id: gu_mem.h 1622 2010-04-25 14:47:22Z teemu $ */ #ifndef _gu_mem_h_ #define _gu_mem_h_ #include #include #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /** @name Functions to help with dynamic allocation debugging. * Take additional __FILE__ and __LINE__ arguments. Should be * used as part of macros defined below */ /*@{*/ void* gu_malloc_dbg (size_t size, const char* file, unsigned int line); void* gu_calloc_dbg (size_t nmemb, size_t size, const char* file, unsigned int line); void* gu_realloc_dbg (void* ptr, size_t size, const char* file, unsigned int line); void gu_free_dbg (void* ptr, const char* file, unsigned int line); /*@}*/ /** Reports statistics on the current amount of allocated memory * total number of allocations and deallocations */ void gu_mem_stats (ssize_t* total, ssize_t* allocs, ssize_t* reallocs, ssize_t* deallocs); /** @name Applications should use the following macros */ /*@{*/ #ifdef DEBUG_MALLOC #define gu_malloc(S) gu_malloc_dbg ((S), __FILE__, __LINE__) #define gu_calloc(N,S) gu_calloc_dbg ((N), (S), __FILE__, __LINE__) #define gu_realloc(P,S) gu_realloc_dbg ((P), (S), __FILE__, __LINE__) #define gu_free(P) gu_free_dbg ((P), __FILE__, __LINE__) #else /* !DEBUG_MALLOC - use standard allocation routines */ #define gu_malloc(S) malloc ((S)) #define gu_calloc(N,S) calloc ((N), (S)) #define gu_realloc(P,S) realloc ((P), (S)) #define gu_free(P) free ((P)) #endif /* DEBUG_MALLOC */ /** Convenience macros - to avoid code clutter */ #define GU_MALLOC(type) (type*) gu_malloc (sizeof(type)) #define GU_MALLOCN(N,type) (type*) gu_malloc ((N) * sizeof(type)) #define GU_CALLOC(N,type) (type*) gu_calloc ((N), sizeof(type)) #define GU_REALLOC(P,N,type) (type*) gu_realloc((P), (N) * sizeof(type)) /*@}*/ #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* _gu_mem_h_ */ percona-xtradb-cluster-galera/galerautils/src/gu_mmh3.c0000644000000000000000000000715112247075736023464 0ustar rootroot00000000000000// Copyright (C) 2012 Codership Oy /** * @file MurmurHash3 implementation * (slightly rewritten from the refrence C++ impl.) * * $Id: gu_mmh3.c 2822 2012-06-20 19:59:58Z alex $ */ #include "gu_mmh3.h" void gu_mmh3_32 (const void* const key, int const len, uint32_t const seed, void* const out) { uint32_t const res = _mmh32_seed (key, len, seed); *((uint32_t*)out) = gu_le32(res); } //----------------------------------------------------------------------------- #if 0 /* x86 variant is faulty and unsuitable for short keys, ignore */ void gu_mmh3_x86_128 (const void* key, const int len, const uint32_t seed, void* out) { const uint8_t* data = (const uint8_t*)key; const int nblocks = len >> 4; uint32_t h1 = seed; uint32_t h2 = seed; uint32_t h3 = seed; uint32_t h4 = seed; const uint32_t c1 = 0x239b961b; const uint32_t c2 = 0xab0e9789; const uint32_t c3 = 0x38b34ae5; const uint32_t c4 = 0xa1e38b93; //---------- // body const uint32_t* blocks = (const uint32_t*)(data + (nblocks << 4)); int i; for(i = -nblocks; i; i++) { uint32_t k1 = gu_le32(blocks[(i << 2) + 0]); uint32_t k2 = gu_le32(blocks[(i << 2) + 1]); uint32_t k3 = gu_le32(blocks[(i << 2) + 2]); uint32_t k4 = gu_le32(blocks[(i << 2) + 3]); k1 *= c1; k1 = GU_ROTL32(k1,15); k1 *= c2; h1 ^= k1; h1 = GU_ROTL32(h1,19); h1 += h2; h1 = h1*5+0x561ccd1b; k2 *= c2; k2 = GU_ROTL32(k2,16); k2 *= c3; h2 ^= k2; h2 = GU_ROTL32(h2,17); h2 += h3; h2 = h2*5+0x0bcaa747; k3 *= c3; k3 = GU_ROTL32(k3,17); k3 *= c4; h3 ^= k3; h3 = GU_ROTL32(h3,15); h3 += h4; h3 = h3*5+0x96cd1c35; k4 *= c4; k4 = GU_ROTL32(k4,18); k4 *= c1; h4 ^= k4; h4 = GU_ROTL32(h4,13); h4 += h1; h4 = h4*5+0x32ac3b17; } //---------- // tail const uint8_t * tail = (const uint8_t*)(blocks); uint32_t k1 = 0; uint32_t k2 = 0; uint32_t k3 = 0; uint32_t k4 = 0; switch(len & 15) { case 15: k4 ^= tail[14] << 16; case 14: k4 ^= tail[13] << 8; case 13: k4 ^= tail[12] << 0; k4 *= c4; k4 = GU_ROTL32(k4,18); k4 *= c1; h4 ^= k4; case 12: k3 ^= tail[11] << 24; case 11: k3 ^= tail[10] << 16; case 10: k3 ^= tail[ 9] << 8; case 9: k3 ^= tail[ 8] << 0; k3 *= c3; k3 = GU_ROTL32(k3,17); k3 *= c4; h3 ^= k3; case 8: k2 ^= tail[ 7] << 24; case 7: k2 ^= tail[ 6] << 16; case 6: k2 ^= tail[ 5] << 8; case 5: k2 ^= tail[ 4] << 0; k2 *= c2; k2 = GU_ROTL32(k2,16); k2 *= c3; h2 ^= k2; case 4: k1 ^= tail[ 3] << 24; case 3: k1 ^= tail[ 2] << 16; case 2: k1 ^= tail[ 1] << 8; case 1: k1 ^= tail[ 0] << 0; k1 *= c1; k1 = GU_ROTL32(k1,15); k1 *= c2; h1 ^= k1; }; //---------- // finalization h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len; h1 += h2; h1 += h3; h1 += h4; h2 += h1; h3 += h1; h4 += h1; h1 = _mmh3_fmix32(h1); h2 = _mmh3_fmix32(h2); h3 = _mmh3_fmix32(h3); h4 = _mmh3_fmix32(h4); h1 += h2; h1 += h3; h1 += h4; h2 += h1; h3 += h1; h4 += h1; ((uint32_t*)out)[0] = gu_le32(h1); ((uint32_t*)out)[1] = gu_le32(h2); ((uint32_t*)out)[2] = gu_le32(h3); ((uint32_t*)out)[3] = gu_le32(h4); } #endif /* 0 */ //----------------------------------------------------------------------------- void gu_mmh3_x64_128 (const void* key, int len, uint32_t const seed, void* const out) { uint64_t* const res = (uint64_t*)out; _mmh3_128_seed (key, len, seed, seed, res); res[0] = gu_le64(res[0]); res[1] = gu_le64(res[1]); } percona-xtradb-cluster-galera/galerautils/src/gu_mmh3.h0000644000000000000000000002445112247075736023473 0ustar rootroot00000000000000// Copyright (C) 2012 Codership Oy /** * @file MurmurHash3 header * * $Id: gu_mmh3.h 2867 2012-10-05 16:31:28Z alex $ */ #ifndef _gu_mmh3_h_ #define _gu_mmh3_h_ #include "gu_byteswap.h" #include // for memset() and memcpy() #ifdef __cplusplus extern "C" { #endif //----------------------------------------------------------------------------- // Finalization mix - force all bits of a hash block to avalanche static GU_FORCE_INLINE uint32_t _mmh3_fmix32 (uint32_t h) { h ^= h >> 16; h *= 0x85ebca6b; h ^= h >> 13; h *= 0xc2b2ae35; h ^= h >> 16; return h; } static GU_FORCE_INLINE uint64_t _mmh3_fmix64 (uint64_t k) { k ^= k >> 33; k *= GU_ULONG_LONG(0xff51afd7ed558ccd); k ^= k >> 33; k *= GU_ULONG_LONG(0xc4ceb9fe1a85ec53); k ^= k >> 33; return k; } //----------------------------------------------------------------------------- static uint32_t const _mmh3_32_c1 = 0xcc9e2d51; static uint32_t const _mmh3_32_c2 = 0x1b873593; static GU_FORCE_INLINE void _mmh3_block_32 (uint32_t k1, uint32_t* h1) { k1 *= _mmh3_32_c1; k1 = GU_ROTL32(k1,15); k1 *= _mmh3_32_c2; *h1 ^= k1; *h1 = GU_ROTL32(*h1,13); *h1 *= 5; *h1 += 0xe6546b64; } static GU_FORCE_INLINE void _mmh3_blocks_32 (const uint32_t* const blocks,size_t const nblocks,uint32_t* h1) { //---------- // body size_t i; for (i = 0; i < nblocks; i++) { //----------------------------------------------------------------------------- // Block read - if your platform needs to do endian-swapping or can only // handle aligned reads, do the conversion here _mmh3_block_32 (gu_le32(blocks[i]), h1);/* convert from little-endian */ } } static GU_FORCE_INLINE uint32_t _mmh3_tail_32 (const uint8_t* const tail, size_t const len, uint32_t h1) { //---------- // tail #if 0 /* Reference implementation */ uint32_t k1 = 0; switch(len & 3) { case 3: k1 ^= tail[2] << 16; case 2: k1 ^= tail[1] << 8; case 1: k1 ^= tail[0]; k1 *= _mmh3_32_c1; k1 = GU_ROTL32(k1,15); k1 *= _mmh3_32_c2; h1 ^= k1; }; #else /* Optimized implementation */ size_t const shift = (len & 3) << 3; if (shift) { uint32_t k1 = gu_le32(((uint32_t*)tail)[0]) & (0x00ffffff>>(24-shift)); k1 *= _mmh3_32_c1; k1 = GU_ROTL32(k1,15); k1 *= _mmh3_32_c2; h1 ^= k1; } #endif /* Optimized implementation */ //---------- // finalization h1 ^= len; h1 = _mmh3_fmix32(h1); return h1; } static GU_FORCE_INLINE uint32_t _mmh32_seed (const void* key, size_t const len, uint32_t seed) { size_t const nblocks = len >> 2; const uint32_t* const blocks = (const uint32_t*)key; const uint8_t* const tail = (const uint8_t*)(blocks + nblocks); _mmh3_blocks_32 (blocks, nblocks, &seed); return _mmh3_tail_32 (tail, len, seed); } // same as FNV32 seed static uint32_t const GU_MMH32_SEED = GU_ULONG(2166136261); /*! A function to hash buffer in one go */ #define gu_mmh32(_buf, _len) \ _mmh32_seed (_buf, _len, GU_MMH32_SEED); /* * 128-bit MurmurHash3 */ static uint64_t const _mmh3_128_c1 = GU_ULONG_LONG(0x87c37b91114253d5); static uint64_t const _mmh3_128_c2 = GU_ULONG_LONG(0x4cf5ad432745937f); static GU_FORCE_INLINE void _mmh3_128_block (uint64_t k1, uint64_t k2, uint64_t* h1, uint64_t* h2) { k1 *= _mmh3_128_c1; k1 = GU_ROTL64(k1,31); k1 *= _mmh3_128_c2; *h1 ^= k1; *h1 = GU_ROTL64(*h1,27); *h1 += *h2; *h1 *= 5; *h1 += 0x52dce729; k2 *= _mmh3_128_c2; k2 = GU_ROTL64(k2,33); k2 *= _mmh3_128_c1; *h2 ^= k2; *h2 = GU_ROTL64(*h2,31); *h2 += *h1; *h2 *= 5; *h2 += 0x38495ab5; } static GU_FORCE_INLINE void _mmh3_128_blocks (const uint64_t* const blocks, size_t const nblocks, uint64_t* h1, uint64_t* h2) { //---------- // body size_t i; for(i = 0; i < nblocks; i++) { //----------------------------------------------------------------------------- // Block read - if your platform needs to do endian-swapping or can only // handle aligned reads, do the conversion here uint64_t k1 = gu_le64(blocks[i]); i++; uint64_t k2 = gu_le64(blocks[i]); _mmh3_128_block (k1, k2, h1, h2); } } static GU_FORCE_INLINE void _mmh3_128_tail (const uint8_t* const tail, size_t const len, uint64_t h1, uint64_t h2, uint64_t* const out) { //---------- // tail uint64_t k1 = 0; uint64_t k2 = 0; switch(len & 15) { case 15: k2 ^= ((uint64_t)tail[14]) << 48; case 14: k2 ^= ((uint64_t)tail[13]) << 40; case 13: k2 ^= ((uint64_t)tail[12]) << 32; case 12: k2 ^= ((uint64_t)tail[11]) << 24; case 11: k2 ^= ((uint64_t)tail[10]) << 16; case 10: k2 ^= ((uint64_t)tail[ 9]) << 8; case 9: k2 ^= ((uint64_t)tail[ 8]) << 0; k2 *= _mmh3_128_c2; k2 = GU_ROTL64(k2,33); k2 *= _mmh3_128_c1; h2 ^= k2; k1 = gu_le64(((uint64_t*)tail)[0]); k1 *= _mmh3_128_c1; k1 = GU_ROTL64(k1,31); k1 *= _mmh3_128_c2; h1 ^= k1; break; case 8: k1 ^= ((uint64_t)tail[ 7]) << 56; case 7: k1 ^= ((uint64_t)tail[ 6]) << 48; case 6: k1 ^= ((uint64_t)tail[ 5]) << 40; case 5: k1 ^= ((uint64_t)tail[ 4]) << 32; case 4: k1 ^= ((uint64_t)tail[ 3]) << 24; case 3: k1 ^= ((uint64_t)tail[ 2]) << 16; case 2: k1 ^= ((uint64_t)tail[ 1]) << 8; case 1: k1 ^= ((uint64_t)tail[ 0]) << 0; k1 *= _mmh3_128_c1; k1 = GU_ROTL64(k1,31); k1 *= _mmh3_128_c2; h1 ^= k1; }; //---------- // finalization h1 ^= len; h2 ^= len; h1 += h2; h2 += h1; h1 = _mmh3_fmix64(h1); h2 = _mmh3_fmix64(h2); h1 += h2; h2 += h1; out[0] = h1; out[1] = h2; } static GU_FORCE_INLINE void _mmh3_128_seed (const void* const key, size_t const len, uint64_t s1, uint64_t s2, uint64_t* const out) { size_t const nblocks = (len >> 4) << 1; /* using 64-bit half-blocks */ const uint64_t* const blocks = (const uint64_t*)(key); const uint8_t* const tail = (const uint8_t*)(blocks + nblocks); _mmh3_128_blocks (blocks, nblocks, &s1, &s2); _mmh3_128_tail (tail, len, s1, s2, out); } // same as FNV128 seed static uint64_t const GU_MMH128_SEED1 = GU_ULONG_LONG(0x6C62272E07BB0142); static uint64_t const GU_MMH128_SEED2 = GU_ULONG_LONG(0x62B821756295C58D); /* returns hash in the canonical byte order, as a byte array */ static GU_FORCE_INLINE void gu_mmh128 (const void* const msg, size_t const len, void* const out) { _mmh3_128_seed (msg, len, GU_MMH128_SEED1, GU_MMH128_SEED2, (uint64_t*)out); uint64_t* const res = (uint64_t*)out; res[0] = gu_le64(res[0]); res[1] = gu_le64(res[1]); } /* returns hash as an integer, in host byte-order */ static GU_FORCE_INLINE uint64_t gu_mmh128_64 (const void* const msg, size_t len) { uint64_t res[2]; _mmh3_128_seed (msg, len, GU_MMH128_SEED1, GU_MMH128_SEED2, res); return res[0]; } /* returns hash as an integer, in host byte-order */ static GU_FORCE_INLINE uint32_t gu_mmh128_32 (const void* const msg, size_t len) { uint64_t res[2]; _mmh3_128_seed (msg, len, GU_MMH128_SEED1, GU_MMH128_SEED2, res); return (uint32_t)res[0]; } /* * Functions to hash message by parts * (only 128-bit version, 32-bit is not relevant any more) */ typedef struct gu_mmh128_ctx { uint64_t hash[2]; uint64_t tail[2]; size_t length; } gu_mmh128_ctx_t; /*! Initialize/reset MMH context with a particular seed. * The seed is two 8-byte _integers_, obviously in HOST BYTE ORDER. * Should not be used directly. */ static GU_INLINE void _mmh128_init_seed (gu_mmh128_ctx_t* const mmh, uint64_t const s1, uint64_t const s2) { memset (mmh, 0, sizeof(*mmh)); mmh->hash[0] = s1; mmh->hash[1] = s2; } /*! Initialize MMH context with a default Galera seed. */ #define gu_mmh128_init(_mmh) \ _mmh128_init_seed (_mmh, GU_MMH128_SEED1, GU_MMH128_SEED2); /*! Apeend message part to hash context */ static GU_INLINE void gu_mmh128_append (gu_mmh128_ctx_t* const mmh, const void* part, size_t len) { size_t tail_len = mmh->length & 15; mmh->length += len; if (tail_len) /* there's something in the tail */// do we need this if()? { size_t const to_fill = 16 - tail_len; void* const tail_end = (uint8_t*)mmh->tail + tail_len; if (len >= to_fill) /* we can fill a full block */ { memcpy (tail_end, part, to_fill); _mmh3_128_block (gu_le64(mmh->tail[0]), gu_le64(mmh->tail[1]), &mmh->hash[0], &mmh->hash[1]); part = ((char*)part) + to_fill; len -= to_fill; } else { memcpy (tail_end, part, len); return; } } size_t const nblocks = (len >> 4) << 1; /* using 64-bit half-blocks */ const uint64_t* const blocks = (const uint64_t*)(part); _mmh3_128_blocks (blocks, nblocks, &mmh->hash[0], &mmh->hash[1]); /* save possible trailing bytes to tail */ memcpy (mmh->tail, blocks + nblocks, len & 15); } /*! Get the accumulated message hash (does not change the context) */ static GU_INLINE void gu_mmh128_get (const gu_mmh128_ctx_t* const mmh, void* const res) { uint64_t* const r = (uint64_t*)res; _mmh3_128_tail ((const uint8_t*)mmh->tail, mmh->length, mmh->hash[0], mmh->hash[1], r); r[0] = gu_le64(r[0]); r[1] = gu_le64(r[1]); } static GU_INLINE uint64_t gu_mmh128_get64 (const gu_mmh128_ctx_t* const mmh) { uint64_t res[2]; _mmh3_128_tail ((const uint8_t*)mmh->tail, mmh->length, mmh->hash[0], mmh->hash[1], res); return res[0]; } static GU_INLINE uint32_t gu_mmh128_get32 (const gu_mmh128_ctx_t* const mmh) { uint64_t res[2]; _mmh3_128_tail ((const uint8_t*)mmh->tail, mmh->length, mmh->hash[0], mmh->hash[1], res); return (uint32_t)res[0]; } /* * Below are fuctions with reference signatures for implementation verification */ extern void gu_mmh3_32 (const void* key, int len, uint32_t seed, void* out); #if 0 /* x86 variant is faulty and unsuitable for short keys, ignore */ extern void gu_mmh3_x86_128 (const void* key, int len, uint32_t seed, void* out); #endif /* 0 */ extern void gu_mmh3_x64_128 (const void* key, int len, uint32_t seed, void* out); #ifdef __cplusplus } #endif #endif /* _gu_mmh3_h_ */ percona-xtradb-cluster-galera/galerautils/src/gu_monitor.hpp0000644000000000000000000000330612247075736024652 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id$ */ /*! * @file gu_monitor.hpp * * */ #ifndef __GU_MONITOR_HPP__ #define __GU_MONITOR_HPP__ #include #include namespace gu { class Monitor; class Critical; } class gu::Monitor { int mutable refcnt; Mutex mutex; Cond cond; #ifndef NDEBUG pthread_t mutable holder; #endif // copy contstructor and operator= disabled by mutex and cond members. // but on Darwin, we got an error 'class gu::Monitor' has pointer data members // so make non-copyable explicitly Monitor(const Monitor&); void operator=(const Monitor&); public: #ifndef NDEBUG Monitor() : refcnt(0), mutex(), cond(), holder(0) {} #else Monitor() : refcnt(0), mutex(), cond() {} #endif ~Monitor() {} void enter() const { Lock lock(mutex); // Teemu, pthread_equal() check seems redundant, refcnt too (counted in cond) // while (refcnt > 0 && pthread_equal(holder, pthread_self()) == 0) while (refcnt) { lock.wait(cond); } refcnt++; #ifndef NDEBUG holder = pthread_self(); #endif } void leave() const { Lock lock(mutex); assert(refcnt > 0); assert(pthread_equal(holder, pthread_self()) != 0); refcnt--; if (refcnt == 0) { cond.signal(); } } }; class gu::Critical { const Monitor& mon; Critical (const Critical&); Critical& operator= (const Critical&); public: Critical(const Monitor& m) : mon(m) { mon.enter(); } ~Critical() { mon.leave(); } }; #endif /* __GU_MONITOR_HPP__ */ percona-xtradb-cluster-galera/galerautils/src/gu_mutex.c0000644000000000000000000002366112247075736023766 0ustar rootroot00000000000000// Copyright (C) 2007 Codership Oy /** * Debug versions of thread functions * * $Id: gu_mutex.c 3207 2013-08-21 09:08:51Z vlad $ */ #include #include #include #include "galerautils.h" /* Is it usable? */ static const struct gu_mutex gu_mutex_init = { .target_mutex = PTHREAD_MUTEX_INITIALIZER, .control_mutex = PTHREAD_MUTEX_INITIALIZER, .lock_waiter_count = 0, .cond_waiter_count = 0, .holder_count = 0, .thread = 0, // unknown thread .file = __FILE__, .line = __LINE__ }; int gu_mutex_init_dbg (struct gu_mutex *m, const pthread_mutexattr_t* attr, const char *file, unsigned int line) { m->file = file; m->line = line; m->lock_waiter_count = 0; m->cond_waiter_count = 0; m->holder_count = 0; m->thread = pthread_self(); pthread_mutex_init(&m->control_mutex, NULL); pthread_mutex_init(&m->target_mutex, attr); return 0; // as per pthread spec } int gu_mutex_lock_dbg(struct gu_mutex *m, const char *file, unsigned int line) { int err = 0; pthread_mutex_lock(&m->control_mutex); { if (m->holder_count > 0 && pthread_equal(pthread_self(), m->thread)) { // Have to explicitly submit file and line info as they come // from a totally different place gu_fatal("Second mutex lock attempt by the same thread, %lu, " "at %s:%d, first locked at %s:%d", pthread_self(), file, line, m->file, m->line); assert(0); err = EDEADLK; /* return error in case assert is not defined */ } m->lock_waiter_count++; } /* unlocking control mutex here since we may block waiting for target * mutext and unlocking target mutex again involves locking the control */ pthread_mutex_unlock(&m->control_mutex); if (err) return err; /* request the actual mutex */ if ((err = pthread_mutex_lock(&m->target_mutex))) { /* This i a valid situation - mutex could be destroyed */ gu_debug("%lu mutex lock error (%d: %s) at %s:%d", pthread_self(), err, strerror(err), file, line); return err; } /* need control mutex for info field changes */ if ((err = pthread_mutex_lock(&m->control_mutex))) { // do we need this check - it's only a control mutex? gu_fatal("%lu mutex lock error (%d: %s) at %s:%d", pthread_self(), err, strerror(err), file, line); assert(0); } else { if (gu_likely(m->holder_count == 0)) { m->thread = pthread_self(); m->lock_waiter_count--; m->holder_count++; m->file = file; m->line = line; } else { gu_fatal("Mutex lock granted %d times at %s:%d", m->holder_count, file, line); assert(0); } pthread_mutex_unlock(&m->control_mutex); } /* we have to return 0 here since target mutex was successfully locked */ return 0; } int gu_mutex_unlock_dbg (struct gu_mutex *m, const char *file, unsigned int line) { int err = 0; pthread_mutex_lock(&m->control_mutex); { /** must take into account that mutex unlocking can happen in * cleanup handlers when thread is terminated in cond_wait(). * Then holder_count would still be 0 (see gu_cond_wait()), * but cond_waiter - not */ if (m->holder_count == 0 && m->cond_waiter_count == 0) { gu_fatal ("%lu attempts to unlock unlocked mutex at %s:%d. " "Last use at %s:%d", pthread_self(), file, line, m->file ? m->file : "" , m->line); assert(0); } if (m->holder_count > 0 && !pthread_equal(pthread_self(), m->thread)) { /** last time pthread_t was unsigned long int */ gu_fatal ("%lu attempts to unlock mutex owned by %lu at %s:%d. " "Locked at %s:%d", pthread_self(), m->thread, file, line, m->file, m->line); assert(0); return EPERM; /** return in case assert is undefined */ } err = pthread_mutex_unlock (&m->target_mutex); if (gu_likely(!err)) { m->file = file; m->line = line; m->thread = 0; /* At this point it is difficult to say if we're unlocking * normally or from cancellation handler, if holder_count not 0 - * assume it is normal unlock, otherwise we decrement * cond_waiter_count */ if (gu_likely(m->holder_count)) { m->holder_count--; } else { if (gu_likely(0 != m->cond_waiter_count)) { m->cond_waiter_count--; } else { gu_fatal ("Internal galerautils error: both holder_count " "and cond_waiter_count are 0"); assert (0); } } } else { gu_fatal("Error: (%d,%d) during mutex unlock at %s:%d", err, errno, file, line); assert(0); } } pthread_mutex_unlock(&m->control_mutex); return err; } int gu_mutex_destroy_dbg (struct gu_mutex *m, const char *file, unsigned int line) { int err=0; pthread_mutex_lock(&m->control_mutex); { if (!m->file) { gu_fatal("%lu attempts to destroy uninitialized mutex at %s:%d", pthread_self(), file, line); assert(0); } if (m->holder_count != 0) { if (pthread_self() == m->thread) { gu_fatal ("%lu attempts to destroy mutex locked by " "itself at %s:%d", pthread_self(), m->file, m->line); assert (0); /* logical error in program */ } else { gu_debug("%lu attempts to destroy a mutex at %s:%d " "locked by %lu at %s:%d (not error)", pthread_self(), file, line, m->thread, m->file, m->line); // assert (0); // DELETE when not needed! } } if (m->cond_waiter_count != 0) { gu_debug("%lu attempts to destroy a mutex at %s:%d " "that is waited by %d thread(s)", pthread_self(), file, line, m->cond_waiter_count); assert (m->cond_waiter_count > 0); } if ((err = pthread_mutex_destroy(&m->target_mutex))) { gu_debug("Error (%d: %s, %d) during mutex destroy at %s:%d", err, strerror(err), errno, file, line); pthread_mutex_unlock (&m->control_mutex); return err; } m->file = 0; m->line = 0; m->thread = 0; } pthread_mutex_unlock(&m->control_mutex); while (pthread_mutex_destroy(&m->control_mutex)); return err; } int gu_cond_wait_dbg (pthread_cond_t *cond, struct gu_mutex *m, const char *file, unsigned int line) { int err = 0; // Unfortunately count updates here are not atomic with cond_wait. // But cond_wait() semantics does not allow them to be. pthread_mutex_lock (&m->control_mutex); { if (gu_unlikely(m->holder_count <= 0)) { gu_fatal ("%lu tries to wait for condition on unlocked mutex " "at %s %d", pthread_self(), file, line); assert (0); } else if (!pthread_equal(pthread_self(), m->thread)) { gu_fatal ("%lu tries to wait for condition on the mutex that" "belongs to %lu at %s %d", pthread_self(), m->thread, file, line); assert (0); } /** pthread_cond_wait frees the mutex */ m->holder_count--; m->cond_waiter_count++; m->thread = 0; assert (m->holder_count >= 0); } pthread_mutex_unlock(&m->control_mutex); if ((err = pthread_cond_wait (cond, &m->target_mutex))) { gu_fatal("Error (%d: %s, %d) during cond_wait at %s:%d", err, strerror(err), errno, file, line); assert(0); } pthread_mutex_lock (&m->control_mutex); { /** acquired mutex again */ m->holder_count++; m->cond_waiter_count--; m->thread = pthread_self(); } pthread_mutex_unlock(&m->control_mutex); return err; } #if defined(__APPLE__) int pthread_barrier_init (pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count) { if(count == 0) { errno = EINVAL; return -1; } if(pthread_mutex_init (&barrier->mutex, 0) < 0) { return -1; } if(pthread_cond_init (&barrier->cond, 0) < 0) { pthread_mutex_destroy (&barrier->mutex); return -1; } barrier->tripCount = count; barrier->count = 0; return 0; } int pthread_barrier_destroy (pthread_barrier_t *barrier) { pthread_cond_destroy (&barrier->cond); pthread_mutex_destroy (&barrier->mutex); return 0; } int pthread_barrier_wait (pthread_barrier_t *barrier) { pthread_mutex_lock (&barrier->mutex); ++(barrier->count); if(barrier->count >= barrier->tripCount) { barrier->count = 0; pthread_cond_broadcast (&barrier->cond); pthread_mutex_unlock (&barrier->mutex); return 1; } else { pthread_cond_wait (&barrier->cond, &(barrier->mutex)); pthread_mutex_unlock (&barrier->mutex); return 0; } } #endif /* __APPLE__ */ percona-xtradb-cluster-galera/galerautils/src/gu_mutex.h0000644000000000000000000000741412247075736023771 0ustar rootroot00000000000000// Copyright (C) 2007 Codership Oy /** * @file Special mutex replacements for debugging/porting * * $Id: gu_mutex.h 3207 2013-08-21 09:08:51Z vlad $ */ #ifndef _gu_mutex_h_ #define _gu_mutex_h_ #include struct gu_mutex { pthread_mutex_t target_mutex; //!< for critical section pthread_mutex_t control_mutex; //!< for mutex operations volatile int lock_waiter_count; //!< # of threads waiting for lock volatile int cond_waiter_count; //!< # of threads waiting for cond volatile int holder_count; //!< must be 0 or 1 volatile pthread_t thread; /* point in source code, where called from */ volatile const char *file; volatile int line; }; /** @name Usual mutex operations storing FILE and LINE information */ /*@{*/ int gu_mutex_init_dbg (struct gu_mutex *mutex, const pthread_mutexattr_t *attr, const char *file, unsigned int line); int gu_mutex_lock_dbg (struct gu_mutex *mutex, const char *file, unsigned int line); int gu_mutex_unlock_dbg (struct gu_mutex *mutex, const char *file, unsigned int line); int gu_mutex_destroy_dbg (struct gu_mutex *mutex, const char *file, unsigned int line); int gu_cond_wait_dbg (pthread_cond_t *cond, struct gu_mutex *mutex, const char *file, unsigned int line); /*@}*/ /** Shorter mutex API for applications. * Depending on compile-time flags application will either use * debug or normal version of the mutex API */ /*@{*/ #ifdef DEBUG_MUTEX typedef struct gu_mutex gu_mutex_t; #define gu_mutex_init(M,A) gu_mutex_init_dbg ((M),(A), __FILE__, __LINE__) #define gu_mutex_lock(M) gu_mutex_lock_dbg ((M), __FILE__, __LINE__) #define gu_mutex_unlock(M) gu_mutex_unlock_dbg ((M), __FILE__, __LINE__) #define gu_mutex_destroy(M) gu_mutex_destroy_dbg((M), __FILE__, __LINE__) #define gu_cond_wait(S,M) gu_cond_wait_dbg ((S),(M), __FILE__, __LINE__) #define GU_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, \ PTHREAD_MUTEX_INITIALIZER, \ 0,0,0,0,0,0 } #else /* DEBUG_MUTEX not defined - use regular pthread functions */ typedef pthread_mutex_t gu_mutex_t; #define gu_mutex_init(M,A) pthread_mutex_init ((M),(A)) #define gu_mutex_lock(M) pthread_mutex_lock ((M)) #define gu_mutex_unlock(M) pthread_mutex_unlock ((M)) #define gu_mutex_destroy(M) pthread_mutex_destroy((M)) #define gu_cond_wait(S,M) pthread_cond_wait ((S),(M)) #define GU_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER #endif /* DEBUG_MUTEX */ /*@}*/ /* The following typedefs and macros don't do anything now, * but may be used later */ typedef pthread_t gu_thread_t; typedef pthread_cond_t gu_cond_t; #define gu_thread_create pthread_create #define gu_thread_join pthread_join #define gu_thread_cancel pthread_cancel #define gu_thread_exit pthread_exit #define gu_cond_init pthread_cond_init #define gu_cond_destroy pthread_cond_destroy #define gu_cond_signal pthread_cond_signal #define gu_cond_broadcast pthread_cond_broadcast #define gu_cond_timedwait pthread_cond_timedwait #if defined(__APPLE__) #ifdef __cplusplus extern "C" { #endif typedef int pthread_barrierattr_t; typedef struct { pthread_mutex_t mutex; pthread_cond_t cond; int count; int tripCount; } pthread_barrier_t; int pthread_barrier_init (pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count); int pthread_barrier_destroy (pthread_barrier_t *barrier); int pthread_barrier_wait (pthread_barrier_t *barrier); #ifdef __cplusplus } #endif #endif /* __APPLE__ */ #endif /* _gu_mutex_h_ */ percona-xtradb-cluster-galera/galerautils/src/gu_mutex.hpp0000644000000000000000000000343012247075736024323 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy * */ #ifndef __GU_MUTEX__ #define __GU_MUTEX__ #include #include #include #include "gu_macros.h" #include "gu_mutex.h" #include "gu_throw.hpp" namespace gu { class Mutex { public: Mutex () : value() { gu_mutex_init (&value, NULL); // always succeeds } ~Mutex () { int err = gu_mutex_destroy (&value); if (gu_unlikely(err != 0)) { gu_throw_error (err) << "pthread_mutex_destroy()"; } } void lock() { gu_mutex_lock(&value); } void unlock() { gu_mutex_unlock(&value); } protected: gu_mutex_t mutable value; private: Mutex (const Mutex&); Mutex& operator= (const Mutex&); friend class Lock; }; class RecursiveMutex { public: RecursiveMutex() : mutex_() { pthread_mutexattr_t mattr; pthread_mutexattr_init(&mattr); pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&mutex_, &mattr); pthread_mutexattr_destroy(&mattr); } ~RecursiveMutex() { pthread_mutex_destroy(&mutex_); } void lock() { if (pthread_mutex_lock(&mutex_)) gu_throw_fatal; } void unlock() { if (pthread_mutex_unlock(&mutex_)) gu_throw_fatal; } private: RecursiveMutex(const RecursiveMutex&); void operator=(const RecursiveMutex&); pthread_mutex_t mutex_; }; } #endif /* __GU_MUTEX__ */ percona-xtradb-cluster-galera/galerautils/src/gu_print_buf.c0000644000000000000000000000342312247075736024606 0ustar rootroot00000000000000// Copyright (C) 2012 Codership Oy /** * @file Functions to dump buffer contents in a readable form * * $Id: gu_print_buf.c 3336 2013-10-28 07:41:56Z teemu $ */ #include "gu_print_buf.h" #define GU_ASCII_0 0x30 #define GU_ASCII_10 0x3a #define GU_ASCII_A 0x41 #define GU_ASCII_a 0x61 #define GU_ASCII_A_10 (GU_ASCII_A - GU_ASCII_10) #define GU_ASCII_a_10 (GU_ASCII_a - GU_ASCII_10) static inline int _hex_code (uint8_t const x) { return (x + GU_ASCII_0 + (x > 9)*GU_ASCII_A_10); } static inline void _write_byte_binary (char* const str, uint8_t const byte) { str[0] = _hex_code(byte >> 4); str[1] = _hex_code(byte & 0x0f); } #define GU_ASCII_ALPHA_START 0x20 /* ' ' */ #define GU_ASCII_ALPHA_END 0x7e /* '~' */ static inline void _write_byte_alpha (char* const str, uint8_t const byte) { if (byte >= GU_ASCII_ALPHA_START && byte <= GU_ASCII_ALPHA_END) { str[0] = (char)byte; str[1] = '.'; } else { _write_byte_binary (str, byte); } } /*! Dumps contents of the binary buffer into a readable form */ void gu_print_buf(const void* buf, ssize_t const buf_size, char* str, ssize_t str_size, bool alpha) { uint8_t* b = (uint8_t*)buf; ssize_t i; str_size--; /* reserve a space for \0 */ for (i = 0; i < buf_size && str_size > 1;) { if (alpha) _write_byte_alpha (str, b[i]); else _write_byte_binary (str, b[i]); str += 2; str_size -= 2; i++; if (0 == (i % 4) && str_size > 0 && i < buf_size) { /* insert space after every 4 bytes and newline after every 32 */ str[0] = (i % 32) ? ' ' : '\n'; str_size--; str++; } } str[0] = '\0'; } percona-xtradb-cluster-galera/galerautils/src/gu_print_buf.h0000644000000000000000000000166412247075736024620 0ustar rootroot00000000000000// Copyright (C) 2012 Codership Oy /** * @file Functions to dump buffer contents in a readable form * * $Id: gu_print_buf.h 3336 2013-10-28 07:41:56Z teemu $ */ #ifndef _gu_print_buf_h_ #define _gu_print_buf_h_ #include "gu_types.h" #ifdef __cplusplus extern "C" { #endif /*! Dumps contents of the binary buffer in a readable form to a 0-terminated * string of length not exeeding str_size - 1 * @param buf input binary buffer * @param but_size size of the input buffer * @param str target string buffer (will be always 0-terminated) * @param str_size string buffer size (including terminating 0) * @param alpha dump alphanumeric characters as they are, padded with '.' * (e.g. D.u.m.p.) */ extern void gu_print_buf(const void* buf, ssize_t buf_size, char* str, ssize_t str_size, bool alpha); #ifdef __cplusplus } #endif #endif /* _gu_print_buf_h_ */ percona-xtradb-cluster-galera/galerautils/src/gu_prodcons.cpp0000644000000000000000000000341612247075736025007 0ustar rootroot00000000000000// Copyright (C) 2009 Codership Oy #include "gu_prodcons.hpp" #include #include using namespace std; class gu::prodcons::MessageQueue { public: MessageQueue() : que() { } bool empty() const { return que.empty(); } size_t size() const { return que.size(); } const Message& front() const { return que.front(); } void pop_front() { que.pop_front(); } void push_back(const Message& msg) { que.push_back(msg); } private: std::deque que; }; void gu::prodcons::Producer::send(const Message& msg, Message* ack) { cons.queue_and_wait(msg, ack); } const gu::prodcons::Message* gu::prodcons::Consumer::get_next_msg() { const Message* ret = 0; Lock lock(mutex); if (mque->empty() == false) { ret = &mque->front(); } return ret; } void gu::prodcons::Consumer::queue_and_wait(const Message& msg, Message* ack) { Lock lock(mutex); mque->push_back(msg); if (mque->size() == 1) { notify(); } lock.wait(msg.get_producer().get_cond()); assert(&rque->front().get_producer() == &msg.get_producer()); if (ack) { *ack = rque->front(); } rque->pop_front(); if (rque->empty() == false) { rque->front().get_producer().get_cond().signal(); } } void gu::prodcons::Consumer::return_ack(const Message& ack) { Lock lock(mutex); assert(&ack.get_producer() == &mque->front().get_producer()); rque->push_back(ack); mque->pop_front(); if (rque->size() == 1) { ack.get_producer().get_cond().signal(); } } gu::prodcons::Consumer::Consumer() : mutex(), mque(new MessageQueue), rque(new MessageQueue) { } gu::prodcons::Consumer::~Consumer() { delete mque; delete rque; } percona-xtradb-cluster-galera/galerautils/src/gu_prodcons.hpp0000644000000000000000000001021412247075736025006 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id:$ */ /*! * @file gu_prodcons.hpp Synchronous producer/consumer interface */ #include "gu_lock.hpp" // For byte_t #include "gu_buffer.hpp" /* Forward declarations */ namespace gu { namespace prodcons { class MessageData; class Message; class MessageQueue; class Producer; class Consumer; } } class gu::prodcons::MessageData { public: virtual ~MessageData() { } }; /*! * @brief Message class for Producer/Consumer communication */ class gu::prodcons::Message { Producer* producer; /*! Producer associated to this message */ int val; /*! Integer value (command/errno) */ const MessageData* data; public: /*! * @brief Constructor * * @param prod_ Producer associated to the message * @param data_ Message data * @param val_ Integer value associated to the message */ Message(Producer* prod_ = 0, const MessageData* data_ = 0, int val_ = -1) : producer(prod_), val(val_), data(data_) { } Message(const Message& msg) : producer(msg.producer), val(msg.val), data(msg.data) { } Message& operator=(const Message& msg) { producer = msg.producer; val = msg.val; data = msg.data; return *this; } /*! * @brief Get producer associated to the message * * @return Producer associated to the message */ Producer& get_producer() const { return *producer; } /*! * @brief Get data associated to the message * * @return Data associated to the message */ const MessageData* get_data() const { return data; } /*! * @brief Get int value associated to the message * * @return Int value associated to the message */ int get_val() const { return val; } }; /*! * @brief Producer interface */ class gu::prodcons::Producer { gu::Cond cond; /*! Condition variable */ Consumer& cons; /*! Consumer associated to this producer */ /*! * @brief Return reference to condition variable * * @return Reference to condition variable */ Cond& get_cond() { return cond; } friend class Consumer; public: /*! * @brief Consturctor * * @param cons_ Consumer associated to this producer */ Producer(Consumer& cons_) : cond(), cons(cons_) { } /*! * @brief Send message to the consumer and wait for response * * @param[in] msg Message to be sent to consumer * @param[out] ack Ack message returned by the Consumer, containing error code */ void send(const Message& msg, Message* ack); }; /*! * @brief Consumer interface */ class gu::prodcons::Consumer { Mutex mutex; /*! Mutex for internal locking */ MessageQueue* mque; /*! Message queue for producer messages */ MessageQueue* rque; /*! Message queue for ack messages */ Consumer(const Consumer&); void operator=(const Consumer&); protected: /*! * @brief Get the first message from the message queue * * Get the first message from the message queue. Note that * this method does not remove the first message from message queue. * * @return Next message from the message queue */ const Message* get_next_msg(); /*! * @brief Return ack message for the producer * * Return ack message for the producer. Note that this method * pops the first message from the message queue. * * @param msg Ack message corresponding the current head of mque */ void return_ack(const Message& msg); /*! * @brief Virtual method to notify consumer about queued message */ virtual void notify() = 0; public: /*! * @brief Default constructor */ Consumer(); /*! * @brief Default destructor */ virtual ~Consumer(); /*! * @brief Queue message and wait for ack * * @param[in] msg Message to be queued * @param[out] ack Ack returned by consumer */ void queue_and_wait(const Message& msg, Message* ack); }; percona-xtradb-cluster-galera/galerautils/src/gu_profile.hpp0000644000000000000000000002021712247075736024623 0ustar rootroot00000000000000// // Copyright (C) 2010 Codership Oy // /*! * @file gu_profile.hpp * * @brief Lightweight profiling utility. * * Profiling utility suitable for getting runtime code profile information * with minimal overhead. Macros profile_enter() and profile_leave() * can be inserted around the code and will be expanded to profiling * code if GU_PROFILE is defined. * * Example usage: * @code * * Profile prof("prof"); * * void func() * { * if (is_true()) * { * profile_enter(prof); // This is line 227 * // Do something * // ... * profile_leave(prof); * } * else * { * profile_enter(prof); // This is line 250 * // Do something else * // ... * profile_leave(prof); * } * } * * // Somewhere else in your code * log_info << prof; * @endcode * */ #ifndef GU_PROFILE_HPP #define GU_PROFILE_HPP #include "gu_time.h" #include "gu_datetime.hpp" #include "gu_lock.hpp" #ifdef HAVE_BOOST_UNORDERED_MAP_HPP #include #else #include #endif // HAVE_BOOST_UNORDERED_MAP_HPP #include namespace gu { namespace prof { class Key; class KeyHash; class Point; class Profile; std::ostream& operator<<(std::ostream&, const Key&); std::ostream& operator<<(std::ostream&, const Profile&); } } /*! * Profile key storing human readable point description :: * and entry time. */ class gu::prof::Key { public: Key(const char* const file, const char* const func, const int line) : file_(file), func_(func), line_(line) { } bool operator==(const Key& cmp) const { return (line_ == cmp.line_ && func_ == cmp.func_ && file_ == cmp.file_); } bool operator<(const Key& cmp) const { return (line_ < cmp.line_ || (line_ == cmp.line_ && (func_ < cmp.func_ || (func_ == cmp.func_ && file_ < cmp.file_)))); } std::string to_string() const { std::ostringstream os; os << *this; return os.str(); } private: friend class KeyHash; friend class Point; friend class Profile; friend std::ostream& operator<<(std::ostream& os, const Key&); const char* const file_; const char* const func_; const int line_; }; #ifdef HAVE_BOOST_UNORDERED_MAP_HPP class gu::prof::KeyHash { public: size_t operator()(const Key& key) const { return boost::hash_value(key.file_) ^ boost::hash_value(key.func_) ^ boost::hash_value(key.line_); } }; #endif // HAVE_BOOST_UNORDERED_MAP_HPP inline std::ostream& gu::prof::operator<<(std::ostream& os, const gu::prof::Key& key) { return os << key.file_ << ":" << key.func_ << ":" << key.line_; } class gu::prof::Point { public: Point(const Profile& prof, const char* file, const char* func, const int line); ~Point(); private: friend class Profile; const Profile& prof_; const Key key_; mutable long long int enter_time_calendar_; mutable long long int enter_time_thread_cputime_; }; /*! * Profile class for collecting statistics about profile points. */ class gu::prof::Profile { struct PointStats { PointStats(long long int count = 0, long long int time_calendar = 0, long long int time_thread_cputime = 0) : count_ (count ), time_calendar_ (time_calendar ), time_thread_cputime_(time_thread_cputime) { } PointStats operator+(const PointStats& add) const { return PointStats(count_ + add.count_, time_calendar_ + add.time_calendar_, time_thread_cputime_+ add.time_thread_cputime_); } long long int count_; long long int time_calendar_; long long int time_thread_cputime_; }; #ifdef HAVE_BOOST_UNORDERED_MAP_HPP typedef boost::unordered_map Map; #else typedef std::map Map; #endif public: /*! * Default constructor. * * @param name_ Name identifying the profile in ostream output. */ Profile(const std::string& name = "profile") : name_(name), start_time_calendar_(gu_time_calendar()), start_time_thread_cputime_(gu_time_thread_cputime()), mutex_(), points_() { } void enter(const Point& point) const { point.enter_time_calendar_ = gu_time_calendar(); point.enter_time_thread_cputime_ = gu_time_thread_cputime(); gu::Lock lock(mutex_); points_[point.key_].count_++; } void leave(const Point& point) const { long long int t_cal(gu_time_calendar()); long long int t_thdcpu(gu_time_thread_cputime()); gu::Lock lock(mutex_); PointStats& pointst(points_[point.key_]); pointst.time_calendar_ += (t_cal - point.enter_time_calendar_); pointst.time_thread_cputime_ += (t_thdcpu - point.enter_time_thread_cputime_); } void clear() const { gu::Lock lock(mutex_); points_.clear(); } friend std::ostream& operator<<(std::ostream&, const Profile&); std::string const name_; long long int const start_time_calendar_; long long int const start_time_thread_cputime_; gu::Mutex mutex_; mutable Map points_; }; inline gu::prof::Point::Point(const Profile& prof, const char* file, const char* func, const int line) : prof_(prof), key_(file, func, line), enter_time_calendar_(), enter_time_thread_cputime_() { prof_.enter(*this); } inline gu::prof::Point::~Point() { prof_.leave(*this); } // // Ostream operator for Profile class. // inline std::ostream& gu::prof::operator<<(std::ostream& os, const Profile& prof) { Profile::PointStats cumul; char prev_fill(os.fill()); os.fill(' '); os << "\nprofile name: " << prof.name_; os << std::left << std::fixed << std::setprecision(3); os << "\n\n"; os << std::setw(40) << "point"; os << std::setw(10) << "count"; os << std::setw(10) << "calendar"; os << std::setw(10) << "cpu"; os << "\n" << std::setfill('-') << std::setw(70) << "" << std::setfill(' ') << "\n"; for (Profile::Map::const_iterator i = prof.points_.begin(); i != prof.points_.end(); ++i) { os << std::setw(40) << std::left << i->first.to_string(); os << std::right; os << std::setw(10) << i->second.count_; os << std::setw(10) << double(i->second.time_calendar_)*1.e-9; os << std::setw(10) << double(i->second.time_thread_cputime_)*1.e-9; os << std::left; os << "\n"; cumul = cumul + i->second; } os << "\ntot count : " << cumul.count_; os << "\ntot calendar time : " << double(cumul.time_calendar_)*1.e-9; os << "\ntot thread cputime: " << double(cumul.time_thread_cputime_)*1.e-9; os << "\ntot ct since ctor : " << double(gu::datetime::Date::now().get_utc() - prof.start_time_calendar_)*1.e-9; os.fill(prev_fill); return os; } // // Convenience macros for defining profile entry and leave points. // If GU_PROFILE is undefined, these macros expand to no-op. // #ifdef GU_PROFILE #define profile_enter(__p) \ do { \ const gu::prof::Point __point((__p), __FILE__, \ __FUNCTION__, __LINE__); \ #define profile_leave(__p) \ } while (0) #else #define profile_enter(__p) #define profile_leave(__p) #endif // GU_PROFILE #endif // GU_PROFILE_HPP percona-xtradb-cluster-galera/galerautils/src/gu_rand.c0000644000000000000000000000203412247075736023537 0ustar rootroot00000000000000// Copyright (C) 2013 Codership Oy /** * @file routines to generate "random" seeds for RNGs by collecting some easy * entropy. * * gu_rand_seed_long() goes for srand48() * * gu_rand_seed_int() goes for srand() and rand_r() * * $Id: gu_rand.c 3306 2013-09-21 11:33:17Z alex $ */ #include "gu_rand.h" #include "gu_hash.h" /*! Structure to hold entropy data. * Should be at least 20 bytes on 32-bit systems and 28 bytes on 64-bit */ struct gu_rse { long long time; const void* heap_ptr; const void* stack_ptr; long pid; }; typedef struct gu_rse gu_rse_t; long int gu_rand_seed_long (long long time, const void* heap_ptr, pid_t pid) { gu_rse_t rse = { time, heap_ptr, &time, pid }; return gu_fast_hash64_medium (&rse, sizeof(rse)); } #if GU_WORDSIZE == 32 unsigned int gu_rand_seed_int (long long time, const void* heap_ptr, pid_t pid) { gu_rse_t rse = { time, heap_ptr, &time, pid }; return gu_fast_hash32_short (&rse, sizeof(rse)); } #endif /* GU_WORDSIZE == 32 */ percona-xtradb-cluster-galera/galerautils/src/gu_rand.h0000644000000000000000000000131612247075736023546 0ustar rootroot00000000000000// Copyright (C) 2013 Codership Oy /** * @file routines to generate "random" seeds for RNGs by collecting some easy * entropy. * * gu_rand_seed_long() goes for srand48() * * gu_rand_seed_int() goes for srand() and rand_r() * * $Id: gu_rand.h 3055 2013-04-19 20:29:20Z alex $ */ #ifndef _gu_rand_h_ #define _gu_rand_h_ #include "gu_arch.h" #include // for pid_t extern long int gu_rand_seed_long (long long time, const void* heap_ptr, pid_t pid); #if GU_WORDSIZE == 32 extern unsigned int gu_rand_seed_int (long long time, const void* heap_ptr, pid_t pid); #else #define gu_rand_seed_int gu_rand_seed_long #endif /* GU_WORDSIZE */ #endif /* _gu_rand_h_ */ percona-xtradb-cluster-galera/galerautils/src/gu_regex.cpp0000644000000000000000000000233112247075736024265 0ustar rootroot00000000000000// Copyright (C) 2009 Codership Oy /** * @file Regular expressions parser based on POSIX regex functions in * * $Id: gu_regex.cpp 3336 2013-10-28 07:41:56Z teemu $ */ #include "gu_utils.hpp" #include "gu_regex.hpp" namespace gu { using std::string; using std::vector; string RegEx::strerror (int rc) const { char buf[128]; regerror(rc, ®ex, buf, sizeof(buf)); return string (buf); } static inline RegEx::Match regmatch2Match (const string& str, const regmatch_t& rm) { if (rm.rm_so == -1) return RegEx::Match(); return RegEx::Match (str.substr(rm.rm_so, rm.rm_eo - rm.rm_so)); } vector RegEx::match (const string& str, size_t num) const { vector ret; int rc; VLA matches(num); if ((rc = regexec(®ex, str.c_str(), num, &matches, 0))) { gu_throw_error (EINVAL) << "regexec(" << str << "): " << strerror(rc); } for (size_t i = 0; i < num; ++i) { ret.push_back (regmatch2Match (str, matches[i])); } return ret; } } percona-xtradb-cluster-galera/galerautils/src/gu_regex.hpp0000644000000000000000000000352312247075736024276 0ustar rootroot00000000000000// Copyright (C) 2009 Codership Oy /** * @file Regular expressions parser based on POSIX regex functions in * * $Id: gu_regex.hpp 3336 2013-10-28 07:41:56Z teemu $ */ #ifndef _gu_regex_hpp_ #define _gu_regex_hpp_ #include #include #include #include "gu_throw.hpp" namespace gu { class RegEx { regex_t regex; std::string strerror (int rc) const; public: /*! * @param expr regular expression string */ RegEx (const std::string& expr) : regex() { int rc; if ((rc = regcomp(®ex, expr.c_str(), REG_EXTENDED)) != 0) { gu_throw_fatal << "regcomp(" << expr << "): " << strerror(rc); } } ~RegEx () { regfree (®ex); } /*! * This class is to differentiate between an empty and unset strings. * @todo: find a proper name for it and move to gu_utils.hpp */ class Match { std::string value; bool set; public: Match() : value(), set(false) {} Match(const std::string& s) : value(s), set(true) {} // throws NotSet const std::string& str() const { if (set) return value; throw NotSet(); } bool is_set() const { return set; } }; /*! * @brief Matches given string * * @param str string to match with expression * @param num number of matches to return * * @return vector of matched substrings */ std::vector match (const std::string& str, size_t num) const; }; } #endif /* _gu_regex_hpp_ */ percona-xtradb-cluster-galera/galerautils/src/gu_resolver.cpp0000644000000000000000000003353312247075736025024 0ustar rootroot00000000000000// Copyright (C) 2009-2013 Codership Oy #include "gu_resolver.hpp" #include "gu_logger.hpp" #include "gu_string.hpp" #include "gu_utils.hpp" #include "gu_throw.hpp" #include "gu_uri.hpp" #include #include #include // for close() #include #include #include #define BSD_COMP /* For SIOCGIFCONF et al on Solaris */ #include #include #include #if defined(__APPLE__) || defined(__FreeBSD__) # include # define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP # define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP #else /* !__APPLE__ && !__FreeBSD__ */ extern "C" /* old style cast */ { static int const GU_SIOCGIFCONF = SIOCGIFCONF; static int const GU_SIOCGIFINDEX = SIOCGIFINDEX; } #endif /* !__APPLE__ && !__FreeBSD__ */ //using namespace std; using std::make_pair; // Map from scheme string to addrinfo class SchemeMap { public: typedef std::map Map; typedef Map::const_iterator const_iterator; SchemeMap() : ai_map() { ai_map.insert(make_pair("tcp", get_addrinfo(0, AF_UNSPEC, SOCK_STREAM, 0))); ai_map.insert(make_pair("ssl", get_addrinfo(0, AF_UNSPEC, SOCK_STREAM, 0))); ai_map.insert(make_pair("udp", get_addrinfo(0, AF_UNSPEC, SOCK_DGRAM, 0))); // TODO: } const_iterator find(const std::string& key) const { return ai_map.find(key); } const_iterator end() const { return ai_map.end(); } static const addrinfo* get_addrinfo(const_iterator i) { return &i->second; } private: Map ai_map; struct addrinfo get_addrinfo(int flags, int family, int socktype, int protocol) { struct addrinfo ret = { flags, family, socktype, protocol, #if defined(__FreeBSD__) 0, // FreeBSD gives ENOMEM error with non-zero value #else sizeof(struct sockaddr), #endif 0, 0, 0 }; return ret; } }; static SchemeMap scheme_map; // Helper to copy addrinfo structs. static void copy(const addrinfo& from, addrinfo& to) { to.ai_flags = from.ai_flags; to.ai_family = from.ai_family; to.ai_socktype = from.ai_socktype; to.ai_protocol = from.ai_protocol; to.ai_addrlen = from.ai_addrlen; if (from.ai_addr != 0) { if ((to.ai_addr = reinterpret_cast(malloc(to.ai_addrlen))) == 0) { gu_throw_fatal << "out of memory while trying to allocate " << to.ai_addrlen << " bytes"; } memcpy(to.ai_addr, from.ai_addr, to.ai_addrlen); } to.ai_canonname = 0; to.ai_next = 0; } ///////////////////////////////////////////////////////////////////////// // Sockaddr implementation ///////////////////////////////////////////////////////////////////////// bool gu::net::Sockaddr::is_multicast() const { switch (sa_->sa_family) { case AF_INET: return IN_MULTICAST(ntohl(reinterpret_cast(sa_)->sin_addr.s_addr)); case AF_INET6: return IN6_IS_ADDR_MULTICAST(&reinterpret_cast(sa_)->sin6_addr); default: gu_throw_fatal; } } bool gu::net::Sockaddr::is_anyaddr() const { switch (sa_->sa_family) { case AF_INET: return (ntohl(reinterpret_cast(sa_)->sin_addr.s_addr) == INADDR_ANY); case AF_INET6: return IN6_IS_ADDR_UNSPECIFIED(&reinterpret_cast(sa_)->sin6_addr); default: gu_throw_fatal; } } gu::net::Sockaddr::Sockaddr(const sockaddr* sa, socklen_t sa_len) : sa_ (0 ), sa_len_(sa_len) { if ((sa_ = reinterpret_cast(malloc(sa_len_))) == 0) { gu_throw_fatal; } memcpy(sa_, sa, sa_len_); } gu::net::Sockaddr::Sockaddr(const Sockaddr& s) : sa_ (0 ), sa_len_(s.sa_len_) { if ((sa_ = reinterpret_cast(malloc(sa_len_))) == 0) { gu_throw_fatal; } memcpy(sa_, s.sa_, sa_len_); } gu::net::Sockaddr::~Sockaddr() { free(sa_); } ///////////////////////////////////////////////////////////////////////// // MReq implementation ///////////////////////////////////////////////////////////////////////// static unsigned int get_ifindex_by_addr(const gu::net::Sockaddr& addr) { if (addr.is_anyaddr() == true) { return 0; } unsigned int idx(-1); int err(0); #if defined(__APPLE__) || defined(__FreeBSD__) struct ifaddrs *if_addrs = NULL; struct ifaddrs *if_addr = NULL; if (getifaddrs (&if_addrs) != 0) { err = errno; goto out; } for (if_addr = if_addrs; if_addr != NULL; if_addr = if_addr->ifa_next) { try { gu::net::Sockaddr sa (if_addr->ifa_addr, sizeof (struct sockaddr)); if (sa.get_family () == addr.get_family () && memcmp (sa.get_addr (), addr.get_addr (), addr.get_addr_len ()) == 0) { idx = if_nametoindex (if_addr->ifa_name); goto out; } } catch (gu::Exception& e) { } } out: # else /* !__APPLE__ && !__FreeBSD__ */ struct ifconf ifc; memset(&ifc, 0, sizeof(struct ifconf)); ifc.ifc_len = 16*sizeof(struct ifreq); std::vector ifr(16); ifc.ifc_req = &ifr[0]; int fd(socket(AF_INET, SOCK_DGRAM, 0)); if (fd == -1) { err = errno; gu_throw_error(err) << "could not create socket"; } if ((err = ioctl(fd, GU_SIOCGIFCONF, &ifc)) == -1) { err = errno; goto out; } log_debug << "read: " << ifc.ifc_len; for (size_t i(0); i < ifc.ifc_len/sizeof(struct ifreq); ++i) { struct ifreq* ifrp(&ifr[i]); try { log_debug << "read: " << ifrp->ifr_name; gu::net::Sockaddr sa(&ifrp->ifr_addr, sizeof(struct sockaddr)); if (sa.get_family() == addr.get_family() && memcmp(sa.get_addr(), addr.get_addr(), addr.get_addr_len()) == 0) { if ((err = ioctl(fd, GU_SIOCGIFINDEX, ifrp, sizeof(struct ifreq))) == -1) { err = errno; } #if defined(__linux__) idx = ifrp->ifr_ifindex; #elif defined(__sun__) idx = ifrp->ifr_index; #else # error "Unsupported ifreq structure" #endif goto out; } } catch (gu::Exception& e) { } } out: close(fd); #endif /* !__APPLE__ && !__FreeBSD__ */ if (err != 0) { gu_throw_error(err) << "failed to get interface index"; } else { log_debug << "returning ifindex: " << idx; } return idx; } gu::net::MReq::MReq(const Sockaddr& mcast_addr, const Sockaddr& if_addr) : mreq_ ( 0), mreq_len_ ( 0), ipproto_ ( 0), add_membership_opt_ (-1), drop_membership_opt_(-1), multicast_if_opt_ (-1), multicast_loop_opt_ (-1), multicast_ttl_opt_ (-1) { log_debug << mcast_addr.get_family() << " " << if_addr.get_family(); if (mcast_addr.get_family() != if_addr.get_family()) { gu_throw_fatal << "address families do not match: " << mcast_addr.get_family() << ", " << if_addr.get_family(); } if (mcast_addr.get_family() != AF_INET && mcast_addr.get_family() != AF_INET6) { gu_throw_fatal << "Mreq: address family " << mcast_addr.get_family() << " not supported"; } get_ifindex_by_addr(if_addr); mreq_len_ = (mcast_addr.get_family() == AF_INET ? sizeof(struct ip_mreq) : sizeof(struct ipv6_mreq)); if ((mreq_ = malloc(mreq_len_)) == 0) { gu_throw_fatal << "could not allocate memory"; } memset(mreq_, 0, mreq_len_); switch (mcast_addr.get_family()) { case AF_INET: { struct ip_mreq* mr(reinterpret_cast(mreq_)); mr->imr_multiaddr.s_addr = *reinterpret_cast(mcast_addr.get_addr()); mr->imr_interface.s_addr = *reinterpret_cast(if_addr.get_addr()); ipproto_ = IPPROTO_IP; add_membership_opt_ = IP_ADD_MEMBERSHIP; drop_membership_opt_ = IP_DROP_MEMBERSHIP; multicast_if_opt_ = IP_MULTICAST_IF; multicast_loop_opt_ = IP_MULTICAST_LOOP; multicast_ttl_opt_ = IP_MULTICAST_TTL; break; } case AF_INET6: { struct ipv6_mreq* mr(reinterpret_cast(mreq_)); mr->ipv6mr_multiaddr = *reinterpret_cast(mcast_addr.get_addr()); mr->ipv6mr_interface = get_ifindex_by_addr(if_addr); ipproto_ = IPPROTO_IPV6; add_membership_opt_ = IPV6_ADD_MEMBERSHIP; drop_membership_opt_ = IPV6_DROP_MEMBERSHIP; multicast_loop_opt_ = IPV6_MULTICAST_LOOP; multicast_ttl_opt_ = IPV6_MULTICAST_HOPS; break; } } } gu::net::MReq::~MReq() { free(mreq_); } const void* gu::net::MReq::get_multicast_if_value() const { switch (ipproto_) { case IPPROTO_IP: return &reinterpret_cast(mreq_)->imr_interface; case IPPROTO_IPV6: return &reinterpret_cast(mreq_)->ipv6mr_interface; default: gu_throw_fatal << "get_multicast_if_value() not implemented for: " << ipproto_; } } int gu::net::MReq::get_multicast_if_value_size() const { switch (ipproto_) { case IPPROTO_IP: return sizeof(reinterpret_cast(mreq_)->imr_interface); case IPPROTO_IPV6: return sizeof(reinterpret_cast(mreq_)->ipv6mr_interface); default: gu_throw_fatal << "get_multicast_if_value_size() not implemented for: " << ipproto_; } } ///////////////////////////////////////////////////////////////////////// // Addrinfo implementation ///////////////////////////////////////////////////////////////////////// gu::net::Addrinfo::Addrinfo(const addrinfo& ai) : ai_() { copy(ai, ai_); } gu::net::Addrinfo::Addrinfo(const Addrinfo& ai) : ai_() { copy(ai.ai_, ai_); } gu::net::Addrinfo::Addrinfo(const Addrinfo& ai, const Sockaddr& sa) : ai_() { if (ai.get_addrlen() != sa.get_sockaddr_len()) { gu_throw_fatal; } copy(ai.ai_, ai_); memcpy(ai_.ai_addr, &sa.get_sockaddr(), ai_.ai_addrlen); } gu::net::Addrinfo::~Addrinfo() { free(ai_.ai_addr); } std::string gu::net::Addrinfo::to_string() const { static const size_t max_addr_str_len = (6 /* tcp|udp:// */ + INET6_ADDRSTRLEN + 2 /* [] */ + 6 /* :portt */); std::string ret; ret.reserve(max_addr_str_len); Sockaddr addr(ai_.ai_addr, ai_.ai_addrlen); switch (get_socktype()) { case SOCK_STREAM: ret += "tcp://"; break; case SOCK_DGRAM: ret += "udp://"; break; default: gu_throw_error(EINVAL) << "invalid socktype: " << get_socktype(); } char dst[INET6_ADDRSTRLEN + 1]; if (inet_ntop(get_family(), addr.get_addr(), dst, sizeof(dst)) == 0) { gu_throw_error(errno) << "inet ntop failed"; } switch (get_family()) { case AF_INET: ret += dst; break; case AF_INET6: ret += "["; ret += dst; ret += "]"; break; default: gu_throw_error(EINVAL) << "invalid address family: " << get_family(); } ret += ":" + gu::to_string(ntohs(addr.get_port())); ret.reserve(0); // free unused space if possible return ret; } ///////////////////////////////////////////////////////////////////////// // Public methods ///////////////////////////////////////////////////////////////////////// gu::net::Addrinfo gu::net::resolve(const URI& uri) { SchemeMap::const_iterator i(scheme_map.find(uri.get_scheme())); if (i == scheme_map.end()) { gu_throw_error(EINVAL) << "invalid scheme: " << uri.get_scheme(); } try { std::string host(uri.get_host()); // remove [] if this is IPV6 address size_t pos(host.find_first_of('[')); if (pos != std::string::npos) { host.erase(pos, pos + 1); pos = host.find_first_of(']'); if (pos == std::string::npos) { gu_throw_error(EINVAL) << "invalid host: " << uri.get_host(); } host.erase(pos, pos + 1); } int err; addrinfo* ai(0); try { err = getaddrinfo(host.c_str(), uri.get_port().c_str(), SchemeMap::get_addrinfo(i), &ai); } catch (NotSet&) { err = getaddrinfo(host.c_str(), NULL, SchemeMap::get_addrinfo(i), &ai); } if (err != 0) { // Use EHOSTUNREACH as generic error number in case errno // is zero. Real error should be apparent from exception message gu_throw_error(errno == 0 ? EHOSTUNREACH : errno) << "getaddrinfo failed with error '" << gai_strerror(err) << "' (" << err << ") for " << uri.to_string(); } // Assume that the first entry is ok Addrinfo ret(*ai); freeaddrinfo(ai); return ret; } catch (NotFound& nf) { gu_throw_error(EINVAL) << "invalid URI: " << uri.to_string(); } } percona-xtradb-cluster-galera/galerautils/src/gu_resolver.hpp0000644000000000000000000001644712247075736025036 0ustar rootroot00000000000000/* * Copyright (C) 2008-2012 Codership Oy * * $Id$ */ /*! * @file gu_resolver.hpp Simple resolver utility */ #ifndef __GU_RESOLVER_HPP__ #define __GU_RESOLVER_HPP__ #include "gu_throw.hpp" #include #include #include #include // Forward declarations namespace gu { class URI; } // namespace gu // Declarations namespace gu { namespace net { /*! * @class Sockaddr * * @brief Class encapsulating struct sockaddr. * * Class encapsulating struct sockaddr and providing * simple interface to access sockaddr fields. */ class Sockaddr; /*! * @class IMReq * * @brief Class encapsulating imreq structs. */ class MReq; /*! * @class Addrinfo * * @brief Class encapsulating struct addrinfo. * * Class encapsulating struct addrinfo and providing interface * to access addrinfo fields. */ class Addrinfo; /*! * Resolve address given in @uri * * @return Addrinfo object representing address * * @throw gu::Exception in case of failure */ Addrinfo resolve(const gu::URI& uri); } // namespace net } // namespace gu class gu::net::Sockaddr { public: /*! * Default constuctor. * * @param sa Pointer to sockaddr struct * @param sa_len Length of sockaddr struct */ Sockaddr(const sockaddr* sa, socklen_t sa_len); /*! * Copy constructor. * * @param sa Reference to Sockaddr */ Sockaddr(const Sockaddr& sa); /*! * Destructor */ ~Sockaddr(); /*! * Get address family. * * @return Address family */ sa_family_t get_family() const { return sa_->sa_family; } /*! * Get port in network byte order. This is applicable only * for AF_INET, AF_INET6. * * @return Port in nework byte order */ unsigned short get_port() const { switch(sa_->sa_family) { case AF_INET: return reinterpret_cast(sa_)->sin_port; case AF_INET6: return reinterpret_cast(sa_)->sin6_port; default: gu_throw_fatal; } } /*! * Get pointer to address. Return value is pointer to void, * user must do casting by himself. * * @todo: Figure out how this could be done in type safe way. * * @return Void pointer to address element. */ const void* get_addr() const { switch(sa_->sa_family) { case AF_INET: return &reinterpret_cast(sa_)->sin_addr; case AF_INET6: return &reinterpret_cast(sa_)->sin6_addr; default: gu_throw_fatal << "invalid address family: " << sa_->sa_family; } } socklen_t get_addr_len() const { switch(sa_->sa_family) { case AF_INET: return sizeof(reinterpret_cast(sa_)->sin_addr); case AF_INET6: return sizeof(reinterpret_cast(sa_)->sin6_addr); default: gu_throw_fatal; } } /*! * Get non-const reference to sockaddr struct. * * @return Non-const reference to sockaddr struct. */ sockaddr& get_sockaddr() { return *sa_; } /*! * Get const reference to sockaddr struct. * * @return Const reference to sockaddr struct. */ const sockaddr& get_sockaddr() const { return *sa_; } /*! * Get length of sockaddr struct. * * @return Length of sockaddr struct */ socklen_t get_sockaddr_len() const { return sa_len_; } bool is_multicast() const; bool is_broadcast() const; bool is_anyaddr() const; static Sockaddr get_anyaddr(const Sockaddr& sa) { Sockaddr ret(sa); switch(ret.sa_->sa_family) { case AF_INET: reinterpret_cast(ret.sa_)->sin_addr.s_addr = 0; break; case AF_INET6: memset(&reinterpret_cast(ret.sa_)->sin6_addr, 0, sizeof(struct in6_addr)); break; default: gu_throw_fatal << "invalid address family: " << ret.sa_->sa_family; } return ret; } Sockaddr& operator=(const Sockaddr& sa) { memcpy(sa_, sa.sa_, sa_len_); return *this; } private: sockaddr* sa_; socklen_t sa_len_; }; class gu::net::MReq { public: MReq(const Sockaddr& mcast_addr, const Sockaddr& if_addr); ~MReq(); const void* get_mreq() const { return mreq_; } socklen_t get_mreq_len() const { return mreq_len_; } int get_ipproto() const { return ipproto_; } int get_add_membership_opt() const { return add_membership_opt_; } int get_drop_membership_opt() const { return drop_membership_opt_; } int get_multicast_if_opt() const { return multicast_if_opt_; } int get_multicast_loop_opt() const { return multicast_loop_opt_; } int get_multicast_ttl_opt() const { return multicast_ttl_opt_; } const void* get_multicast_if_value() const; int get_multicast_if_value_size() const; private: MReq(const MReq&); void operator=(const MReq&); void* mreq_; socklen_t mreq_len_; int ipproto_; int add_membership_opt_; int drop_membership_opt_; int multicast_if_opt_; int multicast_loop_opt_; int multicast_ttl_opt_; }; class gu::net::Addrinfo { public: /*! * Default constructor. * * @param ai Const reference to addrinfo struct */ Addrinfo(const addrinfo& ai); /*! * Copy costructor. * * @param ai Const reference to Addrinfo object to copy */ Addrinfo(const Addrinfo& ai); /*! * Copy constructor that replaces @ai sockaddr struct. * * @param ai Const reference to Addrinfo object to copy * @param sa Const reference to Sockaddr struct that replaces * @ai sockaddr data */ Addrinfo(const Addrinfo& ai, const Sockaddr& sa); /*! * Destructor. */ ~Addrinfo(); /*! * Get address family, AF_INET, AF_INET6 etc. * * @return Address family */ int get_family() const { return ai_.ai_family; } /*! * Get socket type, SOCK_STREAM, SOCK_DGRAM etc * * @return Socket type */ int get_socktype() const { return ai_.ai_socktype; } /*! * Get protocol. * * @return Protocol */ int get_protocol() const { return ai_.ai_protocol; } /*! * Get length of associated sockaddr struct * * @return Length of associated sockaddr struct */ socklen_t get_addrlen() const { return ai_.ai_addrlen; } /*! * Get associated Sockaddr object. * * @return Associated Sockaddr object */ Sockaddr get_addr() const { return Sockaddr(ai_.ai_addr, ai_.ai_addrlen); } /*! * Get string representation of the addrinfo. * * @return String representation of the addrinfo */ std::string to_string() const; private: addrinfo ai_; }; #endif /* __GU_RESOLVER_HPP__ */ percona-xtradb-cluster-galera/galerautils/src/gu_serialize.hpp0000644000000000000000000002513012247075736025151 0ustar rootroot00000000000000/* * Copyright (C) 2009-2012 Codership Oy */ /*! * @file Helper templates for serialization/unserialization. * As we are usually working on little endian platforms, integer * storage order is little-endian - in other words we use "Galera" * order, which is by default little-endian. * * What is going on down there? Templates are good. However we do * not serialize the value of size_t variable into sizeof(size_t) * bytes. We serialize it into a globally consistent, fixed number * of bytes, regardless of the local size of size_t variable. * * Hence templating by the source variable size should not be used. * Instead there are functions/templates that serialize to an explicit * number of bytes. * * @todo Templates are safe to use with integer types only. Adjust them * to work also with classes that have special serialization * routines. * @todo Make buffer serialization functions Buffer class methods. * @todo Alignment issues. */ #ifndef GU_SERIALIZE_HPP #define GU_SERIALIZE_HPP #include "gu_throw.hpp" #include "gu_byteswap.hpp" #include "gu_buffer.hpp" #include #include namespace gu { template inline size_t serial_size(const T& t) { return t.serial_size(); } template <> inline size_t serial_size(const uint8_t& b) { return sizeof(b); } template <> inline size_t serial_size(const uint16_t& b) { return sizeof(b); } template <> inline size_t serial_size(const uint32_t& b) { return sizeof(b); } template <> inline size_t serial_size(const uint64_t& b) { return sizeof(b); } /* Should not be used directly! */ template inline size_t __private_serialize(const FROM& f, byte_t* const buf, size_t const buflen, size_t const offset) { BOOST_STATIC_ASSERT(std::numeric_limits::is_integer); BOOST_STATIC_ASSERT(std::numeric_limits::is_integer); BOOST_STATIC_ASSERT(sizeof(FROM) == sizeof(TO)); size_t const ret = offset + sizeof(TO); if (gu_unlikely(ret > buflen)) gu_throw_error(EMSGSIZE) << ret << " > " << buflen; *reinterpret_cast(buf + offset) = htog(f); return ret; } /* Should not be used directly! */ template inline size_t __private_unserialize(const byte_t* const buf, size_t const buflen, size_t const offset, TO& t) { BOOST_STATIC_ASSERT(std::numeric_limits::is_integer); BOOST_STATIC_ASSERT(std::numeric_limits::is_integer); BOOST_STATIC_ASSERT(sizeof(FROM) == sizeof(TO)); size_t const ret = offset + sizeof(t); if (gu_unlikely(ret > buflen)) gu_throw_error(EMSGSIZE) << ret << " > " << buflen; t = gtoh(*reinterpret_cast(buf + offset)); return ret; } template GU_FORCE_INLINE size_t serialize1(const T& t, byte_t* const buf, size_t const buflen, size_t const offset) { return __private_serialize(t, buf, buflen, offset); } template GU_FORCE_INLINE size_t unserialize1(const byte_t* const buf, size_t const buflen, size_t const offset, T& t) { return __private_unserialize(buf, buflen, offset, t); } template GU_FORCE_INLINE size_t serialize2(const T& t, byte_t* const buf, size_t const buflen, size_t const offset) { return __private_serialize(t, buf, buflen, offset); } template GU_FORCE_INLINE size_t unserialize2(const byte_t* const buf, size_t const buflen, size_t const offset, T& t) { return __private_unserialize(buf, buflen, offset, t); } template GU_FORCE_INLINE size_t serialize4(const T& t, byte_t* const buf, size_t const buflen, size_t const offset) { return __private_serialize(t, buf, buflen, offset); } template GU_FORCE_INLINE size_t unserialize4(const byte_t* const buf, size_t const buflen, size_t const offset, T& t) { return __private_unserialize(buf, buflen, offset, t); } template GU_FORCE_INLINE size_t serialize8(const T& t, byte_t* const buf, size_t const buflen, size_t const offset) { return __private_serialize(t, buf, buflen, offset); } template GU_FORCE_INLINE size_t unserialize8(const byte_t* const buf, size_t const buflen, size_t const offset, T& t) { return __private_unserialize(buf, buflen, offset, t); } template inline size_t __private_serial_size(const gu::Buffer& sb) { BOOST_STATIC_ASSERT(std::numeric_limits::is_integer); if (sb.size() > std::numeric_limits::max()) gu_throw_error(ERANGE) << sb.size() << " unrepresentable in " << sizeof(ST) << " bytes."; return sizeof(ST) + sb.size(); } GU_FORCE_INLINE size_t serial_size1(const gu::Buffer& sb) { return __private_serial_size(sb); } GU_FORCE_INLINE size_t serial_size2(const gu::Buffer& sb) { return __private_serial_size(sb); } GU_FORCE_INLINE size_t serial_size4(const gu::Buffer& sb) { return __private_serial_size(sb); } GU_FORCE_INLINE size_t serial_size8(const gu::Buffer& sb) { return __private_serial_size(sb); } template inline size_t __private_serialize(const gu::Buffer& b, gu::byte_t* const buf, size_t const buflen, size_t offset) { size_t const ret = offset + __private_serial_size(b); if (ret > buflen) gu_throw_error(EMSGSIZE) << ret << " > " << buflen; offset = __private_serialize(static_cast(b.size()), buf, buflen, offset); copy(b.begin(), b.end(), buf + offset); return ret; } template inline size_t __private_unserialize(const gu::byte_t* const buf, size_t const buflen, size_t offset, gu::Buffer& b) { BOOST_STATIC_ASSERT(std::numeric_limits::is_integer); ST len(0); size_t ret = offset + sizeof(len); if (ret > buflen) gu_throw_error(EMSGSIZE) << ret << " > " << buflen; offset = __private_unserialize(buf, buflen, offset, len); ret += len; if (ret > buflen) gu_throw_error(EMSGSIZE) << ret << " > " << buflen; b.resize(len); copy(buf + offset, buf + ret, b.begin()); return ret; } GU_FORCE_INLINE size_t serialize1(const gu::Buffer& b, gu::byte_t* const buf, size_t const buflen, size_t const offset) { return __private_serialize(b, buf, buflen, offset); } GU_FORCE_INLINE size_t unserialize1(const gu::byte_t* const buf, size_t const buflen, size_t const offset, gu::Buffer& b) { return __private_unserialize(buf, buflen, offset, b); } GU_FORCE_INLINE size_t serialize2(const gu::Buffer& b, gu::byte_t* const buf, size_t const buflen, size_t const offset) { return __private_serialize(b, buf, buflen, offset); } GU_FORCE_INLINE size_t unserialize2(const gu::byte_t* const buf, size_t const buflen, size_t const offset, gu::Buffer& b) { return __private_unserialize(buf, buflen, offset, b); } GU_FORCE_INLINE size_t serialize4(const gu::Buffer& b, gu::byte_t* const buf, size_t const buflen, size_t const offset) { return __private_serialize(b, buf, buflen, offset); } GU_FORCE_INLINE size_t unserialize4(const gu::byte_t* const buf, size_t const buflen, size_t const offset, gu::Buffer& b) { return __private_unserialize(buf, buflen, offset, b); } GU_FORCE_INLINE size_t serialize8(const gu::Buffer& b, gu::byte_t* const buf, size_t const buflen, size_t const offset) { return __private_serialize(b, buf, buflen, offset); } GU_FORCE_INLINE size_t unserialize8(const gu::byte_t* const buf, size_t const buflen, size_t const offset, gu::Buffer& b) { return __private_unserialize(buf, buflen, offset, b); } } // namespace gu #endif // GU_SERIALIZE_HPP percona-xtradb-cluster-galera/galerautils/src/gu_spooky.c0000644000000000000000000000051512247075736024141 0ustar rootroot00000000000000// Copyright (C) 2012 Codership Oy /** * @file external Spooky hash implementation to avoid code bloat * * $Id: gu_spooky.c 2821 2012-06-20 18:42:43Z alex $ */ #include "gu_spooky.h" void gu_spooky128_host (const void* const msg, size_t const len, uint64_t* res) { gu_spooky_inline (msg, len, res); } percona-xtradb-cluster-galera/galerautils/src/gu_spooky.h0000644000000000000000000003256612247075736024161 0ustar rootroot00000000000000// Copyright (C) 2012 Codership Oy /*! * @file Spooky hash by Bob Jenkins: * http://www.burtleburtle.net/bob/c/spooky.h * * Original author comments preserved in C++ style * * $Id: gu_spooky.h 2821 2012-06-20 18:42:43Z alex $ */ #ifndef _gu_spooky_h_ #define _gu_spooky_h_ #include "gu_types.h" #include "gu_byteswap.h" #ifdef __cplusplus extern "C" { #endif #include // for memcpy() /*! GCC complains about 'initializer element is not constant', hence macros */ #define _spooky_numVars 12 #define _spooky_blockSize 96 /* (_spooky_numVars * 8) */ #define _spooky_bufSize 192 /* (_spooky_blockSize * 2) */ static uint64_t const _spooky_const = GU_ULONG_LONG(0xDEADBEEFDEADBEEF); // // This is used if the input is 96 bytes long or longer. // // The internal state is fully overwritten every 96 bytes. // Every input bit appears to cause at least 128 bits of entropy // before 96 other bytes are combined, when run forward or backward // For every input bit, // Two inputs differing in just that input bit // Where "differ" means xor or subtraction // And the base value is random // When run forward or backwards one Mix // I tried 3 pairs of each; they all differed by at least 212 bits. // static GU_FORCE_INLINE void _spooky_mix( const uint64_t *data, uint64_t* s0, uint64_t* s1, uint64_t* s2, uint64_t* s3, uint64_t* s4, uint64_t* s5, uint64_t* s6, uint64_t* s7, uint64_t* s8, uint64_t* s9, uint64_t* sA, uint64_t* sB) { *s0 += gu_le64(data[0]); *s2 ^= *sA; *sB ^= *s0; *s0 =GU_ROTL64(*s0,11); *sB += *s1; *s1 += gu_le64(data[1]); *s3 ^= *sB; *s0 ^= *s1; *s1 =GU_ROTL64(*s1,32); *s0 += *s2; *s2 += gu_le64(data[2]); *s4 ^= *s0; *s1 ^= *s2; *s2 =GU_ROTL64(*s2,43); *s1 += *s3; *s3 += gu_le64(data[3]); *s5 ^= *s1; *s2 ^= *s3; *s3 =GU_ROTL64(*s3,31); *s2 += *s4; *s4 += gu_le64(data[4]); *s6 ^= *s2; *s3 ^= *s4; *s4 =GU_ROTL64(*s4,17); *s3 += *s5; *s5 += gu_le64(data[5]); *s7 ^= *s3; *s4 ^= *s5; *s5 =GU_ROTL64(*s5,28); *s4 += *s6; *s6 += gu_le64(data[6]); *s8 ^= *s4; *s5 ^= *s6; *s6 =GU_ROTL64(*s6,39); *s5 += *s7; *s7 += gu_le64(data[7]); *s9 ^= *s5; *s6 ^= *s7; *s7 =GU_ROTL64(*s7,57); *s6 += *s8; *s8 += gu_le64(data[8]); *sA ^= *s6; *s7 ^= *s8; *s8 =GU_ROTL64(*s8,55); *s7 += *s9; *s9 += gu_le64(data[9]); *sB ^= *s7; *s8 ^= *s9; *s9 =GU_ROTL64(*s9,54); *s8 += *sA; *sA += gu_le64(data[10]); *s0 ^= *s8; *s9 ^= *sA; *sA =GU_ROTL64(*sA,22); *s9 += *sB; *sB += gu_le64(data[11]); *s1 ^= *s9; *sA ^= *sB; *sB =GU_ROTL64(*sB,46); *sA += *s0; } // // Mix all 12 inputs together so that h0, h1 are a hash of them all. // // For two inputs differing in just the input bits // Where "differ" means xor or subtraction // And the base value is random, or a counting value starting at that bit // The final result will have each bit of h0, h1 flip // For every input bit, // with probability 50 +- .3% // For every pair of input bits, // with probability 50 +- 3% // // This does not rely on the last Mix() call having already mixed some. // Two iterations was almost good enough for a 64-bit result, but a // 128-bit result is reported, so End() does three iterations. // static GU_FORCE_INLINE void _spooky_end_part( uint64_t* h0, uint64_t* h1, uint64_t* h2, uint64_t* h3, uint64_t* h4, uint64_t* h5, uint64_t* h6, uint64_t* h7, uint64_t* h8, uint64_t* h9, uint64_t* h10,uint64_t* h11) { *h11+= *h1; *h2 ^= *h11; *h1 = GU_ROTL64(*h1,44); *h0 += *h2; *h3 ^= *h0; *h2 = GU_ROTL64(*h2,15); *h1 += *h3; *h4 ^= *h1; *h3 = GU_ROTL64(*h3,34); *h2 += *h4; *h5 ^= *h2; *h4 = GU_ROTL64(*h4,21); *h3 += *h5; *h6 ^= *h3; *h5 = GU_ROTL64(*h5,38); *h4 += *h6; *h7 ^= *h4; *h6 = GU_ROTL64(*h6,33); *h5 += *h7; *h8 ^= *h5; *h7 = GU_ROTL64(*h7,10); *h6 += *h8; *h9 ^= *h6; *h8 = GU_ROTL64(*h8,13); *h7 += *h9; *h10^= *h7; *h9 = GU_ROTL64(*h9,38); *h8 += *h10; *h11^= *h8; *h10= GU_ROTL64(*h10,53); *h9 += *h11; *h0 ^= *h9; *h11= GU_ROTL64(*h11,42); *h10+= *h0; *h1 ^= *h10; *h0 = GU_ROTL64(*h0,54); } static GU_FORCE_INLINE void _spooky_end( uint64_t* h0, uint64_t* h1, uint64_t* h2, uint64_t* h3, uint64_t* h4, uint64_t* h5, uint64_t* h6, uint64_t* h7, uint64_t* h8, uint64_t* h9, uint64_t* h10,uint64_t* h11) { #if 0 _spooky_end_part(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); _spooky_end_part(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); _spooky_end_part(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); #endif int i; for (i = 0; i < 3; i++) { _spooky_end_part(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); } } // // The goal is for each bit of the input to expand into 128 bits of // apparent entropy before it is fully overwritten. // n trials both set and cleared at least m bits of h0 h1 h2 h3 // n: 2 m: 29 // n: 3 m: 46 // n: 4 m: 57 // n: 5 m: 107 // n: 6 m: 146 // n: 7 m: 152 // when run forwards or backwards // for all 1-bit and 2-bit diffs // with diffs defined by either xor or subtraction // with a base of all zeros plus a counter, or plus another bit, or random // static GU_FORCE_INLINE void _spooky_short_mix(uint64_t* h0, uint64_t* h1, uint64_t* h2, uint64_t* h3) { *h2 = GU_ROTL64(*h2,50); *h2 += *h3; *h0 ^= *h2; *h3 = GU_ROTL64(*h3,52); *h3 += *h0; *h1 ^= *h3; *h0 = GU_ROTL64(*h0,30); *h0 += *h1; *h2 ^= *h0; *h1 = GU_ROTL64(*h1,41); *h1 += *h2; *h3 ^= *h1; *h2 = GU_ROTL64(*h2,54); *h2 += *h3; *h0 ^= *h2; *h3 = GU_ROTL64(*h3,48); *h3 += *h0; *h1 ^= *h3; *h0 = GU_ROTL64(*h0,38); *h0 += *h1; *h2 ^= *h0; *h1 = GU_ROTL64(*h1,37); *h1 += *h2; *h3 ^= *h1; *h2 = GU_ROTL64(*h2,62); *h2 += *h3; *h0 ^= *h2; *h3 = GU_ROTL64(*h3,34); *h3 += *h0; *h1 ^= *h3; *h0 = GU_ROTL64(*h0,5); *h0 += *h1; *h2 ^= *h0; *h1 = GU_ROTL64(*h1,36); *h1 += *h2; *h3 ^= *h1; } // // Mix all 4 inputs together so that h0, h1 are a hash of them all. // // For two inputs differing in just the input bits // Where "differ" means xor or subtraction // And the base value is random, or a counting value starting at that bit // The final result will have each bit of h0, h1 flip // For every input bit, // with probability 50 +- .3% (it is probably better than that) // For every pair of input bits, // with probability 50 +- .75% (the worst case is approximately that) // static GU_FORCE_INLINE void _spooky_short_end(uint64_t* h0, uint64_t* h1, uint64_t* h2, uint64_t* h3) { *h3 ^= *h2; *h2 = GU_ROTL64(*h2,15); *h3 += *h2; *h0 ^= *h3; *h3 = GU_ROTL64(*h3,52); *h0 += *h3; *h1 ^= *h0; *h0 = GU_ROTL64(*h0,26); *h1 += *h0; *h2 ^= *h1; *h1 = GU_ROTL64(*h1,51); *h2 += *h1; *h3 ^= *h2; *h2 = GU_ROTL64(*h2,28); *h3 += *h2; *h0 ^= *h3; *h3 = GU_ROTL64(*h3,9); *h0 += *h3; *h1 ^= *h0; *h0 = GU_ROTL64(*h0,47); *h1 += *h0; *h2 ^= *h1; *h1 = GU_ROTL64(*h1,54); *h2 += *h1; *h3 ^= *h2; *h2 = GU_ROTL64(*h2,32); *h3 += *h2; *h0 ^= *h3; *h3 = GU_ROTL64(*h3,25); *h0 += *h3; *h1 ^= *h0; *h0 = GU_ROTL64(*h0,63); *h1 += *h0; } // // short hash ... it could be used on any message, // but it's used by Spooky just for short messages. // static GU_INLINE void gu_spooky_short_host( const void* const message, size_t const length, uint64_t* const hash) { union { const uint8_t* p8; uint32_t* p32; uint64_t* p64; #if !GU_ALLOW_UNALIGNED_READS size_t i; #endif /* !GU_ALLOW_UNALIGNED_READS */ } u; u.p8 = (const uint8_t *)message; #if !GU_ALLOW_UNALIGNED_READS if (u.i & 0x7) { uint64_t buf[_spooky_numVars << 1]; memcpy(buf, message, length); u.p64 = buf; } #endif /* !GU_ALLOW_UNALIGNED_READS */ size_t remainder = length & 0x1F; /* length%32 */ /* author version : */ // uint64_t a = gu_le64(*hash[0]); // uint64_t b = gu_le64(*hash[1]); /* consistent seed version: */ uint64_t a = 0; uint64_t b = 0; uint64_t c = _spooky_const; uint64_t d = _spooky_const; if (length > 15) { const uint64_t *end = u.p64 + ((length >> 5) << 2); /* (length/32)*4 */ // handle all complete sets of 32 bytes for (; u.p64 < end; u.p64 += 4) { c += gu_le64(u.p64[0]); d += gu_le64(u.p64[1]); _spooky_short_mix(&a, &b, &c, &d); a += gu_le64(u.p64[2]); b += gu_le64(u.p64[3]); } //Handle the case of 16+ remaining bytes. if (remainder >= 16) { c += gu_le64(u.p64[0]); d += gu_le64(u.p64[1]); _spooky_short_mix(&a, &b, &c, &d); u.p64 += 2; remainder -= 16; } } // Handle the last 0..15 bytes, and its length d = ((uint64_t)length) << 56; switch (remainder) { case 15: d += ((uint64_t)u.p8[14]) << 48; case 14: d += ((uint64_t)u.p8[13]) << 40; case 13: d += ((uint64_t)u.p8[12]) << 32; case 12: d += gu_le32(u.p32[2]); c += gu_le64(u.p64[0]); break; case 11: d += ((uint64_t)u.p8[10]) << 16; case 10: d += ((uint64_t)u.p8[9]) << 8; case 9: d += (uint64_t)u.p8[8]; case 8: c += gu_le64(u.p64[0]); break; case 7: c += ((uint64_t)u.p8[6]) << 48; case 6: c += ((uint64_t)u.p8[5]) << 40; case 5: c += ((uint64_t)u.p8[4]) << 32; case 4: c += gu_le32(u.p32[0]); break; case 3: c += ((uint64_t)u.p8[2]) << 16; case 2: c += ((uint64_t)u.p8[1]) << 8; case 1: c += (uint64_t)u.p8[0]; break; case 0: c += _spooky_const; d += _spooky_const; } _spooky_short_end(&a, &b, &c, &d); // @note - in native-endian order! hash[0] = a; hash[1] = b; } static GU_FORCE_INLINE void gu_spooky_short( const void* message, size_t length, void* const hash) { uint64_t* const u64 = (uint64_t*)hash; gu_spooky_short_host(message, length, u64); u64[0] = gu_le64(u64[0]); u64[1] = gu_le64(u64[1]); } // do the whole hash in one call static GU_INLINE void gu_spooky_inline ( const void* const message, size_t const length, uint64_t* const hash) { #ifdef GU_USE_SPOOKY_SHORT if (length < _spooky_bufSize) { gu_spooky_short_base (message, length, hash); return; } #endif /* GU_USE_SPOOKY_SHORT */ uint64_t h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11; uint64_t buf[_spooky_numVars]; uint64_t* end; union { const uint8_t* p8; uint64_t* p64; #if !GU_ALLOW_UNALIGNED_READS size_t i; #endif /* !GU_ALLOW_UNALIGNED_READS */ } u; size_t remainder; /* this is how the author wants it: a possibility for different seeds h0=h3=h6=h9 = gu_le64(hash[0]); h1=h4=h7=h10 = gu_le64(hash[1]); * this is how we want it - constant seed */ h0=h3=h6=h9 = 0; h1=h4=h7=h10 = 0; h2=h5=h8=h11 = _spooky_const; u.p8 = (const uint8_t*) message; end = u.p64 + (length/_spooky_blockSize)*_spooky_numVars; // handle all whole _spooky_blockSize blocks of bytes #if !GU_ALLOW_UNALIGNED_READS if ((u.i & 0x7) == 0) { #endif /* !GU_ALLOW_UNALIGNED_READS */ while (u.p64 < end) { _spooky_mix(u.p64, &h0,&h1,&h2,&h3,&h4,&h5,&h6,&h7,&h8,&h9,&h10,&h11); u.p64 += _spooky_numVars; } #if !GU_ALLOW_UNALIGNED_READS } else { while (u.p64 < end) { memcpy(buf, u.p64, _spooky_blockSize); _spooky_mix(buf, &h0,&h1,&h2,&h3,&h4,&h5,&h6,&h7,&h8,&h9,&h10,&h11); u.p64 += _spooky_numVars; } } #endif /* !GU_ALLOW_UNALIGNED_READS */ // handle the last partial block of _spooky_blockSize bytes remainder = (length - ((const uint8_t*)end - (const uint8_t*)message)); memcpy(buf, end, remainder); memset(((uint8_t*)buf) + remainder, 0, _spooky_blockSize - remainder); ((uint8_t*)buf)[_spooky_blockSize - 1] = remainder; _spooky_mix(buf, &h0,&h1,&h2,&h3,&h4,&h5,&h6,&h7,&h8,&h9,&h10,&h11); // do some final mixing _spooky_end(&h0,&h1,&h2,&h3,&h4,&h5,&h6,&h7,&h8,&h9,&h10,&h11); /*! @note: in native order */ hash[0] = h0; hash[1] = h1; } /* As is apparent from the gu_spooky_inline(), Spooky hash is enormous. * Since it has advantage only on long messages, it makes sense to make it * a regular function to avoid code bloat. * WARNING: does not do final endian conversion! */ extern void gu_spooky128_host (const void* const msg, size_t const len, uint64_t* res); /* returns hash in the canonical byte order, as a byte array */ static GU_FORCE_INLINE void gu_spooky128 (const void* const msg, size_t const len, void* const res) { uint64_t* const r = (uint64_t*)res; gu_spooky128_host (msg, len, r); r[0] = gu_le64(r[0]); r[1] = gu_le64(r[1]); } /* returns hash as an integer, in host byte-order */ static GU_FORCE_INLINE uint64_t gu_spooky64 (const void* const msg, size_t const len) { uint64_t res[2]; gu_spooky128_host (msg, len, res); return res[0]; } /* returns hash as an integer, in host byte-order */ static GU_FORCE_INLINE uint32_t gu_spooky32 (const void* const msg, size_t const len) { uint64_t res[2]; gu_spooky128_host (msg, len, res); return (uint32_t)res[0]; } #ifdef __cplusplus } #endif #endif /* _gu_spooky_h_ */ percona-xtradb-cluster-galera/galerautils/src/gu_str.h0000644000000000000000000001061012247075736023427 0ustar rootroot00000000000000/* * Copyright (C) 2010 Codership Oy */ #ifndef GU_STR_H #define GU_STR_H #include #include #include #include /*! * Append after position */ static inline char* gu_str_append(char* str, size_t* off, const char* app, size_t app_len) { char* tmp; assert(str == NULL || *(str + *off - 1) == '\0'); tmp = realloc(str, *off + app_len + 1); if (tmp != NULL) { memcpy(tmp + *off, app, app_len + 1); *off += app_len + 1; } return tmp; } /*! * Get next string after position */ static inline const char* gu_str_next(const char* str) { return strchr(str, '\0') + 1; } /*! * Advance position starting from over n */ static inline const char* gu_str_advance(const char* str, size_t n) { const char* ptr = str; while (n-- > 0) { ptr = gu_str_next(ptr); } return ptr; } /* * Utilities to construct and scan tables from null terminated strings. * The table format is the following: * * name\0\columns\0\rows\0 * colname0\0colname1\0... * elem00\0elem01\0elem02\0... * elem10\0elem11\0elem\12\... * . * . * . */ static inline char* gu_str_table_set_name(char* str, size_t* off, const char* name) { return gu_str_append(str, off, name, strlen(name)); } static inline const char* gu_str_table_get_name(const char* str) { return str; } static inline char* gu_str_table_append_size(char* str, size_t* off, size_t n) { char buf[10]; size_t len = snprintf(buf, sizeof(buf), "%zu", n); return gu_str_append(str, off, buf, len); } static inline char* gu_str_table_set_n_cols(char* str, size_t* off, size_t n) { return gu_str_table_append_size(str, off, n); } static inline size_t gu_str_table_get_n_cols(const char* str) { str = gu_str_advance(str, 1); return strtoul(str, NULL, 0); } static inline char* gu_str_table_set_n_rows(char* str, size_t* off, size_t n) { return gu_str_table_append_size(str, off, n); } static inline size_t gu_str_table_get_n_rows(const char* str) { str = gu_str_advance(str, 2); return strtoul(str, NULL, 0); } static inline char* gu_str_table_set_cols(char* str, size_t *off, size_t n, const char const* cols[]) { size_t i; for (i = 0; i < n; ++i) { str = gu_str_append(str, off, cols[i], strlen(cols[i])); } return str; } static inline char* gu_str_table_append_row(char* str, size_t *off, size_t n, const char const* row[]) { size_t i; for (i = 0; i < n; ++i) { str = gu_str_append(str, off, row[i], strlen(row[i])); } return str; } static inline const char* gu_str_table_get_cols(const char* str, size_t n, char const* row[]) { size_t i; str = gu_str_advance(str, 3); for (i = 0; i < n; i++) { row[i] = str; str = gu_str_next(str); } return str; } static inline const char* gu_str_table_rows_begin(const char* str, size_t n) { return gu_str_advance(str, 3 + n); } static inline const char* gu_str_table_row_get(const char* str, size_t n, char const* row[]) { size_t i; for (i = 0; i < n; ++i) { row[i] = str; str = gu_str_next(str); } return str; } static inline void gu_str_table_print_row(FILE* file, size_t n, const char* const row[]) { size_t i; for (i = 0; i < n; ++i) { fprintf(file, "%s ", row[i]); } fprintf(file, "\n"); } static inline void gu_str_table_print(FILE* file, const char* str) { size_t i; size_t n_cols, n_rows; const char* ptr; char const**vec; fprintf(file, "%s\n", gu_str_table_get_name(str)); n_cols = gu_str_table_get_n_cols(str); n_rows = gu_str_table_get_n_rows(str); vec = malloc(n_cols*sizeof(char*)); ptr = gu_str_table_get_cols(str, n_cols, vec); gu_str_table_print_row(file, n_cols, vec); for (i = 0; i < n_rows; ++i) { ptr = gu_str_table_row_get(ptr, n_cols, vec); gu_str_table_print_row(file, n_cols, vec); } free(vec); } #endif /* GU_STR_H */ percona-xtradb-cluster-galera/galerautils/src/gu_string.cpp0000644000000000000000000000447412247075736024473 0ustar rootroot00000000000000// Copyright (C) 2009-2010 Codership Oy #include "gu_assert.hpp" #include "gu_string.hpp" #include using std::string; using std::vector; vector gu::strsplit(const string& s, char sep) { vector ret; size_t pos, prev_pos = 0; while ((pos = s.find_first_of(sep, prev_pos)) != string::npos) { ret.push_back(s.substr(prev_pos, pos - prev_pos)); prev_pos = pos + 1; } if (s.length() > prev_pos) { ret.push_back(s.substr(prev_pos, s.length() - prev_pos)); } return ret; } vector gu::tokenize(const string& s, const char sep, const char esc, const bool empty) { vector ret; size_t pos, prev_pos, search_pos; prev_pos = search_pos = 0; while ((pos = s.find_first_of(sep, search_pos)) != string::npos) { assert (pos >= prev_pos); if (esc != '\0' && pos > search_pos && esc == s[pos - 1]) { search_pos = pos + 1; continue; } if (pos > prev_pos || empty) { string t = s.substr(prev_pos, pos - prev_pos); // get rid of escapes size_t p, search_p = 0; while ((p = t.find_first_of(esc, search_p)) != string::npos && esc != '\0') { if (p > search_p) { t.erase(p, 1); search_p = p + 1; } } ret.push_back(t); } prev_pos = search_pos = pos + 1; } if (s.length() > prev_pos) { ret.push_back(s.substr(prev_pos, s.length() - prev_pos)); } else if (s.length() == prev_pos && empty) { assert(0 == prev_pos || s[prev_pos - 1] == sep); ret.push_back(""); } return ret; } void gu::trim (string& s) { const ssize_t s_length = s.length(); for (ssize_t begin = 0; begin < s_length; ++begin) { if (!isspace(s[begin])) { for (ssize_t end = s_length - 1; end >= begin; --end) { if (!isspace(s[end])) { s = s.substr(begin, end - begin + 1); return; } } assert(0); } } s.clear(); } percona-xtradb-cluster-galera/galerautils/src/gu_string.hpp0000644000000000000000000000174412247075736024475 0ustar rootroot00000000000000// Copyright (C) 2009-2010 Codership Oy #ifndef __GU_STRING_HPP__ #define __GU_STRING_HPP__ #include #include namespace gu { /*! * @brief Split string into tokens using given separator * * @param sep token separator */ std::vector strsplit(const std::string& s, char sep = ' '); /*! * @brief Split string into tokens using given separator and escape. * * @param sep token separator * @param esc separator escape sequence ('\0' to disable escapes) * @param empty whether to return empty tokens */ std::vector tokenize(const std::string& s, char sep = ' ', char esc = '\\', bool empty = false); /*! Remove non-alnum symbols from the beginning and end of string */ void trim (std::string& s); } #endif /* __GU_STRING_HPP__ */ percona-xtradb-cluster-galera/galerautils/src/gu_system.h0000644000000000000000000000151212247075736024144 0ustar rootroot00000000000000// Copyright (C) 2013 Codership Oy /** * @system/os/platform dependent functions/macros * * $Id$ */ #ifndef _gu_system_h_ #define _gu_system_h_ #define _GNU_SOURCE // program_invocation_name, program_invocation_short_name #include #include // getexecname, getprogname #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /* See: http://lists.gnu.org/archive/html/bug-gnulib/2010-12/txtrjMzutB7Em.txt * for implementation of GU_SYS_PROGRAM_NAME on other platforms */ #if defined(__sun__) # define GU_SYS_PROGRAM_NAME getexecname () #elif defined(__APPLE__) || defined(__FreeBSD__) # define GU_SYS_PROGRAM_NAME getprogname () #elif defined(__linux__) # define GU_SYS_PROGRAM_NAME program_invocation_name #endif #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* _gu_system_h_ */ percona-xtradb-cluster-galera/galerautils/src/gu_throw.hpp0000644000000000000000000000517112247075736024330 0ustar rootroot00000000000000/* * Copyright (C) 2009-2013 Codership Oy * * $Id: gu_throw.hpp 3348 2013-11-02 10:56:57Z alex $ */ /*! * @file Classes to allow throwing more verbose exceptions. Should be only * used from one-line macros below. Concrete classes intended to be final. */ #ifndef __GU_THROW__ #define __GU_THROW__ #include #include #include #include #include "gu_macros.h" #include "gu_exception.hpp" namespace gu { /*! "base" class */ class ThrowBase { protected: const char* const file; const char* const func; int const line; std::ostringstream os; ThrowBase (const char* file_, const char* func_, int line_) : file (file_), func (func_), line (line_), os () {} private: ThrowBase (const ThrowBase&); ThrowBase& operator= (const ThrowBase&); friend class ThrowError; friend class ThrowFatal; }; /* final*/ class ThrowError //: public ThrowBase { public: ThrowError (const char* file_, const char* func_, int line_, int err_) : base (file_, func_, line_), err (err_) {} ~ThrowError() GU_NORETURN { base.os << ": " << err << " (" << ::strerror(err) << ')'; Exception e(base.os.str(), err); e.trace (base.file, base.func, base.line); // cppcheck-suppress exceptThrowInDestructor throw e; } std::ostringstream& msg () { return base.os; } private: ThrowBase base; int const err; }; /* final*/ class ThrowFatal { public: ThrowFatal (const char* file, const char* func, int line) : base (file, func, line) {} ~ThrowFatal () GU_NORETURN { base.os << " (FATAL)"; Exception e(base.os.str(), ENOTRECOVERABLE); e.trace (base.file, base.func, base.line); // cppcheck-suppress exceptThrowInDestructor throw e; } std::ostringstream& msg () { return base.os; } private: ThrowBase base; }; } // Usage: gu_throw_xxxxx << msg1 << msg2 << msg3; #define gu_throw_error(err_) \ gu::ThrowError(__FILE__, __FUNCTION__, __LINE__, err_).msg() #define gu_throw_fatal \ gu::ThrowFatal(__FILE__, __FUNCTION__, __LINE__).msg() #endif // __GU_THROW__ percona-xtradb-cluster-galera/galerautils/src/gu_time.c0000644000000000000000000001442212247075736023555 0ustar rootroot00000000000000// Copyright (C) 2013 Codership Oy /** * @file time manipulation functions/macros * * $Id: $ */ #if defined(__APPLE__) #include #include // struct timespec #include // gettimeofday #include // clock_get_time #include // host_get_clock_service #include // mach_absolute_time, mach_timebase_info #include #include "gu_time.h" #define NSEC_PER_SEC 1000000000 #define NSEC_PER_USEC 1000 # if defined(__LP64__) // OS X comm page time offsets // see http://www.opensource.apple.com/source/xnu/xnu-2050.22.13/osfmk/i386/cpu_capabilities.h #define nt_tsc_base "0x50" #define nt_scale "0x58" #define nt_shift "0x5c" #define nt_ns_base "0x60" #define nt_generation "0x68" #define gtod_generation "0x6c" #define gtod_ns_base "0x70" #define gtod_sec_base "0x78" static inline int64_t nanotime (void) { int64_t ntime; __asm volatile ( "mov $0x7fffffe00000, %%rbx;" /* comm page base */ "0:" /* Loop trying to take a consistent snapshot of the time parameters. */ "movl "gtod_generation"(%%rbx), %%r8d;" "testl %%r8d, %%r8d;" "jz 1f;" "movl "nt_generation"(%%rbx), %%r9d;" "testl %%r9d, %%r9d;" "jz 0b;" "rdtsc;" "movq "nt_tsc_base"(%%rbx), %%r10;" "movl "nt_scale"(%%rbx), %%r11d;" "movq "nt_ns_base"(%%rbx), %%r12;" "cmpl "nt_generation"(%%rbx), %%r9d;" "jne 0b;" "movq "gtod_ns_base"(%%rbx), %%r13;" "movq "gtod_sec_base"(%%rbx), %%r14;" "cmpl "gtod_generation"(%%rbx), %%r8d;" "jne 0b;" /* Gathered all the data we need. Compute time. */ /* ((tsc - nt_tsc_base) * nt_scale) >> 32 + nt_ns_base - gtod_ns_base + gtod_sec_base*1e9 */ /* The multiply and shift extracts the top 64 bits of the 96-bit product. */ "shlq $32, %%rdx;" "addq %%rdx, %%rax;" "subq %%r10, %%rax;" "mulq %%r11;" "shrdq $32, %%rdx, %%rax;" "addq %%r12, %%rax;" "subq %%r13, %%rax;" "imulq $1000000000, %%r14;" "addq %%r14, %%rax;" "jmp 2f;" "1:" /* Fall back to system call (usually first call in this thread). */ "movq %%rsp, %%rdi;" /* rdi must be non-nil, unused */ "movq $0, %%rsi;" "movl $(0x2000000+116), %%eax;" /* SYS_gettimeofday */ "syscall; /* may destroy rcx and r11 */" /* sec is in rax, usec in rdx */ /* return nsec in rax */ "imulq $1000000000, %%rax;" "imulq $1000, %%rdx;" "addq %%rdx, %%rax;" "2:" : "=a"(ntime) : /* no input parameters */ : "%rbx", "%rcx", "%rdx", "%rsi", "%rdi", "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14" ); return ntime; } static inline int64_t nanouptime (void) { int64_t ntime; __asm volatile ( "movabs $0x7fffffe00000, %%rbx;" /* comm page base */ "0:" /* Loop trying to take a consistent snapshot of the time parameters. */ "movl "nt_generation"(%%rbx), %%r9d;" "testl %%r9d, %%r9d;" "jz 0b;" "rdtsc;" "movq "nt_tsc_base"(%%rbx), %%r10;" "movl "nt_scale"(%%rbx), %%r11d;" "movq "nt_ns_base"(%%rbx), %%r12;" "cmpl "nt_generation"(%%rbx), %%r9d;" "jne 0b;" /* Gathered all the data we need. Compute time. */ /* ((tsc - nt_tsc_base) * nt_scale) >> 32 + nt_ns_base */ /* The multiply and shift extracts the top 64 bits of the 96-bit product. */ "shlq $32, %%rdx;" "addq %%rdx, %%rax;" "subq %%r10, %%rax;" "mulq %%r11;" "shrdq $32, %%rdx, %%rax;" "addq %%r12, %%rax;" : "=a"(ntime) : /* no input parameters */ : "%rbx", "%rcx", "%rdx", "%rsi", "%rdi", "%r9", "%r10", "%r11", "%r12" ); return ntime; } int clock_gettime (clockid_t clk_id, struct timespec * tp) { int64_t abstime = 0; if (tp == NULL) { return EFAULT; } switch (clk_id) { case CLOCK_REALTIME: abstime = nanotime (); break; case CLOCK_MONOTONIC: abstime = nanouptime (); break; default: errno = EINVAL; return -1; } tp->tv_sec = abstime / (uint64_t)NSEC_PER_SEC; tp->tv_nsec = (uint32_t)(abstime % (uint64_t)NSEC_PER_SEC); return 0; } #else /* !__LP64__ */ static struct mach_timebase_info g_mti; int clock_gettime (clockid_t clk_id, struct timespec * tp) { int64_t abstime = 0; mach_timebase_info_data_t mti; /* {uint32_t numer, uint32_t denom} */ if (tp == NULL) { return EFAULT; } switch (clk_id) { case CLOCK_REALTIME: struct timeval tv; if (gettimeofday (&tv, NULL) != 0) { return -1; } tp->tv_sec = tv.tv_sec; tp->tv_nsec = tv.tv_usec * NSEC_PER_USEC; return 0; case CLOCK_MONOTONIC: abstime = mach_absolute_time (); break; default: errno = EINVAL; return -1; } if (g_mti.denom == 0) { struct mach_timebase_info mti; mach_timebase_info (&mti); g_mti.numer = mti.numer; OSMemoryBarrier (); g_mti.denom = mti.denom; } nanos = (uint64_t)(abstime * (((double)g_mti.numer) / ((double)g_mti.denom))); tp->tv_sec = nanos / (uint64_t)NSEC_PER_SEC; tp->tv_nsec = (uint32_t)(nanos % (uint64_t)NSEC_PER_SEC); return 0; } #endif /* !__LP64__ */ #else /* !__APPLE__ */ #ifdef __GNUC__ // error: ISO C forbids an empty translation unit int dummy_var_gu_time; #endif #endif /* __APPLE__ */ percona-xtradb-cluster-galera/galerautils/src/gu_time.h0000644000000000000000000000605012247075736023560 0ustar rootroot00000000000000// Copyright (C) 2008 Codership Oy /** * @file time manipulation functions/macros * * $Id: gu_time.h 3207 2013-08-21 09:08:51Z vlad $ */ #ifndef _gu_time_h_ #define _gu_time_h_ #include #include #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /** Returns seconds */ static inline double gu_timeval_diff (struct timeval* left, struct timeval* right) { register long long diff = left->tv_sec; diff = ((diff - right->tv_sec)*1000000LL) + left->tv_usec - right->tv_usec; return (((double)diff) * 1.0e-06); } static inline void gu_timeval_add (struct timeval* t, double s) { double ret = (double)t->tv_sec + ((double)t->tv_usec) * 1.0e-06 + s; t->tv_sec = (long)ret; t->tv_usec = (long)((ret - (double)t->tv_sec) * 1.0e+06); } static const double SEC_PER_CLOCK = ((double)1.0)/CLOCKS_PER_SEC; /** Returns seconds */ static inline double gu_clock_diff (clock_t left, clock_t right) { return ((double)(left - right)) * SEC_PER_CLOCK; } #include "gu_limits.h" // for GU_LONG_LONG_MAX #include /** * New time interface * * All funcitons return nanoseconds. */ /* Maximum date representable by long long and compatible with timespec */ #define GU_TIME_ETERNITY 9223372035999999999LL #if defined(__APPLE__) /* synced with linux/time.h */ # define CLOCK_REALTIME 0 # define CLOCK_MONOTONIC 1 typedef int clockid_t; int clock_gettime (clockid_t clk_id, struct timespec * tp); #endif /* __APPLE__ */ static inline long long gu_time_getres() { #if _POSIX_TIMERS > 0 struct timespec tmp; clock_getres (CLOCK_REALTIME, &tmp); return ((tmp.tv_sec * 1000000000LL) + tmp.tv_nsec); #else return 1000LL; // assumed resolution of gettimeofday() in nanoseconds #endif } static inline long long gu_time_calendar() { #if _POSIX_TIMERS > 0 || defined(__APPLE__) struct timespec tmp; clock_gettime (CLOCK_REALTIME, &tmp); return ((tmp.tv_sec * 1000000000LL) + tmp.tv_nsec); #else struct timeval tmp; gettimeofday (&tmp, NULL); return ((tmp.tv_sec * 1000000000LL) + (tmp.tv_usec * 1000LL)); #endif } static inline long long gu_time_monotonic() { #if defined(_POSIX_MONOTONIC_CLOCK) || defined(__APPLE__) struct timespec tmp; clock_gettime (CLOCK_MONOTONIC, &tmp); return ((tmp.tv_sec * 1000000000LL) + tmp.tv_nsec); #else struct timeval tmp; gettimeofday (&tmp, NULL); return ((tmp.tv_sec * 1000000000LL) + (tmp.tv_usec * 1000LL)); #endif } #ifdef CLOCK_PROCESS_CPUTIME_ID static inline long long gu_time_process_cputime() { #if _POSIX_TIMERS > 0 struct timespec tmp; clock_gettime (CLOCK_PROCESS_CPUTIME_ID, &tmp); return ((tmp.tv_sec * 1000000000LL) + tmp.tv_nsec); #else return -1; #endif } #endif /* CLOCK_PROCESS_CPUTIME_ID */ static inline long long gu_time_thread_cputime() { #if _POSIX_TIMERS > 0 struct timespec tmp; clock_gettime (CLOCK_THREAD_CPUTIME_ID, &tmp); return ((tmp.tv_sec * 1000000000LL) + tmp.tv_nsec); #else return -1; #endif } #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* _gu_time_h_ */ percona-xtradb-cluster-galera/galerautils/src/gu_to.c0000644000000000000000000002656412247075736023253 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gu_to.c 3336 2013-10-28 07:41:56Z teemu $ */ /*! \file \brief Total order access "class" implementation. * Although gcs_repl() and gcs_recv() calls return sequence * numbers in total order, there are concurrency issues between * application threads and they can grab critical section * mutex out of order. Wherever total order access to critical * section is required, these functions can be used to do this. */ #include #include #include #include // abort() #include "gu_log.h" #include "gu_assert.h" #include "gu_mem.h" #include "gu_mutex.h" #include "gu_to.h" #define TO_USE_SIGNAL 1 typedef enum { HOLDER = 0, //!< current TO holder WAIT, //!< actively waiting in the queue CANCELED, //!< Waiter has canceled its to request INTERRUPTED,//!< marked to be interrupted RELEASED, //!< has been released, free entry now } waiter_state_t; typedef struct { #ifdef TO_USE_SIGNAL gu_cond_t cond; #else pthread_mutex_t mtx; // have to use native pthread for double locking #endif waiter_state_t state; } to_waiter_t; struct gu_to { volatile gu_seqno_t seqno; size_t used; /* number of active waiters */ ssize_t qlen; size_t qmask; to_waiter_t* queue; gu_mutex_t lock; }; /** Returns pointer to the waiter with the given seqno */ static inline to_waiter_t* to_get_waiter (gu_to_t* to, gu_seqno_t seqno) { // Check for queue overflow. Tell application that it should wait. if (seqno >= to->seqno + to->qlen) { return NULL; } return (to->queue + (seqno & to->qmask)); } gu_to_t *gu_to_create (int len, gu_seqno_t seqno) { gu_to_t *ret; assert (seqno >= 0); if (len <= 0) { gu_error ("Negative length parameter: %d", len); return NULL; } ret = GU_CALLOC (1, gu_to_t); if (ret) { /* Make queue length a power of 2 */ ret->qlen = 1; while (ret->qlen < len) { // unsigned, can be bigger than any integer ret->qlen = ret->qlen << 1; } ret->qmask = ret->qlen - 1; ret->seqno = seqno; ret->queue = GU_CALLOC (ret->qlen, to_waiter_t); if (ret->queue) { ssize_t i; for (i = 0; i < ret->qlen; i++) { to_waiter_t *w = ret->queue + i; #ifdef TO_USE_SIGNAL gu_cond_init (&w->cond, NULL); #else pthread_mutex_init (&w->mtx, NULL); #endif w->state = RELEASED; } gu_mutex_init (&ret->lock, NULL); return ret; } gu_free (ret); } return NULL; } long gu_to_destroy (gu_to_t** to) { gu_to_t *t = *to; long ret; ssize_t i; gu_mutex_lock (&t->lock); if (t->used) { gu_mutex_unlock (&t->lock); return -EBUSY; } for (i = 0; i < t->qlen; i++) { to_waiter_t *w = t->queue + i; #ifdef TO_USE_SIGNAL if (gu_cond_destroy (&w->cond)) { // @todo: what if someone is waiting? gu_warn ("Failed to destroy condition %d. Should not happen", i); } #else if (pthread_mutex_destroy (&w->mtx)) { // @todo: what if someone is waiting? gu_warn ("Failed to destroy mutex %d. Should not happen", i); } #endif } t->qlen = 0; gu_mutex_unlock (&t->lock); /* What else can be done here? */ ret = gu_mutex_destroy (&t->lock); if (ret) return -ret; // application can retry gu_free (t->queue); gu_free (t); *to = NULL; return 0; } long gu_to_grab (gu_to_t* to, gu_seqno_t seqno) { long err; to_waiter_t *w; assert (seqno >= 0); if ((err = gu_mutex_lock(&to->lock))) { gu_fatal("Mutex lock failed (%d): %s", err, strerror(err)); abort(); } if (seqno < to->seqno) { gu_mutex_unlock(&to->lock); return -ECANCELED; } if ((w = to_get_waiter (to, seqno)) == NULL) { gu_mutex_unlock(&to->lock); return -EAGAIN; } /* we have a valid waiter now */ switch (w->state) { case INTERRUPTED: w->state = RELEASED; err = -EINTR; break; case CANCELED: err = -ECANCELED; break; case RELEASED: if (seqno == to->seqno) { w->state = HOLDER; } else if (seqno < to->seqno) { gu_error("Trying to grab outdated seqno"); err = -ECANCELED; } else { /* seqno > to->seqno, wait for my turn */ w->state = WAIT; to->used++; #ifdef TO_USE_SIGNAL gu_cond_wait(&w->cond, &to->lock); #else pthread_mutex_lock (&w->mtx); pthread_mutex_unlock (&to->lock); pthread_mutex_lock (&w->mtx); // wait for unlock by other thread pthread_mutex_lock (&to->lock); pthread_mutex_unlock (&w->mtx); #endif to->used--; switch (w->state) { case WAIT:// should be most probable assert (seqno == to->seqno); w->state = HOLDER; break; case INTERRUPTED: w->state = RELEASED; err = -EINTR; break; case CANCELED: err = -ECANCELED; break; case RELEASED: /* this waiter has been cancelled */ assert(seqno < to->seqno); err = -ECANCELED; break; default: gu_fatal("Invalid cond wait exit state %d, seqno %llu(%llu)", w->state, seqno, to->seqno); abort(); } } break; default: gu_fatal("TO queue over wrap"); abort(); } gu_mutex_unlock(&to->lock); return err; } static inline long to_wake_waiter (to_waiter_t* w) { long err = 0; if (w->state == WAIT) { #ifdef TO_USE_SIGNAL err = gu_cond_signal (&w->cond); #else err = pthread_mutex_unlock (&w->mtx); #endif if (err) { gu_fatal ("gu_cond_signal failed: %d", err); } } return err; } static inline void to_release_and_wake_next (gu_to_t* to, to_waiter_t* w) { w->state = RELEASED; /* * Iterate over CANCELED waiters and set states as RELEASED * We look for waiter in the head of queue, which guarantees that * to_get_waiter() will always return a valid waiter pointer */ for (to->seqno++; (w = to_get_waiter(to, to->seqno)) && w && w->state == CANCELED; to->seqno++) { w->state = RELEASED; } to_wake_waiter (w); } long gu_to_release (gu_to_t *to, gu_seqno_t seqno) { long err; to_waiter_t *w; assert (seqno >= 0); if ((err = gu_mutex_lock(&to->lock))) { gu_fatal("Mutex lock failed (%d): %s", err, strerror(err)); abort(); } if ((w = to_get_waiter (to, seqno)) == NULL) { gu_mutex_unlock(&to->lock); return -EAGAIN; } /* we have a valid waiter now */ if (seqno == to->seqno) { to_release_and_wake_next (to, w); } else if (seqno > to->seqno) { if (w->state != CANCELED) { gu_fatal("Illegal state in premature release: %d", w->state); abort(); } /* Leave state CANCELED so that real releaser can iterate */ } else { /* */ if (w->state != RELEASED) { gu_fatal("Outdated seqno and state not RELEASED: %d", w->state); abort(); } } gu_mutex_unlock(&to->lock); return err; } gu_seqno_t gu_to_seqno (gu_to_t* to) { return to->seqno - 1; } long gu_to_cancel (gu_to_t *to, gu_seqno_t seqno) { long err; to_waiter_t *w; assert (seqno >= 0); if ((err = gu_mutex_lock (&to->lock))) { gu_fatal("Mutex lock failed (%d): %s", err, strerror(err)); abort(); } // Check for queue overflow. This is totally unrecoverable. Abort. if ((w = to_get_waiter (to, seqno)) == NULL) { gu_mutex_unlock(&to->lock); abort(); } /* we have a valid waiter now */ if ((seqno > to->seqno) || (seqno == to->seqno && w->state != HOLDER)) { err = to_wake_waiter (w); w->state = CANCELED; } else if (seqno == to->seqno && w->state == HOLDER) { gu_warn("tried to cancel current TO holder, state %d seqno %llu", w->state, seqno); err = -ECANCELED; } else { gu_warn("trying to cancel used seqno: state %d cancel seqno = %llu, " "TO seqno = %llu", w->state, seqno, to->seqno); err = -ECANCELED; } gu_mutex_unlock (&to->lock); return err; } long gu_to_self_cancel(gu_to_t *to, gu_seqno_t seqno) { long err = 0; to_waiter_t *w; assert (seqno >= 0); if ((err = gu_mutex_lock (&to->lock))) { gu_fatal("Mutex lock failed (%d): %s", err, strerror(err)); abort(); } if ((w = to_get_waiter (to, seqno)) == NULL) { gu_mutex_unlock(&to->lock); return -EAGAIN; } /* we have a valid waiter now */ if (seqno > to->seqno) { // most probable case w->state = CANCELED; } else if (seqno == to->seqno) { // have to wake the next waiter as if we grabbed and now releasing TO to_release_and_wake_next (to, w); } else { // (seqno < to->seqno) // This waiter must have been canceled or even released by preceding // waiter. Do nothing. } gu_mutex_unlock(&to->lock); return err; } long gu_to_interrupt (gu_to_t *to, gu_seqno_t seqno) { long rcode = 0; long err; to_waiter_t *w; assert (seqno >= 0); if ((err = gu_mutex_lock (&to->lock))) { gu_fatal("Mutex lock failed (%d): %s", err, strerror(err)); abort(); } if (seqno >= to->seqno) { if ((w = to_get_waiter (to, seqno)) == NULL) { gu_mutex_unlock(&to->lock); return -EAGAIN; } /* we have a valid waiter now */ switch (w->state) { case HOLDER: gu_debug ("trying to interrupt in use seqno: seqno = %llu, " "TO seqno = %llu", seqno, to->seqno); /* gu_mutex_unlock (&to->lock); */ rcode = -ERANGE; break; case CANCELED: gu_debug ("trying to interrupt canceled seqno: seqno = %llu, " "TO seqno = %llu", seqno, to->seqno); /* gu_mutex_unlock (&to->lock); */ rcode = -ERANGE; break; case WAIT: gu_debug ("signaling to interrupt wait seqno: seqno = %llu, " "TO seqno = %llu", seqno, to->seqno); rcode = to_wake_waiter (w); case RELEASED: w->state = INTERRUPTED; break; case INTERRUPTED: gu_debug ("TO waiter interrupt already seqno: seqno = %llu, " "TO seqno = %llu", seqno, to->seqno); break; } } else { gu_debug ("trying to interrupt used seqno: cancel seqno = %llu, " "TO seqno = %llu", seqno, to->seqno); /* gu_mutex_unlock (&to->lock); */ rcode = -ERANGE; } gu_mutex_unlock (&to->lock); return rcode; } percona-xtradb-cluster-galera/galerautils/src/gu_to.h0000644000000000000000000001045612247075736023251 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gu_to.h 3336 2013-10-28 07:41:56Z teemu $ */ /*! * @file gu_to.h Public TO monitor API */ #ifndef _gu_to_h_ #define _gu_to_h_ #ifdef __cplusplus extern "C" { #endif #include #include #include /*! @typedef @brief Sequence number type. */ typedef int64_t gu_seqno_t; /*! Total Order object */ typedef struct gu_to gu_to_t; /*! @brief Creates TO object. * TO object can be used to serialize access to application * critical section using sequence number. * * @param len A length of the waiting queue. Should be no less than the * possible maximum number of threads competing for the resource, * but should not be too high either. Perhaps 1024 is good enough * for most applications. * @param seqno A starting sequence number * (the first to be used by gu_to_grab()). * @return Pointer to TO object or NULL in case of error. */ extern gu_to_t* gu_to_create (int len, gu_seqno_t seqno); /*! @brief Destroys TO object. * * @param to A pointer to TO object to be destroyed * @return 0 in case of success, negative code in case of error. * In particular -EBUSY means the object is used by other threads. */ extern long gu_to_destroy (gu_to_t** to); /*! @brief Grabs TO resource in the specified order. * On successful return the mutex associated with specified TO is locked. * Must be released gu_to_release(). @see gu_to_release * * @param to TO resource to be acquired. * @param seqno The order at which TO resouce should be aquired. For any N * gu_to_grab (to, N) will return exactly after * gu_to_release (to, N-1). * @return 0 in case of success, negative code in case of error. * -EAGAIN means that there are too many threads waiting for TO * already. It is safe to try again later. * -ECANCEL if waiter was canceled, seqno is skipped in TO * -EINTR if wait was interrupted, must retry grabbing later */ extern long gu_to_grab (gu_to_t* to, gu_seqno_t seqno); /*! @brief Releases TO specified resource. * On succesful return unlocks the mutex associated with TO. * TO must be previously acquired with gu_to_grab(). @see gu_to_grab * * @param to TO resource that was previously acquired with gu_to_grab(). * @param seqno The same number with which gu_to_grab() was called. * @return 0 in case of success, negative code in case of error. Any error * here is an application error - attempt to release TO resource * out of order (not paired with gu_to_grab()). */ extern long gu_to_release (gu_to_t* to, gu_seqno_t seqno); /*! @brief The last sequence number that had been used to access TO object. * Note that since no locks are held, it is a conservative estimation. * It is guaranteed however that returned seqno is no longer in use. * * @param to A pointer to TO object. * @return GCS sequence number. Since GCS TO sequence starts with 1, this * sequence can start with 0. */ extern gu_seqno_t gu_to_seqno (gu_to_t* to); /*! @brief cancels a TO monitor waiter making it return immediately * It is assumed that the caller is currenly holding the TO. * The to-be-cancelled waiter can be some later transaction but also * some earlier transaction. Tests have shown that the latter case * can also happen. * * @param to A pointer to TO object. * @param seqno Seqno of the waiter object to be cancelled * @return 0 for success and -ERANGE, if trying to cancel an earlier * transaction */ extern long gu_to_cancel (gu_to_t *to, gu_seqno_t seqno); /*! * Self cancel to without attempting to enter critical secion */ extern long gu_to_self_cancel(gu_to_t *to, gu_seqno_t seqno); /*! @brief interrupts from TO monitor waiting state. * Seqno remains valid in the queue and later seqnos still need to * wait for this seqno to be released. * * The caller can (and must) later try gu_to_grab() again or cancel * the seqno with gu_to_self_cancel(). * * @param to A pointer to TO object. * @param seqno Seqno of the waiter object to be interrupted * @return 0 for success and -ERANGE, if trying to interrupt an already * used transaction */ extern long gu_to_interrupt (gu_to_t *to, gu_seqno_t seqno); #ifdef __cplusplus } #endif #endif // _gu_to_h_ percona-xtradb-cluster-galera/galerautils/src/gu_types.h0000644000000000000000000000055112247075736023766 0ustar rootroot00000000000000// Copyright (C) 2012 Codership Oy /** * @file Location of some "standard" types definitions * * $Id: gu_types.h 3336 2013-10-28 07:41:56Z teemu $ */ #ifndef _gu_types_h_ #define _gu_types_h_ #include /* intXX_t and friends */ #include /* bool */ #include /* ssize_t */ #endif /* _gu_types_h_ */ percona-xtradb-cluster-galera/galerautils/src/gu_unordered.hpp0000644000000000000000000001325112247075736025152 0ustar rootroot00000000000000// // Copyright (C) 2010 Codership Oy // //! // @file gu_unordered.hpp unordered_[multi]map definition // // We still have environments where neither boost or std unordered // stuff is available. Wrapper classes are provided for alternate // implementations with standard semantics. // // For usage see either boost or tr1 specifications for unordered_[multi]map // #ifndef GU_UNORDERED_HPP #define GU_UNORDERED_HPP #if defined(HAVE_BOOST_UNORDERED_MAP_HPP) #include #include #elif defined(HAVE_TR1_UNORDERED_MAP) #include #include #else #error "no unordered map available" #endif #include "gu_throw.hpp" namespace gu { template class Hash { public: #if defined(HAVE_BOOST_UNORDERED_MAP_HPP) typedef boost::hash Type; #elif defined(HAVE_TR1_UNORDERED_MAP) typedef std::tr1::hash Type; #endif size_t operator()(const K& k) const { return Type()(k); } }; template size_t HashValue(const K& key) { return Hash()(key); } template , class P = std::equal_to, class A = std::allocator > class UnorderedSet { #if defined(HAVE_BOOST_UNORDERED_MAP_HPP) typedef boost::unordered_set type; #elif defined(HAVE_TR1_UNORDERED_MAP) typedef std::tr1::unordered_set type; #endif type impl_; public: typedef typename type::value_type value_type; typedef typename type::iterator iterator; typedef typename type::const_iterator const_iterator; UnorderedSet() : impl_() { } iterator begin() { return impl_.begin(); } const_iterator begin() const { return impl_.begin(); } iterator end() { return impl_.end(); } const_iterator end() const { return impl_.end(); } std::pair insert(const value_type& k) { return impl_.insert(k); } iterator insert_unique(const value_type& k) { std::pair ret(insert(k)); if (ret.second == false) gu_throw_fatal << "insert unique failed"; return ret.first; } iterator find(const K& key) { return impl_.find(key); } const_iterator find(const K& key) const { return impl_.find(key); } iterator erase(iterator i) { return impl_.erase(i); } size_t size() const { return impl_.size(); } bool empty() const { return impl_.empty(); } void clear() { impl_.clear(); } void rehash(size_t n) { impl_.rehash(n); } }; template , class P = std::equal_to, class A = std::allocator > > class UnorderedMap { #if defined(HAVE_BOOST_UNORDERED_MAP_HPP) typedef boost::unordered_map type; #elif defined(HAVE_TR1_UNORDERED_MAP) typedef std::tr1::unordered_map type; #endif type impl_; public: typedef typename type::value_type value_type; typedef typename type::iterator iterator; typedef typename type::const_iterator const_iterator; UnorderedMap() : impl_() { } iterator begin() { return impl_.begin(); } const_iterator begin() const { return impl_.begin(); } iterator end() { return impl_.end(); } const_iterator end() const { return impl_.end(); } std::pair insert(const std::pair& kv) { return impl_.insert(kv); } iterator insert_unique(const std::pair& kv) { std::pair ret(insert(kv)); if (ret.second == false) gu_throw_fatal << "insert unique failed"; return ret.first; } iterator find(const K& key) { return impl_.find(key); } const_iterator find(const K& key) const { return impl_.find(key); } iterator erase(iterator i) { return impl_.erase(i); } size_t size() const { return impl_.size(); } bool empty() const { return impl_.empty(); } void clear() { impl_.clear(); } void rehash(size_t n) { impl_.rehash(n); } }; template > class UnorderedMultimap { #if defined(HAVE_BOOST_UNORDERED_MAP_HPP) typedef boost::unordered_multimap type; #elif defined(HAVE_TR1_UNORDERED_MAP) typedef std::tr1::unordered_multimap type; #endif type impl_; public: typedef typename type::value_type value_type; typedef typename type::iterator iterator; typedef typename type::const_iterator const_iterator; UnorderedMultimap() : impl_() { } void clear() { impl_.clear(); } iterator begin() { return impl_.begin(); } const_iterator begin() const { return impl_.begin(); } iterator end() { return impl_.end(); } const_iterator end() const { return impl_.end(); } iterator insert(const std::pair& kv) { return impl_.insert(kv); } iterator find(const K& key) { return impl_.find(key); } const_iterator find(const K& key) const { return impl_.find(key); } std::pair equal_range(const K& key) { return impl_.equal_range(key); } std::pair equal_range(const K& key) const { return impl_.equal_range(key); } void erase(iterator i) { impl_.erase(i); } size_t size() const { return impl_.size(); } bool empty() const { return impl_.empty(); } }; } #endif // GU_UNORDERED_HPP percona-xtradb-cluster-galera/galerautils/src/gu_uri.cpp0000644000000000000000000001732312247075736023761 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy * * $Id$ */ /*! @todo: scheme and host parts should be normalized to lower case except for * %-encodings, which should go upper-case */ #include #include #include #include "gu_assert.h" #include "gu_throw.hpp" #include "gu_logger.hpp" #include "gu_string.hpp" #include "gu_uri.hpp" using std::string; using std::vector; using std::multimap; static void parse_authority (const string& auth, gu::RegEx::Match& user, gu::RegEx::Match& host, gu::RegEx::Match& port) { size_t pos1, pos2; pos1 = auth.find_first_of ('@'); if (pos1 != string::npos) { user = gu::RegEx::Match (auth.substr(0, pos1)); pos1 += 1; // pos1 now points past the first occurence of @, // which may be past the end of the string. } else { pos1 = 0; } pos2 = auth.find_last_of (':'); if (pos2 != string::npos && pos2 >= pos1) { host = gu::RegEx::Match (auth.substr (pos1, pos2 - pos1)); // according to RFC 3986 empty port (nothing after :) should be treated // as unspecified, so make sure that it is not 0-length. if ((pos2 + 1) < auth.length()) { port = gu::RegEx::Match (auth.substr (pos2 + 1)); if ((port.str().find_first_not_of ("0123456789") != string::npos) || // @todo: possible port range is not determined in RFC 3986 (65535 < gu::from_string (port.str()))) { log_debug << "\n\tauth: '" << auth << "'" << "\n\thost: '" << host.str() << "'" << "\n\tport: '" << port.str() << "'" << "\n\tpos1: " << pos1 << ", pos2: " << pos2; gu_throw_error (EINVAL) << "Can't parse port number from '" << port.str() << "'"; } } } else { host = gu::RegEx::Match (auth.substr (pos1)); } } static gu::URIQueryList extract_query_list(const string& s, const string& query) { gu::URIQueryList ret; // scan all key=value pairs vector qlist = gu::strsplit(query, '&'); for (vector::const_iterator i = qlist.begin(); i != qlist.end(); ++i) { vector kvlist = gu::strsplit(*i, '='); if (kvlist.size() != 2) { gu_throw_error (EINVAL) << "Invalid URI query part: '" << *i << "'"; } ret.insert(make_pair(kvlist[0], kvlist[1])); } return ret; } gu::URI::URI(const string& uri_str, bool const strict) : modified_ (true), // recompose to normalize on the first call to_string() str_ (uri_str), scheme_ (), authority_ (), path_ (), fragment_ (), query_list_() { parse(uri_str, strict); } /*! regexp suggested by RFC 3986 to parse URI into 5 canonical parts */ const char* const gu::URI::uri_regex_ = "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?"; /* 12 3 4 5 6 7 8 9 */ /*! positions of URI components as matched by the above pattern */ enum { SCHEME = 2, AUTHORITY = 4, PATH = 5, QUERY = 7, FRAGMENT = 9, NUM_PARTS }; gu::RegEx const gu::URI::regex_(uri_regex_); static string const UNSET_SCHEME("unset://"); void gu::URI::parse (const string& uri_str, bool const strict) { log_debug << "URI: " << uri_str; vector parts; if (!strict && uri_str.find("://") == std::string::npos) { string tmp = UNSET_SCHEME + uri_str; parts = regex_.match (tmp, NUM_PARTS); } else { parts = regex_.match (uri_str, NUM_PARTS); scheme_ = parts[SCHEME]; //set scheme only if it was explicitly provided } if (strict && (!scheme_.is_set() || !scheme_.str().length())) { gu_throw_error (EINVAL) << "URI '" << uri_str << "' has empty scheme"; } try { std::vector auth_list( strsplit(parts[AUTHORITY].str(), ',')); for (size_t i(0); i < auth_list.size(); ++i) { Authority auth; parse_authority (auth_list[i], auth.user_, auth.host_, auth.port_); authority_.push_back(auth); } } catch (NotSet&) { authority_.push_back(Authority()); } path_ = parts[PATH]; if (!parts[AUTHORITY].is_set() && !path_.is_set()) { gu_throw_error (EINVAL) << "URI '" << uri_str << "' has no hierarchical part"; } try { query_list_ = extract_query_list(str_, parts[QUERY].str()); } catch (NotSet&) {} fragment_ = parts[FRAGMENT]; #if 0 try { log_debug << "Base URI: " << scheme.str() << "://" << get_authority(); } catch (NotSet&) {} #endif } std::string gu::URI::get_authority(const gu::URI::Authority& authority) const { const RegEx::Match& user(authority.user_); const RegEx::Match& host(authority.host_); const RegEx::Match& port(authority.port_); if (!user.is_set() && !host.is_set()) throw NotSet(); size_t auth_len = 0; if (user.is_set()) auth_len += user.str().length() + 1; if (host.is_set()) { auth_len += host.str().length(); if (port.is_set()) auth_len += port.str().length() + 1; } string auth; auth.reserve (auth_len); if (user.is_set()) { auth += user.str(); auth += '@'; } if (host.is_set()) { auth += host.str(); if (port.is_set()) { auth += ':'; auth += port.str(); } } return auth; } string gu::URI::get_authority() const { if (authority_.empty()) return ""; return get_authority(authority_.front()); } void gu::URI::recompose() const { size_t l = str_.length(); str_.clear (); str_.reserve (l); // resulting string length will be close to this if (scheme_.is_set()) { str_ += scheme_.str(); str_ += ':'; } str_ += "//"; for (AuthorityList::const_iterator i(authority_.begin()); i != authority_.end(); ++i) { AuthorityList::const_iterator i_next(i); ++i_next; try { string auth = get_authority(*i); str_ += auth; } catch (NotSet&) {} if (i_next != authority_.end()) str_ += ","; } if (path_.is_set()) str_ += path_.str(); if (query_list_.size() > 0) { str_ += '?'; } URIQueryList::const_iterator i = query_list_.begin(); while (i != query_list_.end()) { str_ += i->first + '=' + i->second; URIQueryList::const_iterator i_next = i; ++i_next; if (i_next != query_list_.end()) { str_ += '&'; } i = i_next; } if (fragment_.is_set()) { str_ += '#'; str_ += fragment_.str(); } } void gu::URI::set_query_param(const string& key, const string& val, bool override) { if (override == false) { query_list_.insert(make_pair(key, val)); } else { URIQueryList::iterator i(query_list_.find(key)); if (i == query_list_.end()) { query_list_.insert(make_pair(key, val)); } else { i->second = val; } } modified_ = true; } const std::string& gu::URI::get_option (const std::string& name) const { gu::URIQueryList::const_iterator i = query_list_.find(name); if (i == query_list_.end()) throw NotFound(); return i->second; } percona-xtradb-cluster-galera/galerautils/src/gu_uri.hpp0000644000000000000000000001525512247075736023770 0ustar rootroot00000000000000/* * Copyright (C) 2009-2012 Codership Oy * * $Id$ */ /*! * @file gu_url.hpp * * @brief Utility to parse URIs * * Follows http://tools.ietf.org/html/rfc3986 * * @author Teemu Ollakka */ #ifndef __GU_URI_HPP__ #define __GU_URI_HPP__ #include #include #include #include "gu_utils.hpp" #include "gu_regex.hpp" namespace gu { /*! * @brief URIQueryList * * std::multimap is used to implement query list in URI. * @todo This should be changed to real class having get_key(), * get_value() methods for iterators and to get rid of std::multimap * dependency in header. */ typedef std::multimap URIQueryList; /*! * @brief Utility class to parse URIs */ class URI { public: /*! * @class Helper class for authority list representation. */ class Authority { public: /*! * @brief Get "user" part of authority * * @return user substring * @throws NotSet */ const std::string& user() const { return user_.str(); } /*! * @brief Get "host" part of authority * * @return host substring * @throws NotSet */ const std::string& host() const { return host_.str(); } /*! * @brief Get "port" part of authority * * @return port * @throws NotSet */ const std::string& port() const { return port_.str(); } private: friend class gu::URI; Authority() : user_(), host_(), port_() { } RegEx::Match user_; RegEx::Match host_; RegEx::Match port_; }; typedef std::vector AuthorityList; /*! * @brief Construct URI from string * * @param strict if true, throw exception when scheme is not found, * else use a default one * @throws std::invalid_argument if URI is not valid * @throws std::logic_error in case of internal error * @throws NotSet */ URI (const std::string&, bool strict = true); /*! * @brief Get URI string * @return URI string */ const std::string& to_string() const { if (modified_) recompose(); return str_; } /*! * @brief Get URI scheme * * @return URI scheme (always set) * @throws NotSet */ const std::string& get_scheme() const { return scheme_.str(); } /*! * @brief Get URI authority component * * @return URI authority substring * @throws NotSet */ std::string get_authority() const; /*! * @brief Get "user" part of the first entry in authority list * * @return User substring * @throws NotSet */ const std::string& get_user() const { if (authority_.empty()) throw NotSet(); return authority_.front().user(); } /*! * @brief Get "host" part of the first entry in authority list * * @return Host substring * @throws NotSet */ const std::string& get_host() const { if (authority_.empty()) throw NotSet(); return authority_.front().host(); } /*! * @brief Get "port" part of the first entry in authority list * * @return Port substring * @throws NotSet */ const std::string& get_port() const { if (authority_.empty()) throw NotSet(); return authority_.front().port(); } /*! * @brief Get authority list * * @return Authority list */ const AuthorityList& get_authority_list() const { return authority_; } /*! * @brief Get URI path * * @return URI path (always set) */ const std::string& get_path() const { return path_.str(); } /*! * @brief Get URI path * * @return URI path * @throws NotSet */ const std::string& get_fragment() const { return fragment_.str(); } /*! * @brief Add query param to URI */ void set_query_param(const std::string&, const std::string&, bool override); void set_option(const std::string& key, const std::string& val) { set_query_param(key, val, true); } void append_option(const std::string& key, const std::string& val) { set_query_param(key, val, false); } /*! * @brief Get URI query list */ const URIQueryList& get_query_list() const { return query_list_; } /*! * @brief return opton by name, * @throws NotFound */ const std::string& get_option(const std::string&) const; const std::string& get_option(const std::string& opt, const std::string& def) const { try { return get_option(opt); } catch (NotFound& ) { return def ; } } private: bool modified_; mutable std::string str_; /*! URI string */ RegEx::Match scheme_; /*! URI scheme part */ AuthorityList authority_; RegEx::Match path_; /*! URI path part */ RegEx::Match fragment_; /*! URI fragment part */ URIQueryList query_list_; /*! URI query list */ /*! * @brief Parse URI from str */ void parse (const std::string& s, bool strict); /*! * @brief Recompose URI in str */ void recompose() const; /*! @throws NotSet */ std::string get_authority(const Authority&) const; static const char* const uri_regex_; /*! regexp string to parse URI */ static RegEx const regex_; /*! URI regexp parser */ }; inline std::ostream& operator<<(std::ostream& os, const URI& uri) { os << uri.to_string(); return os; } } #endif /* __GU_URI_HPP__ */ percona-xtradb-cluster-galera/galerautils/src/gu_utils++.cpp0000644000000000000000000000177312247075736024452 0ustar rootroot00000000000000// Copyright (C) 2009-2011 Codership Oy /** * @file General-purpose functions and templates * * $Id$ */ #include "gu_utils.hpp" #include namespace gu { bool _to_bool (const std::string& s) { std::istringstream iss(s); bool ret; if ((iss >> ret).fail()) { /* if 1|0 didn't work, try true|false */ iss.clear(); iss.seekg(0); if ((iss >> std::boolalpha >> ret).fail()) { /* try on/off and yes/no */ std::string tmp(s); gu::trim(tmp); if (tmp.length() >=2 && tmp.length() <= 3) { std::transform (tmp.begin(), tmp.end(), tmp.begin(), static_cast(std::tolower)); if (tmp == "yes" || tmp == "on") return true; if (tmp == "off" || tmp == "no") return false; } throw NotFound(); } } return ret; } } // namespace gu percona-xtradb-cluster-galera/galerautils/src/gu_utils.c0000644000000000000000000000405612247075736023761 0ustar rootroot00000000000000// Copyright (C) 2010 Codership Oy /** * @file Miscellaneous utility functions * * $Id: gu_utils.c 3336 2013-10-28 07:41:56Z teemu $ */ #include "gu_utils.h" #include #include #include #include #include const char* gu_str2ll (const char* str, long long* ll) { char* ret; int shift = 0; long long llret = strtoll (str, &ret, 0); switch (ret[0]) { case 't': case 'T': shift += 10; case 'g': case 'G': shift += 10; case 'm': case 'M': shift += 10; case 'k': case 'K': shift += 10; ret++; if (llret == ((llret << (shift + 1)) >> (shift + 1))) { llret <<= shift; } else { /* ERANGE */ if (llret > 0) llret = LLONG_MAX; else llret = LLONG_MIN; } default: *ll = llret; } return ret; } const char* gu_str2dbl (const char* str, double* dbl) { char* ret; *dbl = strtod (str, &ret); return ret; } const char* gu_str2bool (const char* str, bool* b) { size_t len = strlen(str); int res = -1; /* no conversion */ switch (len) { case 1: switch (str[0]) { case '0': case 'N': case 'n': res = 0; break; case '1': case 'Y': case 'y': res = 1; break; } break; case 2: if (!strcasecmp(str, "on")) res = 1; if (!strcasecmp(str, "no")) res = 0; break; case 3: if (!strcasecmp(str, "off")) res = 0; if (!strcasecmp(str, "yes")) res = 1; break; case 4: if (!strcasecmp(str, "true")) res = 1; if (!strcasecmp(str, "sure")) res = 1; if (!strcasecmp(str, "nope")) res = 0; break; case 5: if (!strcasecmp(str, "false")) res = 0; break; } *b = (res > 0); return (res >= 0) ? (str + len) : str; } const char* gu_str2ptr (const char* str, void** ptr) { char* ret; *ptr = (void*) (intptr_t)strtoll (str, &ret, 16); return ret; } percona-xtradb-cluster-galera/galerautils/src/gu_utils.h0000644000000000000000000000165012247075736023763 0ustar rootroot00000000000000// Copyright (C) 2010 Codership Oy /** * @file Miscellaneous utility functions * * $Id: gu_utils.h 3336 2013-10-28 07:41:56Z teemu $ */ #ifndef _gu_utils_h_ #define _gu_utils_h_ #include #ifdef __cplusplus extern "C" { #endif /* * The string conversion functions below are slighly customized * versions of standard libc functions designed to understand 'on'/'off' and * K/M/G size modifiers and the like. * * They return pointer to the next character after conversion: * - if (ret == str) no conversion was made * - if (ret[0] == '\0') whole string was converted */ extern const char* gu_str2ll (const char* str, long long* ll); extern const char* gu_str2dbl (const char* str, double* dbl); extern const char* gu_str2bool (const char* str, bool* b); extern const char* gu_str2ptr (const char* str, void** ptr); #ifdef __cplusplus } #endif #endif /* _gu_utils_h_ */ percona-xtradb-cluster-galera/galerautils/src/gu_utils.hpp0000644000000000000000000000753412247075736024332 0ustar rootroot00000000000000// Copyright (C) 2009-2010 Codership Oy /** * @file General-purpose functions and templates * * $Id: gu_utils.hpp 3035 2013-04-07 17:27:51Z alex $ */ #ifndef _gu_utils_hpp_ #define _gu_utils_hpp_ #include #include #include #include #include "gu_exception.hpp" #include "gu_string.hpp" namespace gu { /* * String conversion functions for primitive types */ /*! Generic to_string() template function */ template inline std::string to_string(const T& x, std::ios_base& (*f)(std::ios_base&) = std::dec) { std::ostringstream out; out << std::showbase << f << x; return out.str(); } /*! Specialized template: make bool translate into 'true' or 'false' */ template <> inline std::string to_string(const bool& x, std::ios_base& (*f)(std::ios_base&)) { std::ostringstream out; out << std::boolalpha << x; return out.str(); } /*! Specialized template: make double to print with full precision */ template <> inline std::string to_string(const double& x, std::ios_base& (*f)(std::ios_base&)) { const int sigdigits = std::numeric_limits::digits10; // or perhaps std::numeric_limits::max_digits10? std::ostringstream out; out << std::setprecision(sigdigits) << x; return out.str(); } /*! Generic from_string() template. Default base is decimal. * @throws NotFound */ template inline T from_string(const std::string& s, std::ios_base& (*f)(std::ios_base&) = std::dec) { std::istringstream iss(s); T ret; try { if ((iss >> f >> ret).fail()) throw NotFound(); } catch (gu::Exception& e) { throw NotFound(); } return ret; } /*! Specialized template for reading strings. This is to avoid throwing * NotFound in case of empty string. */ template <> inline std::string from_string(const std::string& s, std::ios_base& (*f)(std::ios_base&)) { return s; } /*! Specialized template for reading pointers. Default base is hex. * @throws NotFound */ template <> inline void* from_string(const std::string& s, std::ios_base& (*f)(std::ios_base&)) { std::istringstream iss(s); void* ret; if ((iss >> std::hex >> ret).fail()) throw NotFound(); return ret; } extern "C" const char* gu_str2bool (const char* str, bool* bl); //extern bool _to_bool (const std::string& s); /*! Specialized template for reading bool. Tries both 1|0 and true|false * @throws NotFound */ template <> inline bool from_string (const std::string& s, std::ios_base& (*f)(std::ios_base&)) { const char* const str(s.c_str()); bool ret; const char* endptr(gu_str2bool(str, &ret)); if (endptr == 0 || *endptr != '\0') throw NotFound(); return ret; } /*! * Substitute for the Variable Length Array on the stack from C99. * Provides automatic deallocation when out of scope: * * void foo(size_t n) { VLA bar(n); bar[0] = 5; throw; } */ template class VLA { T* array; VLA (const VLA&); VLA& operator= (const VLA&); public: VLA (size_t n) : array(new T[n]) {} ~VLA () { delete[] array; } T* operator& () { return array; } T& operator[] (size_t i) { return array[i]; } }; /*! * Object deletion operator. Convenient with STL containers containing * pointers. Example: * * @code * void cleanup() * { * for_each(container.begin(), container.end(), DeleteObject()); * container.clear(); * } * * @endcode */ class DeleteObject { public: template void operator()(T* t) { delete t; } }; } // namespace gu #endif /* _gu_utils_hpp_ */ percona-xtradb-cluster-galera/galerautils/src/gu_uuid.c0000644000000000000000000001157112247075736023567 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gu_uuid.c 3055 2013-04-19 20:29:20Z alex $ */ /* * Universally Unique IDentifier. RFC 4122. * Time-based implementation. * */ #include "gu_uuid.h" #include "gu_byteswap.h" #include "gu_log.h" #include "gu_assert.h" #include "gu_mutex.h" #include "gu_time.h" #include "gu_rand.h" #include // for rand_r() #include // for memcmp() #include // for fopen() et al #include // for gettimeofday() #include // for getpid() #include // for errno #include const gu_uuid_t GU_UUID_NIL = {{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}; #define UUID_NODE_LEN 6 /** Returns 64-bit system time in 100 nanoseconds */ static uint64_t uuid_get_time () { static long long check = 0; static gu_mutex_t mtx = GU_MUTEX_INITIALIZER; long long t; gu_mutex_lock (&mtx); do { t = gu_time_calendar() / 100; } while (check == t); check = t; gu_mutex_unlock (&mtx); return (t + 0x01B21DD213814000LL); // offset since the start of 15 October 1582 } #ifndef UUID_URAND // This function can't be called too often, // apparently due to lack of entropy in the pool. /** Fills node part of the uuid with true random data from /dev/urand */ static int uuid_urand_node (uint8_t* node, size_t node_len) { static const char urand_name[] = "/dev/urandom"; FILE* urand; size_t i = 0; int c; urand = fopen (urand_name, "r"); if (NULL == urand) { gu_debug ("Failed to open %s for reading (%d).", urand_name, -errno); return -errno; } while (i < node_len && (c = fgetc (urand)) != EOF) { node[i] = (uint8_t) c; i++; } fclose (urand); return 0; } #else #define uuid_urand_node(a,b) true #endif /** Fills node part with pseudorandom data from rand_r() */ static void uuid_rand_node (uint8_t* node, size_t node_len) { unsigned int seed = gu_rand_seed_int (gu_time_calendar(), node, getpid()); size_t i; for (i = 0; i < node_len; i++) { uint32_t r = (uint32_t) rand_r (&seed); /* combine all bytes into the lowest byte */ node[i] = (uint8_t)((r) ^ (r >> 8) ^ (r >> 16) ^ (r >> 24)); } } static inline void uuid_fill_node (uint8_t* node, size_t node_len) { if (uuid_urand_node (node, node_len)) { uuid_rand_node (node, node_len); } } void gu_uuid_generate (gu_uuid_t* uuid, const void* node, size_t node_len) { assert (NULL != uuid); assert (NULL == node || 0 != node_len); uint32_t* uuid32 = (uint32_t*) uuid->data; uint16_t* uuid16 = (uint16_t*) uuid->data; uint64_t uuid_time = uuid_get_time (); uint16_t clock_seq = gu_rand_seed_int (uuid_time, &GU_UUID_NIL, getpid()); /* time_low */ uuid32[0] = gu_be32 (uuid_time & 0xFFFFFFFF); /* time_mid */ uuid16[2] = gu_be16 ((uuid_time >> 32) & 0xFFFF); /* time_high_and_version */ uuid16[3] = gu_be16 (((uuid_time >> 48) & 0x0FFF) | (1 << 12)); /* clock_seq_and_reserved */ uuid16[4] = gu_be16 ((clock_seq & 0x3FFF) | 0x8000); /* node */ if (NULL != node && 0 != node_len) { memcpy (&uuid->data[10], node, node_len > UUID_NODE_LEN ? UUID_NODE_LEN : node_len); } else { uuid_fill_node (&uuid->data[10], UUID_NODE_LEN); uuid->data[10] |= 0x02; /* mark as "locally administered" */ } return; } /** * Compare two UUIDs * @return -1, 0, 1 if left is respectively less, equal or greater than right */ long gu_uuid_compare (const gu_uuid_t* left, const gu_uuid_t* right) { return memcmp (left, right, sizeof(gu_uuid_t)); } static uint64_t uuid_time (const gu_uuid_t* uuid) { uint64_t uuid_time; /* time_high_and_version */ uuid_time = gu_be16 (((uint16_t*)uuid->data)[3]) & 0x0FFF; /* time_mid */ uuid_time = (uuid_time << 16) + gu_be16 (((uint16_t*)uuid->data)[2]); /* time_low */ uuid_time = (uuid_time << 32) + gu_be32 (((uint32_t*)uuid->data)[0]); return uuid_time; } /** * Compare ages of two UUIDs * @return -1, 0, 1 if left is respectively younger, equal or older than right */ long gu_uuid_older (const gu_uuid_t* left, const gu_uuid_t* right) { uint64_t time_left = uuid_time (left); uint64_t time_right = uuid_time (right); if (time_left < time_right) return 1; if (time_left > time_right) return -1; return 0; } ssize_t gu_uuid_print(const gu_uuid_t* uuid, char* buf, size_t buflen) { if (buflen < GU_UUID_STR_LEN) return -1; return sprintf(buf, GU_UUID_FORMAT, GU_UUID_ARGS(uuid)); } ssize_t gu_uuid_scan(const char* buf, size_t buflen, gu_uuid_t* uuid) { ssize_t ret; if (buflen < GU_UUID_STR_LEN) return -1; ret = sscanf(buf, GU_UUID_FORMAT_SCANF, GU_UUID_ARGS_SCANF(uuid)); if (ret != sizeof(uuid->data)) return -1; return ret; } percona-xtradb-cluster-galera/galerautils/src/gu_uuid.h0000644000000000000000000000560212247075736023572 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gu_uuid.h 3055 2013-04-19 20:29:20Z alex $ */ /* * Universally Unique IDentifier. RFC 4122. * Time-based implementation. * */ #ifndef _gu_uuid_h_ #define _gu_uuid_h_ #include "gu_types.h" #ifdef __cplusplus extern "C" { #endif /*! UUID internally is represented as a BE integer which allows using * memcmp() as comparison function and straightforward printing */ #define GU_UUID_LEN 16 typedef struct { uint8_t data[GU_UUID_LEN]; } gu_uuid_t; extern const gu_uuid_t GU_UUID_NIL; /*! length of string representation */ #define GU_UUID_STR_LEN 36 /*! Macros for pretty printing */ #define GU_UUID_FORMAT \ "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" #define GU_UUID_ARGS(uuid) \ (uuid)->data[ 0], (uuid)->data[ 1], (uuid)->data[ 2], (uuid)->data[ 3],\ (uuid)->data[ 4], (uuid)->data[ 5], (uuid)->data[ 6], (uuid)->data[ 7],\ (uuid)->data[ 8], (uuid)->data[ 9], (uuid)->data[10], (uuid)->data[11],\ (uuid)->data[12], (uuid)->data[13], (uuid)->data[14], (uuid)->data[15] /* this is used for scanf, variables are by reference */ #define GU_UUID_FORMAT_SCANF \ "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" #define GU_UUID_ARGS_SCANF(uuid) \ &(uuid)->data[ 0], &(uuid)->data[ 1], &(uuid)->data[ 2], &(uuid)->data[ 3],\ &(uuid)->data[ 4], &(uuid)->data[ 5], &(uuid)->data[ 6], &(uuid)->data[ 7],\ &(uuid)->data[ 8], &(uuid)->data[ 9], &(uuid)->data[10], &(uuid)->data[11],\ &(uuid)->data[12], &(uuid)->data[13], &(uuid)->data[14], &(uuid)->data[15] /*! * Generates new UUID. * If node is NULL, will generate random (if /dev/urand is present) or * pseudorandom data instead. * @param uuid * pointer to uuid_t * @param node * some unique data that goes in place of "node" field in the UUID * @param node_len * length of the node buffer */ extern void gu_uuid_generate (gu_uuid_t* uuid, const void* node, size_t node_len); /*! * Compare two UUIDs according to RFC * @return -1, 0, 1 if left is respectively less, equal or greater than right */ extern long gu_uuid_compare (const gu_uuid_t* left, const gu_uuid_t* right); /*! * Compare ages of two UUIDs * @return -1, 0, 1 if left is respectively younger, equal or older than right */ extern long gu_uuid_older (const gu_uuid_t* left, const gu_uuid_t* right); /*! * Print UUID into buffer * @return Number of bytes printed (not including trailing '\0') or -1 on error. */ extern ssize_t gu_uuid_print(const gu_uuid_t* uuid, char* buf, size_t buflen); /*! * Scan UUID from buffer * @return Number of bytes read (should match to sizeof(uuid)) or -1 on error */ extern ssize_t gu_uuid_scan(const char* buf, size_t buflen, gu_uuid_t* uuid); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* _gu_uuid_h_ */ percona-xtradb-cluster-galera/galerautils/src/gu_vlq.hpp0000644000000000000000000001470712247075736023774 0ustar rootroot00000000000000// // Copyright (C) 2011 Codership Oy // //! // @file Variable-length quantity encoding for integers // // Unsigned integers: Implementation uses using unsigned LEB128, see for example // http://en.wikipedia.org/wiki/LEB128. // // Signed integers: TODO // #ifndef GU_VLQ_HPP #define GU_VLQ_HPP #include "gu_buffer.hpp" #include "gu_throw.hpp" #include "gu_logger.hpp" #include "gu_macros.h" #define GU_VLQ_CHECKS //#define GU_VLQ_ALEX namespace gu { //! // @brief Retun number of bytes required to represent given value in ULEB128 // representation. // // @param value Unsigned value // // @return Number of bytes required for value representation // template inline size_t uleb128_size(UI value) { size_t i(1); value >>= 7; for (; value != 0; value >>= 7, ++i) {} return i; } //! // @brief Encode unsigned type to ULEB128 representation // // @param value // @param buf // @param buflen // @param offset // // @return Offset // template inline size_t uleb128_encode(UI value, byte_t* buf, size_t buflen, size_t offset) { #ifdef GU_VLQ_ALEX assert (offset < buflen); buf[offset] = value & 0x7f; while (value >>= 7) { buf[offset] |= 0x80; ++offset; assert(offset < buflen); buf[offset] = value & 0x7f; } return offset + 1; #else do { #ifdef GU_VLQ_CHECKS if (gu_unlikely(offset >= buflen)) gu_throw_fatal; #endif buf[offset] = value & 0x7f; value >>= 7; if (gu_unlikely(value != 0)) { buf[offset] |= 0x80; } ++offset; } while (value != 0); return offset; #endif } //! // @brief Decode unsigned type from ULEB128 representation // // @param buf // @param buflen // @param offset // @param value // // @return Offset // template inline size_t uleb128_decode(const byte_t* buf, size_t buflen, size_t offset, UI& value) { #ifdef GU_VLQ_ALEX assert (offset < buflen); value = buf[offset] & 0x7f; size_t shift(0); while (buf[offset] & 0x80) { ++offset; shift +=7; #ifdef GU_VLQ_CHECKS // Check if trying to read past last byte in buffer without // encountering byte without 0x80 bit set. if (gu_unlikely(offset >= buflen)) { gu_throw_error(EINVAL) << "read value is not uleb128 representation, missing " << "terminating byte before end of input"; } // // determine proper bit shift // // type width static const size_t width(sizeof(UI) * 8); // bits available after shift const ssize_t avail_bits(width - shift); assert(avail_bits > 0); if (gu_unlikely(avail_bits < 7)) { // mask to check if the remaining value can be represented // with available bits gu::byte_t mask(~((1 << avail_bits) - 1)); if ((buf[offset] & mask) != 0) { gu_throw_error(ERANGE) << "read value not representable with " << width << " bits, shift: " << shift << " avail bits: " << avail_bits << " mask: 0x" << std::hex << static_cast(mask) << " buf: 0x" << std::hex << static_cast(buf[offset]) << " excess: 0x" << std::hex << static_cast(mask & buf[offset]); } } #endif value |= (static_cast(buf[offset] & 0x7f) << shift); } return offset + 1; #else /* GU_VLQ_ALEX */ value = 0; size_t shift(0); // initial check for overflow, at least one byte must be readable #ifdef GU_VLQ_CHECKS if (gu_unlikely(offset >= buflen)) gu_throw_fatal; #endif while (true) { value |= (static_cast(buf[offset] & 0x7f) << shift); if (gu_likely((buf[offset] & 0x80) == 0)) { // last byte ++offset; break; } ++offset; shift += 7; #ifdef GU_VLQ_CHECKS // Check if trying to read past last byte in buffer without // encountering byte without 0x80 bit set. if (gu_unlikely(offset >= buflen)) { gu_throw_error(EINVAL) << "read value is not uleb128 representation, missing " << "terminating byte before end of input"; } // // determine proper bit shift // // type width static const size_t width(sizeof(UI) * 8); // bits available after shift const ssize_t avail_bits(width - shift); assert(avail_bits > 0); if (gu_unlikely(avail_bits < 7)) { // mask to check if the remaining value can be represented // with available bits gu::byte_t mask(~((1 << avail_bits) - 1)); if ((buf[offset] & mask) != 0) { gu_throw_error(ERANGE) << "read value not representable with " << width << " bits, shift: " << shift << " avail bits: " << avail_bits << " mask: 0x" << std::hex << static_cast(mask) << " buf: 0x" << std::hex << static_cast(buf[offset]) << " excess: 0x" << std::hex << static_cast(mask & buf[offset]); } } #endif } return offset; #endif /* GU_VLQ_NEW */ } } #endif // GU_VLQ_HPP percona-xtradb-cluster-galera/galerautils/tests/SConscript0000644000000000000000000000277712247075736024357 0ustar rootroot00000000000000 Import('check_env') env = check_env.Clone() env.Prepend(LIBS=File('#/galerautils/src/libgalerautils.a')) env.Prepend(LIBS=File('#/galerautils/src/libgalerautils++.a')) gu_tests = env.Program(target = 'gu_tests', source = Split(''' gu_tests.c gu_mem_test.c gu_bswap_test.c gu_fnv_test.c gu_mmh3_test.c gu_spooky_test.c gu_hash_test.c gu_time_test.c gu_fifo_test.c gu_uuid_test.c gu_dbug_test.c gu_lock_step_test.c gu_str_test.c gu_utils_test.c ''')) env.Test("gu_tests.passed", gu_tests) env.Alias("test", "gu_tests.passed") Clean(gu_tests, '#/gu_tests.log') gu_testspp = env.Program(target = 'gu_tests++', source = Split(''' gu_tests++.cpp gu_string_test.cpp gu_uri_test.cpp gu_net_test.cpp gu_datetime_test.cpp gu_vlq_test.cpp ''')) env.Test("gu_tests++.passed", gu_testspp) env.Alias("test", "gu_tests++.passed") Clean(gu_testspp, '#/gu_tests++.log') percona-xtradb-cluster-galera/galerautils/tests/gu_bswap_test.c0000644000000000000000000000275512247075736025353 0ustar rootroot00000000000000// Copyright (C) 2007 Codership Oy // $Id: gu_bswap_test.c 248 2008-03-23 16:32:00Z alex $ #include #include #include "gu_bswap_test.h" #include "../src/gu_byteswap.h" START_TEST (gu_bswap_test) { // need volatile to prevent compile-time optimization volatile uint16_t s = 0x1234; volatile uint32_t i = 0x12345678; volatile uint64_t l = 0x1827364554637281LL; uint16_t sle, sbe; uint32_t ile, ibe; uint64_t lle, lbe; // first conversion sle = gu_le16(s); sbe = gu_be16(s); ile = gu_le32(i); ibe = gu_be32(i); lle = gu_le64(l); lbe = gu_be64(l); #if __BYTE_ORDER == __LITTLE_ENDIAN fail_if (s != sle); fail_if (i != ile); fail_if (l != lle); fail_if (s == sbe); fail_if (i == ibe); fail_if (l == lbe); #else fail_if (s == sle); fail_if (i == ile); fail_if (l == lle); fail_if (s != sbe); fail_if (i != ibe); fail_if (l != lbe); #endif /* __BYTE_ORDER */ // second conversion sle = gu_le16(sle); sbe = gu_be16(sbe); ile = gu_le32(ile); ibe = gu_be32(ibe); lle = gu_le64(lle); lbe = gu_be64(lbe); fail_if (s != sle); fail_if (i != ile); fail_if (l != lle); fail_if (s != sbe); fail_if (i != ibe); fail_if (l != lbe); } END_TEST Suite *gu_bswap_suite(void) { Suite *s = suite_create("Galera byteswap functions"); TCase *tc = tcase_create("gu_bswap"); suite_add_tcase (s, tc); tcase_add_test (tc, gu_bswap_test); return s; } percona-xtradb-cluster-galera/galerautils/tests/gu_bswap_test.h0000644000000000000000000000034412247075736025350 0ustar rootroot00000000000000// Copyright (C) 2007 Codership Oy // $Id: gu_bswap_test.h 248 2008-03-23 16:32:00Z alex $ #ifndef __gu_bswap_test__ #define __gu_bswap_test__ Suite *gu_bswap_suite(void); #endif /* __gu_bswap_test__ */ percona-xtradb-cluster-galera/galerautils/tests/gu_datetime_test.cpp0000644000000000000000000000657212247075736026374 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy * * $Id$ */ #include "gu_datetime.hpp" #include "gu_logger.hpp" #include "gu_utils.hpp" #include "gu_datetime_test.hpp" using namespace gu; using namespace gu::datetime; START_TEST(test_units) { fail_unless(NSec == 1LL); fail_unless(USec == 1000LL); fail_unless(MSec == 1000LL*1000LL); fail_unless(Sec == 1000LL*1000LL*1000LL); fail_unless(Min == 60LL*1000LL*1000LL*1000LL); fail_unless(Hour == 60LL*60LL*1000LL*1000LL*1000LL); fail_unless(Day == 24LL*60LL*60LL*1000LL*1000LL*1000LL); fail_unless(Month == 30LL*24LL*60LL*60LL*1000LL*1000LL*1000LL); fail_unless(Year == 12LL*30LL*24LL*60LL*60LL*1000LL*1000LL*1000LL); } END_TEST START_TEST(test_period) { // Zero periods fail_unless(Period("").get_nsecs() == 0); fail_unless(Period("P").get_nsecs() == 0); fail_unless(Period("PT").get_nsecs() == 0); // Year-mon-day fail_unless(Period("P3Y").get_nsecs() == 3*Year); fail_unless(Period("P5M").get_nsecs() == 5*Month); fail_unless(Period("P37D").get_nsecs() == 37*Day); fail_unless(Period("P3Y17M").get_nsecs() == 3*Year + 17*Month); fail_unless(Period("P5Y66D").get_nsecs() == 5*Year + 66*Day); fail_unless(Period("P37M44D").get_nsecs() == 37*Month + 44*Day); fail_unless(Period("P3YT").get_nsecs() == 3*Year); fail_unless(Period("P5MT").get_nsecs() == 5*Month); fail_unless(Period("P37DT").get_nsecs() == 37*Day); fail_unless(Period("P3Y17MT").get_nsecs() == 3*Year + 17*Month); fail_unless(Period("P5Y66DT").get_nsecs() == 5*Year + 66*Day); fail_unless(Period("P37M44DT").get_nsecs() == 37*Month + 44*Day); // Hour-min-sec fail_unless(Period("PT3H").get_nsecs() == 3*Hour); fail_unless(Period("PT5M").get_nsecs() == 5*Min); fail_unless(Period("P37S").get_nsecs() == 37*Sec); // fail_unless(Period("PT3.578777S").get_nsecs() == 3*Sec + 578*MSec + 777*USec); fail_unless(Period("PT0.5S").get_nsecs() == 500*MSec); // fail_unless(Period("PT5H7M3.578777S").get_nsecs() == 5*Hour + 7*Min + 3*Sec + 578*MSec + 777*USec); // @todo these should fail fail_unless(Period("PT.S").get_nsecs() == 0); fail_unless(Period("PT.D").get_nsecs() == 0); } END_TEST START_TEST(test_date) { Date d1(Date::now()); Date d2 = d1 + Period("PT6S"); fail_unless(d2.get_utc() == d1.get_utc() + 6*Sec); fail_unless(d2 - Period("PT6S") == d1); Date max(Date::max()); fail_unless(d1 < max); } END_TEST START_TEST(test_trac_712) { try { Period p; p = gu::from_string("0x3"); // used to throw gu::Exception } catch (gu::NotFound& nf) { } } END_TEST Suite* gu_datetime_suite() { Suite* s = suite_create("gu::datetime"); TCase* tc; tc = tcase_create("test_units"); tcase_add_test(tc, test_units); suite_add_tcase(s, tc); tc = tcase_create("test_period"); tcase_add_test(tc, test_period); suite_add_tcase(s, tc); tc = tcase_create("test_date"); tcase_add_test(tc, test_date); suite_add_tcase(s, tc); tc = tcase_create("test_trac_712"); tcase_add_test(tc, test_trac_712); suite_add_tcase(s, tc); return s; } percona-xtradb-cluster-galera/galerautils/tests/gu_datetime_test.hpp0000644000000000000000000000034112247075736026365 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy * * $Id$ */ #ifndef __gu_datetime_test_hpp__ #define __gu_datetime_test_hpp__ #include Suite* gu_datetime_suite(); #endif // __gu_datetime_test_hpp__ percona-xtradb-cluster-galera/galerautils/tests/gu_dbug_test.c0000644000000000000000000000323012247075736025145 0ustar rootroot00000000000000// Copyright (C) 2008 Codership Oy // $Id: gu_dbug_test.c 2929 2013-01-14 19:40:03Z alex $ /* Pthread yield */ #define _GNU_SOURCE 1 #include #include #include #include #include "gu_dbug_test.h" #include "../src/gu_dbug.h" static void cf() { GU_DBUG_ENTER("cf"); GU_DBUG_PRINT("galera", ("hello from cf")); sched_yield(); GU_DBUG_VOID_RETURN; } static void bf() { GU_DBUG_ENTER("bf"); GU_DBUG_PRINT("galera", ("hello from bf")); sched_yield(); cf(); GU_DBUG_VOID_RETURN; } static void af() { GU_DBUG_ENTER("af"); GU_DBUG_PRINT("galera", ("hello from af")); sched_yield(); bf(); GU_DBUG_VOID_RETURN; } static time_t stop = 0; static void *dbg_thr(void *arg) { while (time(NULL) < stop) { af(); } pthread_exit(NULL); } START_TEST(gu_dbug_test) { int i; #define N_THREADS 10 pthread_t th[N_THREADS]; /* Log > /dev/null */ GU_DBUG_FILE = fopen("/dev/null", "a+"); /* These should not produce output yet */ af(); af(); af(); /* Start logging */ GU_DBUG_PUSH("d:t:i"); GU_DBUG_PRINT("galera", ("Start logging")); af(); af(); af(); /* Run few threads concurrently */ stop = time(NULL) + 2; for (i = 0; i < N_THREADS; i++) pthread_create(&th[i], NULL, &dbg_thr, NULL); for (i = 0; i < N_THREADS; i++) pthread_join(th[i], NULL); } END_TEST Suite *gu_dbug_suite(void) { Suite *s = suite_create("Galera dbug functions"); TCase *tc = tcase_create("gu_dbug"); suite_add_tcase (s, tc); tcase_add_test (tc, gu_dbug_test); tcase_set_timeout(tc, 60); return s; } percona-xtradb-cluster-galera/galerautils/tests/gu_dbug_test.h0000644000000000000000000000033712247075736025157 0ustar rootroot00000000000000// Copyright (C) 2008 Codership Oy // $Id: gu_dbug_test.h 248 2008-03-23 16:32:00Z alex $ #ifndef __gu_dbug_test__ #define __gu_dbug_test__ Suite *gu_dbug_suite(void); #endif /* __gu_dbug_test__ */ percona-xtradb-cluster-galera/galerautils/tests/gu_fifo_test.c0000644000000000000000000001277612247075736025166 0ustar rootroot00000000000000// Copyright (C) 2007 Codership Oy // $Id: gu_fifo_test.c 2364 2011-09-16 16:32:07Z alex $ #include #include "gu_fifo_test.h" #include "../src/galerautils.h" #define FIFO_LENGTH 10000L START_TEST (gu_fifo_test) { gu_fifo_t* fifo; long i; size_t* item; long used; fifo = gu_fifo_create (0, 1); fail_if (fifo != NULL); fifo = gu_fifo_create (1, 0); fail_if (fifo != NULL); fifo = gu_fifo_create (1, 1); fail_if (fifo == NULL); gu_fifo_close (fifo); mark_point(); gu_fifo_destroy (fifo); mark_point(); fifo = gu_fifo_create (FIFO_LENGTH, sizeof(i)); fail_if (fifo == NULL); fail_if (gu_fifo_length(fifo) != 0, "fifo->used is %lu for an empty FIFO", gu_fifo_length(fifo)); // fill FIFO for (i = 0; i < FIFO_LENGTH; i++) { item = gu_fifo_get_tail (fifo); fail_if (item == NULL, "could not get item %ld", i); *item = i; gu_fifo_push_tail (fifo); } used = i; fail_if (gu_fifo_length(fifo) != used, "used is %zu, expected %zu", used, gu_fifo_length(fifo)); // test pop for (i = 0; i < used; i++) { int err; item = gu_fifo_get_head (fifo, &err); fail_if (item == NULL, "could not get item %ld", i); fail_if (*item != (ulong)i, "got %ld, expected %ld", *item, i); gu_fifo_pop_head (fifo); } fail_if (gu_fifo_length(fifo) != 0, "gu_fifo_length() for empty queue is %ld", gu_fifo_length(fifo)); gu_fifo_close (fifo); int err; item = gu_fifo_get_head (fifo, &err); fail_if (item != NULL); fail_if (err != -ENODATA); gu_fifo_destroy (fifo); } END_TEST static pthread_mutex_t sync_mtx = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t sync_cond = PTHREAD_COND_INITIALIZER; #define ITEM 12345 static void* cancel_thread (void* arg) { gu_fifo_t* q = arg; /* sync with parent */ pthread_mutex_lock (&sync_mtx); pthread_cond_signal (&sync_cond); pthread_mutex_unlock (&sync_mtx); size_t* item; int err; /* try to get from non-empty queue */ item = gu_fifo_get_head (q, &err); fail_if (NULL != item, "Got item %p: %zu", item, item ? *item : 0); fail_if (-ECANCELED != err); /* signal end of the first gu_fifo_get_head() */ pthread_mutex_lock (&sync_mtx); pthread_cond_signal (&sync_cond); /* wait until gets are resumed */ pthread_cond_wait (&sync_cond, &sync_mtx); item = gu_fifo_get_head (q, &err); fail_if (NULL == item); fail_if (ITEM != *item); gu_fifo_pop_head (q); /* signal end of the 2nd gu_fifo_get_head() */ pthread_cond_signal (&sync_cond); pthread_mutex_unlock (&sync_mtx); /* try to get from empty queue (should block) */ item = gu_fifo_get_head (q, &err); fail_if (NULL != item); fail_if (-ECANCELED != err); /* signal end of the 3rd gu_fifo_get_head() */ pthread_mutex_lock (&sync_mtx); pthread_cond_signal (&sync_cond); /* wait until fifo is closed */ pthread_cond_wait (&sync_cond, &sync_mtx); item = gu_fifo_get_head (q, &err); fail_if (NULL != item); fail_if (-ECANCELED != err); /* signal end of the 4th gu_fifo_get_head() */ pthread_cond_signal (&sync_cond); /* wait until fifo is resumed */ pthread_cond_wait (&sync_cond, &sync_mtx); pthread_mutex_unlock (&sync_mtx); item = gu_fifo_get_head (q, &err); fail_if (NULL != item); fail_if (-ENODATA != err); return NULL; } START_TEST(gu_fifo_cancel_test) { gu_fifo_t* q = gu_fifo_create (FIFO_LENGTH, sizeof(size_t)); size_t* item = gu_fifo_get_tail (q); fail_if (item == NULL); *item = ITEM; gu_fifo_push_tail (q); pthread_mutex_lock (&sync_mtx); pthread_t thread; pthread_create (&thread, NULL, cancel_thread, q); /* sync with child thread */ gu_fifo_lock (q); pthread_cond_wait (&sync_cond, &sync_mtx); int err; err = gu_fifo_cancel_gets (q); fail_if (0 != err); err = gu_fifo_cancel_gets (q); fail_if (-EBADFD != err); /* allow the first gu_fifo_get_head() */ gu_fifo_release (q); mark_point(); /* wait for the first gu_fifo_get_head() to complete */ pthread_cond_wait (&sync_cond, &sync_mtx); err = gu_fifo_resume_gets (q); fail_if (0 != err); err = gu_fifo_resume_gets (q); fail_if (-EBADFD != err); /* signal that now gets are resumed */ pthread_cond_signal (&sync_cond); /* wait for the 2nd gu_fifo_get_head() to complete */ pthread_cond_wait (&sync_cond, &sync_mtx); /* wait a bit to make sure 3rd gu_fifo_get_head() is blocked * (even if it is not - still should work)*/ usleep (100000 /* 0.1s */); err = gu_fifo_cancel_gets (q); fail_if (0 != err); /* wait for the 3rd gu_fifo_get_head() to complete */ pthread_cond_wait (&sync_cond, &sync_mtx); gu_fifo_close (q); // closes for puts, but the q still must be canceled pthread_cond_signal (&sync_cond); /* wait for the 4th gu_fifo_get_head() to complete */ pthread_cond_wait (&sync_cond, &sync_mtx); gu_fifo_resume_gets (q); // resumes gets pthread_cond_signal (&sync_cond); pthread_mutex_unlock (&sync_mtx); mark_point(); pthread_join(thread, NULL); } END_TEST Suite *gu_fifo_suite(void) { Suite *s = suite_create("Galera FIFO functions"); TCase *tc = tcase_create("gu_fifo"); suite_add_tcase (s, tc); tcase_add_test (tc, gu_fifo_test); tcase_add_test (tc, gu_fifo_cancel_test); return s; } percona-xtradb-cluster-galera/galerautils/tests/gu_fifo_test.h0000644000000000000000000000033712247075736025161 0ustar rootroot00000000000000// Copyright (C) 2007 Codership Oy // $Id: gu_fifo_test.h 387 2008-08-20 21:11:20Z alex $ #ifndef __gu_fifo_test__ #define __gu_fifo_test__ Suite *gu_fifo_suite(void); #endif /* __gu_fifo_test__ */ percona-xtradb-cluster-galera/galerautils/tests/gu_fnv_test.c0000644000000000000000000000334612247075736025025 0ustar rootroot00000000000000// Copyright (C) 2012 Codership Oy // $Id$ #include "gu_fnv_test.h" #include static const char* const test_buf = "chongo /\\../\\"; // enable normal FNV mode for reference hash checking #define GU_FNV_NORMAL #include "../src/gu_fnv.h" START_TEST (gu_fnv32_test) { uint32_t ret = 0; gu_fnv32a_internal (test_buf, strlen(test_buf), &ret); fail_if (GU_FNV32_SEED != ret, "FNV32 failed: expected %"PRIu32", got %"PRIu32, GU_FNV32_SEED, ret); } END_TEST START_TEST (gu_fnv64_test) { uint64_t ret = 0; gu_fnv64a_internal (test_buf, strlen(test_buf), &ret); fail_if (GU_FNV64_SEED != ret, "FNV64 failed: expected %"PRIu64", got %"PRIu64, GU_FNV64_SEED, ret); } END_TEST START_TEST (gu_fnv128_test) { gu_uint128_t GU_SET128(ret, 0, 0); gu_fnv128a_internal (test_buf, strlen(test_buf), &ret); #if defined(__SIZEOF_INT128__) fail_if (!GU_EQ128(GU_FNV128_SEED, ret), "FNV128 failed: expected %"PRIx64" %"PRIx64", got %"PRIx64" %"PRIx64, (uint64_t)(GU_FNV128_SEED >> 64), (uint64_t)GU_FNV128_SEED, (uint64_t)(ret >> 64), (uint64_t)ret); #else fail_if (!GU_EQ128(GU_FNV128_SEED, ret), "FNV128 failed: expected %"PRIx64" %"PRIx64", got %"PRIx64" %"PRIx64, GU_FNV128_SEED.u64[GU_64HI], GU_FNV128_SEED.u64[GU_64LO], ret.u64[GU_64HI], ret.u64[GU_64LO]); #endif } END_TEST Suite *gu_fnv_suite(void) { Suite *s = suite_create("FNV hash"); TCase *tc_fnv = tcase_create("gu_fnv"); suite_add_tcase (s, tc_fnv); tcase_add_test(tc_fnv, gu_fnv32_test); tcase_add_test(tc_fnv, gu_fnv64_test); tcase_add_test(tc_fnv, gu_fnv128_test); return s; } percona-xtradb-cluster-galera/galerautils/tests/gu_fnv_test.h0000644000000000000000000000030712247075736025024 0ustar rootroot00000000000000// Copyright (C) 2012 Codership Oy // $Id$ #ifndef __gu_fnv_test__ #define __gu_fnv_test__ #include extern Suite *gu_fnv_suite(void); #endif /* __gu_fnv_test__ */ percona-xtradb-cluster-galera/galerautils/tests/gu_hash_test.c0000644000000000000000000001777712247075736025174 0ustar rootroot00000000000000// Copyright (C) 2012 Codership Oy /* * This unit test is mostly to check that Galera hash definitions didn't change: * correctness of hash algorithms definitions is checked in respective unit * tests. * * By convention checks are made against etalon byte arrays, so integers must be * converted to little-endian. * * $Id: gu_hash_test.c 2822 2012-06-20 19:59:58Z alex $ */ #include "gu_hash_test.h" #include "../src/gu_hash.h" #include "../src/gu_log.h" #include "../src/gu_print_buf.h" /* checks equivalence of two buffers, returns true if check fails and logs * buffer contents. */ static bool check (const void* const exp, const void* const got, ssize_t size) { if (memcmp (exp, got, size)) { ssize_t str_size = size * 2.2 + 1; char c[str_size], r[str_size]; gu_print_buf (exp, size, c, sizeof(c), false); gu_print_buf (got, size, r, sizeof(r), false); gu_info ("expected hash value:\n%s\nfound:\n%s\n", c, r); return true; } return false; } static const char test_msg[2048] = { 0, }; #define GU_HASH_TEST_LENGTH 43 /* some random prime */ static const uint8_t gu_hash128_check[16] = { 0xFA,0x2C,0x78,0x67,0x35,0x99,0xD9,0x84,0x73,0x41,0x3F,0xA5,0xEB,0x27,0x40,0x2F }; static const uint8_t gu_hash64_check[8] = { 0xFA,0x2C,0x78,0x67,0x35,0x99,0xD9,0x84 }; static const uint8_t gu_hash32_check[4] = { 0xFA,0x2C,0x78,0x67 }; /* Tests partial hashing functions */ START_TEST (gu_hash_test) { gu_hash_t h; gu_hash_init(&h); gu_hash_append(&h, test_msg, GU_HASH_TEST_LENGTH); uint8_t res128[16]; gu_hash_get128 (&h, res128); fail_if (check (gu_hash128_check, res128, sizeof(res128)), "gu_hash_get128() failed."); uint64_t res64 = gu_hash_get64(&h); fail_if (gu_hash64(test_msg, GU_HASH_TEST_LENGTH) != res64); res64 = gu_le64(res64); fail_if (check (gu_hash64_check, &res64, sizeof(res64)), "gu_hash_get64() failed."); uint32_t res32 = gu_hash_get32(&h); fail_if (gu_hash32(test_msg, GU_HASH_TEST_LENGTH) != res32); res32 = gu_le32(res32); fail_if (check (gu_hash32_check, &res32, sizeof(res32)), "gu_hash_get32() failed."); } END_TEST static const uint8_t fast_hash128_check0 [16] = { 0xA9,0xCE,0x5A,0x56,0x0C,0x0B,0xF7,0xD6,0x63,0x4F,0x6F,0x81,0x0E,0x0B,0xF2,0x0A }; static const uint8_t fast_hash128_check511 [16] = { 0xC6,0x7F,0x4C,0xE7,0x6F,0xE0,0xDA,0x14,0xCC,0x9F,0x21,0x76,0xAF,0xB5,0x12,0x1A }; static const uint8_t fast_hash128_check512 [16] = { 0x38,0x8D,0x2B,0x90,0xC8,0x7F,0x11,0x53,0x3F,0xB4,0x32,0xC1,0xD7,0x2B,0x04,0x39 }; static const uint8_t fast_hash128_check2011[16] = { 0xB7,0xCE,0x75,0xC7,0xB4,0x31,0xBC,0xC8,0x95,0xB3,0x41,0xB8,0x5B,0x8E,0x77,0xF9 }; static const uint8_t fast_hash64_check0 [8] = { 0x6C, 0x55, 0xB8, 0xA1, 0x02, 0xC6, 0x21, 0xCA }; static const uint8_t fast_hash64_check15 [8] = { 0x28, 0x49, 0xE8, 0x34, 0x7A, 0xAB, 0x49, 0x34 }; static const uint8_t fast_hash64_check16 [8] = { 0x44, 0x40, 0x2C, 0x82, 0xD3, 0x8D, 0xAA, 0xFE }; static const uint8_t fast_hash64_check511 [8] = { 0xC6, 0x7F, 0x4C, 0xE7, 0x6F, 0xE0, 0xDA, 0x14 }; static const uint8_t fast_hash64_check512 [8] = { 0x38, 0x8D, 0x2B, 0x90, 0xC8, 0x7F, 0x11, 0x53 }; static const uint8_t fast_hash64_check2011[8] = { 0xB7, 0xCE, 0x75, 0xC7, 0xB4, 0x31, 0xBC, 0xC8 }; static const uint8_t fast_hash32_check0 [4] = { 0x0B, 0x7C, 0x3E, 0xAB }; static const uint8_t fast_hash32_check31 [4] = { 0x1E, 0xFF, 0x48, 0x38 }; static const uint8_t fast_hash32_check32 [4] = { 0x63, 0xC2, 0x53, 0x0D }; static const uint8_t fast_hash32_check511 [4] = { 0xC6, 0x7F, 0x4C, 0xE7 }; static const uint8_t fast_hash32_check512 [4] = { 0x38, 0x8D, 0x2B, 0x90 }; static const uint8_t fast_hash32_check2011[4] = { 0xB7, 0xCE, 0x75, 0xC7 }; /* Tests fast hash functions */ START_TEST (gu_fast_hash_test) { uint8_t res128[16]; gu_fast_hash128 (test_msg, 0, res128); fail_if (check (fast_hash128_check0, res128, sizeof(res128))); gu_fast_hash128 (test_msg, 511, res128); fail_if (check (fast_hash128_check511, res128, sizeof(res128))); gu_fast_hash128 (test_msg, 512, res128); fail_if (check (fast_hash128_check512, res128, sizeof(res128))); gu_fast_hash128 (test_msg, 2011, res128); fail_if (check (fast_hash128_check2011, res128, sizeof(res128))); uint64_t res64; res64 = gu_fast_hash64 (test_msg, 0); res64 = gu_le64(res64); fail_if (check (fast_hash64_check0, &res64, sizeof(res64))); res64 = gu_fast_hash64 (test_msg, 15); res64 = gu_le64(res64); fail_if (check (fast_hash64_check15, &res64, sizeof(res64))); res64 = gu_fast_hash64 (test_msg, 16); res64 = gu_le64(res64); fail_if (check (fast_hash64_check16, &res64, sizeof(res64))); res64 = gu_fast_hash64 (test_msg, 511); res64 = gu_le64(res64); fail_if (check (fast_hash64_check511, &res64, sizeof(res64))); res64 = gu_fast_hash64 (test_msg, 512); res64 = gu_le64(res64); fail_if (check (fast_hash64_check512, &res64, sizeof(res64))); res64 = gu_fast_hash64 (test_msg, 2011); res64 = gu_le64(res64); fail_if (check (fast_hash64_check2011, &res64, sizeof(res64))); uint32_t res32; res32 = gu_fast_hash32 (test_msg, 0); res32 = gu_le32(res32); fail_if (check (fast_hash32_check0, &res32, sizeof(res32))); res32 = gu_fast_hash32 (test_msg, 31); res32 = gu_le32(res32); fail_if (check (fast_hash32_check31, &res32, sizeof(res32))); res32 = gu_fast_hash32 (test_msg, 32); res32 = gu_le32(res32); fail_if (check (fast_hash32_check32, &res32, sizeof(res32))); res32 = gu_fast_hash32 (test_msg, 511); res32 = gu_le32(res32); fail_if (check (fast_hash32_check511, &res32, sizeof(res32))); res32 = gu_fast_hash32 (test_msg, 512); res32 = gu_le32(res32); fail_if (check (fast_hash32_check512, &res32, sizeof(res32))); res32 = gu_fast_hash32 (test_msg, 2011); res32 = gu_le32(res32); fail_if (check (fast_hash32_check2011, &res32, sizeof(res32))); } END_TEST /* Tests table hash functions: * - for 64-bit platforms table hash should be identical to fast 64-bit hash, * - for 32-bit platforms table hash is different. */ #if GU_WORDSIZE == 64 START_TEST (gu_table_hash_test) { size_t res; fail_if (sizeof(res) > 8); res = gu_table_hash (test_msg, 0); res = gu_le64(res); fail_if (check (fast_hash64_check0, &res, sizeof(res))); res = gu_table_hash (test_msg, 15); res = gu_le64(res); fail_if (check (fast_hash64_check15, &res, sizeof(res))); res = gu_table_hash (test_msg, 16); res = gu_le64(res); fail_if (check (fast_hash64_check16, &res, sizeof(res))); res = gu_table_hash (test_msg, 511); res = gu_le64(res); fail_if (check (fast_hash64_check511, &res, sizeof(res))); res = gu_table_hash (test_msg, 512); res = gu_le64(res); fail_if (check (fast_hash64_check512, &res, sizeof(res))); res = gu_table_hash (test_msg, 2011); res = gu_le64(res); fail_if (check (fast_hash64_check2011, &res, sizeof(res))); } END_TEST #elif GU_WORDSIZE == 32 static const uint8_t table_hash32_check0 [4] = { 0x0B, 0x7C, 0x3E, 0xAB }; static const uint8_t table_hash32_check32 [4] = { 0x65, 0x16, 0x17, 0x42 }; static const uint8_t table_hash32_check2011[4] = { 0xF9, 0xBC, 0xEF, 0x7A }; START_TEST (gu_table_hash_test) { size_t res; fail_if (sizeof(res) > 4); res = gu_table_hash (test_msg, 0); res = gu_le32(res); fail_if (check (table_hash32_check0, &res, sizeof(res))); res = gu_table_hash (test_msg, 32); res = gu_le32(res); fail_if (check (table_hash32_check32, &res, sizeof(res))); res = gu_table_hash (test_msg, 2011); res = gu_le32(res); fail_if (check (table_hash32_check2011, &res, sizeof(res))); } END_TEST #else /* GU_WORDSIZE == 32 */ # error "Unsupported word size" #endif Suite *gu_hash_suite(void) { Suite *s = suite_create("Galera hash"); TCase *tc = tcase_create("gu_hash"); suite_add_tcase (s, tc); tcase_add_test (tc, gu_hash_test); tcase_add_test (tc, gu_fast_hash_test); tcase_add_test (tc, gu_table_hash_test); return s; } percona-xtradb-cluster-galera/galerautils/tests/gu_hash_test.h0000644000000000000000000000037312247075736025161 0ustar rootroot00000000000000// Copyright (C) 2012 Codership Oy // $Id: gu_hash_test.h 2806 2012-06-06 17:09:29Z alex $ #ifndef __gu_hash_test__ #define __gu_hash_test__ #include extern Suite *gu_hash_suite(void); #endif /* __gu_hash_test__ */ percona-xtradb-cluster-galera/galerautils/tests/gu_lock_step_test.c0000644000000000000000000000712212247075736026213 0ustar rootroot00000000000000/* * Copyright (C) 2008-2010 Codership Oy * * $Id: gu_lock_step_test.c 3336 2013-10-28 07:41:56Z teemu $ */ #include #include // usleep() #include // strerror() #include "../src/gu_log.h" #include "../src/gu_lock_step.h" #include "gu_lock_step_test.h" #define TEST_USLEEP 1000 // 1ms #define WAIT_FOR(cond) \ { int count = 1000; while (--count && !(cond)) { usleep (TEST_USLEEP); }} gu_lock_step_t LS; static void* lock_step_thread (void* arg) { gu_lock_step_wait (&LS); return NULL; } START_TEST (gu_lock_step_test) { const long timeout = 500; // 500 ms long ret; gu_thread_t thr1, thr2; gu_lock_step_init (&LS); fail_if (LS.wait != 0); fail_if (LS.enabled != false); // first try with lock-stepping disabled ret = gu_thread_create (&thr1, NULL, lock_step_thread, NULL); fail_if (ret != 0); WAIT_FOR(0 == LS.wait); // 10ms fail_if (LS.wait != 0); // by default lock-step is disabled ret = gu_thread_join (thr1, NULL); fail_if (ret != 0, "gu_thread_join() failed: %ld (%s)", ret, strerror(ret)); ret = gu_lock_step_cont (&LS, timeout); fail_if (-1 != ret); // enable lock-step gu_lock_step_enable (&LS, true); fail_if (LS.enabled != true); ret = gu_lock_step_cont (&LS, timeout); fail_if (0 != ret); // nobody's waiting ret = gu_thread_create (&thr1, NULL, lock_step_thread, NULL); fail_if (ret != 0); WAIT_FOR(1 == LS.wait); // 10ms fail_if (LS.wait != 1); ret = gu_thread_create (&thr2, NULL, lock_step_thread, NULL); fail_if (ret != 0); WAIT_FOR(2 == LS.wait); // 10ms fail_if (LS.wait != 2); ret = gu_lock_step_cont (&LS, timeout); fail_if (ret != 2); // there were 2 waiters fail_if (LS.wait != 1); // 1 waiter remains ret = gu_lock_step_cont (&LS, timeout); fail_if (ret != 1); fail_if (LS.wait != 0); // 0 waiters remain ret = gu_thread_join (thr1, NULL); fail_if (ret != 0, "gu_thread_join() failed: %ld (%s)", ret, strerror(ret)); ret = gu_thread_join (thr2, NULL); fail_if (ret != 0, "gu_thread_join() failed: %ld (%s)", ret, strerror(ret)); ret = gu_lock_step_cont (&LS, timeout); fail_if (ret != 0); // there were 0 waiters fail_if (LS.wait != 0, "Expected LS.wait to be 0, found: %ld", LS.wait); gu_lock_step_destroy (&LS); } END_TEST #define RACE_ITERATIONS 1000 static void* lock_step_race (void* arg) { long i; for (i = 0; i < RACE_ITERATIONS; i++) gu_lock_step_wait (&LS); return NULL; } START_TEST (gu_lock_step_race) { const long timeout = 500; // 500 ms long ret, i; gu_thread_t thr1; gu_lock_step_init (&LS); gu_lock_step_enable (&LS, true); fail_if (LS.enabled != true); ret = gu_thread_create (&thr1, NULL, lock_step_race, NULL); fail_if (ret != 0); for (i = 0; i < RACE_ITERATIONS; i++) { ret = gu_lock_step_cont (&LS, timeout); fail_if (ret != 1, "No waiter at iteration: %ld", i); } fail_if (LS.wait != 0); // 0 waiters remain ret = gu_thread_join (thr1, NULL); fail_if (ret != 0, "gu_thread_join() failed: %ld (%s)", ret, strerror(ret)); ret = gu_lock_step_cont (&LS, timeout); fail_if (ret != 0); } END_TEST Suite *gu_lock_step_suite(void) { Suite *suite = suite_create("Galera LOCK_STEP utils"); TCase *tcase = tcase_create("gu_lock_step"); suite_add_tcase (suite, tcase); tcase_add_test (tcase, gu_lock_step_test); tcase_add_test (tcase, gu_lock_step_race); return suite; } percona-xtradb-cluster-galera/galerautils/tests/gu_lock_step_test.h0000644000000000000000000000041212247075736026213 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gu_lock_step_test.h 3336 2013-10-28 07:41:56Z teemu $ */ #ifndef __gu_lock_step_test__ #define __gu_lock_step_test__ extern Suite *gu_lock_step_suite(void); #endif /* __gu_lock_step_test__ */ percona-xtradb-cluster-galera/galerautils/tests/gu_mem_test.c0000644000000000000000000000357312247075736025014 0ustar rootroot00000000000000// Copyright (C) 2007 Codership Oy // $Id: gu_mem_test.c 248 2008-03-23 16:32:00Z alex $ #define DEBUG_MALLOC // turn on the debugging code #define TEST_SIZE 1024 #include #include #include #include #include "gu_mem_test.h" #include "../src/galerautils.h" START_TEST (gu_mem_test) { void* ptr1; void* ptr2; int res; int i; ptr1 = gu_malloc (0); fail_if (NULL != ptr1, "Zero memory allocated, non-NULL pointer returned"); mark_point(); ptr1 = gu_malloc (TEST_SIZE); fail_if (NULL == ptr1, "NULL pointer returned for allocation" " errno: %s", strerror (errno)); mark_point(); ptr2 = memset (ptr1, 0xab, TEST_SIZE); fail_if (ptr2 != ptr1, "Memset changed pointer"); ptr2 = NULL; mark_point(); ptr2 = gu_realloc (ptr2, TEST_SIZE); fail_if (NULL == ptr2, "NULL pointer returned for reallocation" " errno: %s", strerror (errno)); memcpy (ptr2, ptr1, TEST_SIZE); mark_point(); ptr1 = gu_realloc (ptr1, TEST_SIZE + TEST_SIZE); res = memcmp (ptr1, ptr2, TEST_SIZE); fail_if (res != 0, "Realloc changed the contents of the memory"); mark_point(); ptr1 = gu_realloc (ptr1, 0); fail_if (res != 0, "Realloc to 0 didn't return NULL"); mark_point(); ptr1 = gu_calloc (1, TEST_SIZE); fail_if (NULL == ptr1, "NULL pointer returned for allocation" " errno: %s", strerror (errno)); for (i = 0; i < TEST_SIZE; i++) { res = ((char*)ptr1)[i]; if (res != 0) break; } fail_if (res != 0, "Calloc didn't clear up the memory"); mark_point(); gu_free (ptr1); mark_point(); gu_free (ptr2); } END_TEST Suite *gu_mem_suite(void) { Suite *s = suite_create("Galera memory utils"); TCase *tc_mem = tcase_create("gu_mem"); suite_add_tcase (s, tc_mem); tcase_add_test(tc_mem, gu_mem_test); return s; } percona-xtradb-cluster-galera/galerautils/tests/gu_mem_test.h0000644000000000000000000000034212247075736025010 0ustar rootroot00000000000000// Copyright (C) 2007 Codership Oy // $Id: gu_mem_test.h 1036 2009-09-22 12:18:55Z alex $ #ifndef __gu_mem_test__ #define __gu_mem_test__ extern Suite *gu_mem_suite(void); #endif /* __gu_mem_test__ */ percona-xtradb-cluster-galera/galerautils/tests/gu_mmh3_test.c0000644000000000000000000002200012247075736025064 0ustar rootroot00000000000000// Copyright (C) 2012 Codership Oy // $Id: gu_mmh3_test.c 2822 2012-06-20 19:59:58Z alex $ #include "gu_mmh3_test.h" #include "../src/gu_mmh3.h" #include "../src/gu_log.h" #include "../src/gu_print_buf.h" /* This is to verify all tails plus block + all tails. Max block is 16 bytes */ static const char const test_input[] = "0123456789ABCDEF0123456789abcde"; typedef struct hash32 { uint8_t h[4]; } hash32_t; #define NUM_32_TESTS 8 /* 0 to 7 bytes */ static const hash32_t const test_output32[NUM_32_TESTS] = { {{ 0x0b, 0x7c, 0x3e, 0xab }}, /* '' */ {{ 0xba, 0xeb, 0x75, 0x97 }}, /* '0' */ {{ 0x5d, 0x5c, 0x21, 0x60 }}, /* '01' */ {{ 0x4b, 0xff, 0x61, 0x41 }}, /* '012' */ {{ 0x35, 0x3b, 0x57, 0xca }}, /* '0123' */ {{ 0x09, 0xdd, 0x77, 0xf9 }}, /* '01234' */ {{ 0x1f, 0x3c, 0x29, 0x7b }}, /* '012345' */ {{ 0xe1, 0xbe, 0x2d, 0xce }} /* '0123456' */ }; typedef struct hash128 { uint8_t h[16]; } hash128_t; #define NUM_128_TESTS 32 /* 0 to 31 bytes */ static const hash128_t const test_output128[NUM_128_TESTS] = { {{ 0xa9,0xce,0x5a,0x56,0x0c,0x0b,0xf7,0xd6,0x63,0x4f,0x6f,0x81,0x0e,0x0b,0xf2,0x0a }}, {{ 0x72,0xa1,0x46,0xa3,0x73,0x03,0x49,0x85,0x30,0xb9,0x52,0xaa,0x3b,0x00,0xad,0x23 }}, {{ 0x4f,0x32,0xa2,0x15,0x91,0x00,0xea,0xaa,0x59,0x90,0x48,0x30,0xe5,0x86,0x50,0xee }}, {{ 0x55,0xfe,0x86,0x3b,0x9c,0x67,0xc6,0xee,0x5c,0x06,0x34,0xd0,0xe5,0x15,0xfb,0xdd }}, {{ 0x3a,0x50,0x35,0xe5,0x72,0x75,0xa5,0x5e,0x46,0x3d,0x0e,0x23,0xbb,0x17,0x5a,0x66 }}, {{ 0x3b,0xff,0xb5,0x1a,0x93,0x0c,0x77,0x9a,0x40,0x5f,0x62,0x0c,0x40,0x15,0x0b,0x6e }}, {{ 0x7c,0xf8,0xf9,0xd2,0xfa,0x5a,0x8b,0x51,0x65,0x3c,0xa5,0x0e,0xa2,0xca,0x0a,0x87 }}, {{ 0x95,0x69,0x33,0x98,0xe4,0xb2,0x2a,0x21,0xd4,0x23,0x21,0x80,0xb1,0x00,0x46,0xbb }}, {{ 0x92,0xca,0xd3,0xbb,0x39,0x16,0x96,0xb5,0x3a,0x61,0x58,0x53,0xbb,0xf8,0xc4,0xb0 }}, {{ 0x36,0xf0,0xa3,0xc8,0xdc,0x5e,0x46,0x20,0x12,0xcf,0xad,0x3f,0xda,0xd5,0x95,0x7a }}, {{ 0xb9,0x71,0x76,0x54,0xd3,0x74,0x9b,0x31,0x93,0xb2,0xd9,0xbf,0xad,0x78,0x49,0x7e }}, {{ 0x39,0x75,0xc6,0x34,0x38,0x65,0x60,0x32,0xb1,0xa3,0x02,0xd2,0xba,0x47,0x0b,0xc3 }}, {{ 0x37,0xcd,0xe3,0x34,0x7d,0x2d,0xa4,0xdc,0xf3,0x51,0xd1,0x1e,0x46,0xb8,0x1a,0xd4 }}, {{ 0xa0,0xf6,0xff,0xc6,0xcd,0x50,0xdf,0xa2,0x59,0x36,0x8d,0xdf,0x09,0x57,0x14,0x7b }}, {{ 0xeb,0x58,0x42,0xca,0x56,0xb5,0x94,0x16,0x10,0x86,0x38,0x5b,0x2c,0x4a,0x13,0x84 }}, {{ 0x5d,0xee,0x3a,0x5b,0x45,0x5f,0x92,0x7d,0x42,0x91,0x8a,0x7b,0xb6,0xc7,0xde,0xd9 }}, {{ 0x63,0xff,0xe5,0x55,0x38,0x3d,0xd6,0x5d,0xa4,0xad,0xcb,0xf6,0x0a,0xc3,0xd9,0x12 }}, {{ 0x86,0x15,0xd3,0x5a,0x47,0x81,0x3f,0xea,0x6b,0xbc,0x3b,0x82,0xd0,0x49,0xda,0x5d }}, {{ 0xb7,0x41,0xc9,0xf5,0x94,0x3f,0x91,0xa5,0x56,0x68,0x9c,0x12,0xc7,0xa1,0xd9,0x45 }}, {{ 0xb7,0x7c,0x2f,0x60,0xe3,0x2b,0x6a,0xd6,0x5e,0x24,0x6c,0xaf,0x8c,0x83,0x99,0xc7 }}, {{ 0x62,0xdb,0xad,0xab,0xda,0x51,0x82,0x0b,0x04,0xe6,0x7a,0x88,0xaa,0xae,0xfd,0xce }}, {{ 0x70,0x89,0xd2,0x6a,0x35,0x80,0x19,0xa4,0x71,0x0e,0x5c,0x68,0x33,0xf5,0x0c,0x67 }}, {{ 0x05,0xb3,0x50,0x50,0xbe,0x8d,0xaa,0x6e,0x32,0x02,0x1b,0x5e,0xe6,0xb7,0x5f,0x72 }}, {{ 0x85,0x60,0x7c,0x7a,0xdf,0xaa,0x67,0xc6,0xed,0x3e,0x7e,0x13,0x84,0x2c,0xd4,0x28 }}, {{ 0x51,0x4a,0xe3,0x56,0xe0,0x5f,0x7d,0x42,0xfb,0x41,0xec,0xfe,0xff,0xa4,0x74,0x13 }}, {{ 0xb8,0xc0,0xc1,0x01,0xc2,0x74,0xbb,0x84,0xc8,0xca,0x16,0x9c,0x6b,0xf3,0x3e,0x4d }}, {{ 0xab,0xd0,0x4a,0xc5,0xa4,0xc8,0xce,0xf4,0xf2,0xf5,0x2f,0xdc,0x22,0x4f,0x20,0xda }}, {{ 0x36,0x25,0x28,0x74,0xf0,0x4c,0x36,0x38,0xd2,0x9a,0x64,0xf8,0x11,0xcf,0xaf,0x28 }}, {{ 0x8b,0x79,0x18,0x09,0x14,0x19,0x3c,0xa0,0x5b,0x62,0x4d,0x09,0x18,0xdd,0x6a,0x89 }}, {{ 0xc0,0xae,0x4f,0x67,0x45,0x01,0x00,0xb7,0x75,0xc5,0x1c,0x56,0xdf,0x55,0x7c,0x04 }}, {{ 0xcd,0x5a,0xda,0xea,0xbc,0xfb,0x8d,0xc7,0x8a,0xd3,0xc6,0x70,0x12,0x34,0x82,0x84 }}, {{ 0x69,0x53,0x0d,0xc3,0x4d,0xd4,0x33,0xe9,0x00,0x1b,0x27,0x06,0x27,0x7f,0x48,0xf7 }} }; typedef void (*hash_f_t) (const void* key, int len, uint32_t seed, void* out); /* Verification code from the original SMHasher test suite */ static void smhasher_verification (hash_f_t hash, size_t const hashbytes, uint32_t* const res) { ssize_t const n_tests = 256; uint8_t key[n_tests]; uint8_t hashes[hashbytes * n_tests]; uint8_t final[hashbytes]; /* Hash keys of the form {0}, {0,1}, {0,1,2}... up to N=255,using 256-N as * the seed */ ssize_t i; for(i = 0; i < n_tests; i++) { key[i] = (uint8_t)i; hash (key, i, n_tests - i, &hashes[i * hashbytes]); } /* Then hash the result array */ hash (hashes, hashbytes * n_tests, 0, final); memcpy (res, final, sizeof(*res)); } static hash32_t smhasher_checks[3] = { {{ 0xE3, 0x7E, 0xF5, 0xB0 }}, /* mmh3_32 */ {{ 0x2A, 0xE6, 0xEC, 0xB3 }}, /* mmh3_x86_128 */ {{ 0x69, 0xBA, 0x84, 0x63 }} /* mmh3_x64_128 */ }; /* returns true if check fails */ static bool check (const void* const exp, const void* const got, ssize_t size) { if (memcmp (exp, got, size)) { ssize_t str_size = size * 2.2 + 1; char c[str_size], r[str_size]; gu_print_buf (exp, size, c, sizeof(c), false); gu_print_buf (got, size, r, sizeof(r), false); gu_info ("expected MurmurHash3:\n%s\nfound:\n%s\n", c, r); return true; } return false; } START_TEST (gu_mmh32_test) { int i; uint32_t out; smhasher_verification (gu_mmh3_32, sizeof(out), &out); fail_if (check (&smhasher_checks[0], &out, sizeof(out)), "gu_mmh3_32 failed."); for (i = 0; i < NUM_32_TESTS; i++) { uint32_t res = gu_mmh32 (test_input, i); res = gu_le32(res); fail_if(check (&test_output32[i], &res, sizeof(res)), "gu_mmh32() failed at step %d",i); } } END_TEST #if 0 /* x86 variant is faulty and unsuitable for short keys, ignore */ START_TEST (gu_mmh128_x86_test) { int i; uint32_t out32; smhasher_verification (gu_mmh3_x86_128, sizeof(hash128_t), &out32); fail_if (check (&smhasher_checks[1], &out32, sizeof(out32)), "gu_mmh3_x86_128 failed."); for (i = 0; i < NUM_128_TESTS; i++) { hash128_t out; gu_mmh3_x86_128 (test_input, i, GU_MMH32_SEED, &out); check (&test_output128[i], &out, sizeof(out)); } } END_TEST #endif /* 0 */ START_TEST (gu_mmh128_x64_test) { int i; uint32_t out32; smhasher_verification (gu_mmh3_x64_128, sizeof(hash128_t), &out32); fail_if (check (&smhasher_checks[2], &out32, sizeof(out32)), "gu_mmh3_x64_128 failed."); for (i = 0; i < NUM_128_TESTS; i++) { hash128_t out; gu_mmh128 (test_input, i, &out); fail_if(check (&test_output128[i], &out, sizeof(out)), "gu_mmh128() failed at step %d", i); } } END_TEST /* Tests partial hashing functions */ START_TEST (gu_mmh128_partial) { hash128_t part; gu_mmh128_ctx_t ctx; gu_mmh128_init (&ctx); gu_mmh128_append (&ctx, test_input, 31); gu_mmh128_get (&ctx, &part); fail_if(check (&test_output128[31], &part, sizeof(part)), "gu_mmh128_get() failed at one go"); gu_mmh128_init (&ctx); gu_mmh128_get (&ctx, &part); fail_if(check (&test_output128[0], &part, sizeof(part)), "gu_mmh128_get() failed at init"); gu_mmh128_append (&ctx, test_input + 0, 0); gu_mmh128_get (&ctx, &part); fail_if(check (&test_output128[0], &part, sizeof(part)), "gu_mmh128_get() failed at length %d", 0); gu_mmh128_append (&ctx, test_input + 0, 1); gu_mmh128_get (&ctx, &part); fail_if(check (&test_output128[1], &part, sizeof(part)), "gu_mmh128_get() failed at length %d", 1); gu_mmh128_append (&ctx, test_input + 1, 2); gu_mmh128_get (&ctx, &part); fail_if(check (&test_output128[3], &part, sizeof(part)), "gu_mmh128_get() failed at length %d", 3); gu_mmh128_get (&ctx, &part); fail_if(check (&test_output128[3], &part, sizeof(part)), "gu_mmh128_get() failed at length %d again", 3); gu_mmh128_append (&ctx, test_input + 3, 20); gu_mmh128_get (&ctx, &part); fail_if(check (&test_output128[23], &part, sizeof(part)), "gu_mmh128_get() failed at length %d", 23); gu_mmh128_append (&ctx, test_input + 23, 0); gu_mmh128_get (&ctx, &part); fail_if(check (&test_output128[23], &part, sizeof(part)), "gu_mmh128_get() failed at length %d again", 23); gu_mmh128_append (&ctx, test_input + 23, 3); gu_mmh128_append (&ctx, test_input + 26, 3); gu_mmh128_append (&ctx, test_input + 29, 2); gu_mmh128_get (&ctx, &part); fail_if(check (&test_output128[31], &part, sizeof(part)), "gu_mmh128_get() failed at length %d", 31); } END_TEST Suite *gu_mmh3_suite(void) { Suite *s = suite_create("MurmurHash3"); TCase *tc = tcase_create("gu_mmh3"); suite_add_tcase (s, tc); tcase_add_test (tc, gu_mmh32_test); // tcase_add_test (tc, gu_mmh128_x86_test); tcase_add_test (tc, gu_mmh128_x64_test); tcase_add_test (tc, gu_mmh128_partial); return s; } percona-xtradb-cluster-galera/galerautils/tests/gu_mmh3_test.h0000644000000000000000000000037312247075736025102 0ustar rootroot00000000000000// Copyright (C) 2012 Codership Oy // $Id: gu_mmh3_test.h 2794 2012-05-31 18:54:29Z alex $ #ifndef __gu_mmh3_test__ #define __gu_mmh3_test__ #include extern Suite *gu_mmh3_suite(void); #endif /* __gu_mmh3_test__ */ percona-xtradb-cluster-galera/galerautils/tests/gu_net_test.cpp0000644000000000000000000000377412247075736025367 0ustar rootroot00000000000000// Copyright (C) 2009 Codership Oy #include #include #include #include #include #include #include #include #include "gu_logger.hpp" #include "gu_uri.hpp" #include "gu_resolver.hpp" #include "gu_lock.hpp" #include "gu_prodcons.hpp" #include "gu_net_test.hpp" using std::vector; using std::string; using std::deque; using std::mem_fun; using std::for_each; using namespace gu; using namespace gu::net; using namespace gu::prodcons; START_TEST(test_resolver) { std::string tcp_lh4("tcp://127.0.0.1:2002"); Addrinfo tcp_lh4_ai(resolve(tcp_lh4)); fail_unless(tcp_lh4_ai.get_family() == AF_INET); fail_unless(tcp_lh4_ai.get_socktype() == SOCK_STREAM); fail_unless(tcp_lh4_ai.to_string() == tcp_lh4, "%s != %s", tcp_lh4_ai.to_string().c_str(), tcp_lh4.c_str()); std::string tcp_lh6("tcp://[::1]:2002"); Addrinfo tcp_lh6_ai(resolve(tcp_lh6)); fail_unless(tcp_lh6_ai.get_family() == AF_INET6); fail_unless(tcp_lh6_ai.get_socktype() == SOCK_STREAM); fail_unless(tcp_lh6_ai.to_string() == tcp_lh6, "%s != %s", tcp_lh6_ai.to_string().c_str(), tcp_lh6.c_str()); std::string lh("tcp://localhost:2002"); Addrinfo lh_ai(resolve(lh)); fail_unless(lh_ai.to_string() == "tcp://127.0.0.1:2002" || lh_ai.to_string() == "tcp://[::1]:2002"); } END_TEST START_TEST(trac_288) { try { string url("tcp://do-not-resolve:0"); (void)resolve(url); } catch (Exception& e) { log_debug << "exception was " << e.what(); } } END_TEST Suite* gu_net_suite() { Suite* s = suite_create("galerautils++ Networking"); TCase* tc; tc = tcase_create("test_resolver"); tcase_add_test(tc, test_resolver); suite_add_tcase(s, tc); tc = tcase_create("trac_288"); tcase_add_test(tc, trac_288); #if 0 /* bogus test, commenting out for now */ suite_add_tcase(s, tc); #endif return s; } percona-xtradb-cluster-galera/galerautils/tests/gu_net_test.hpp0000644000000000000000000000037112247075736025362 0ustar rootroot00000000000000// Copyright (C) 2007 Codership Oy // $Id: gu_net_test.hpp 3336 2013-10-28 07:41:56Z teemu $ #ifndef __gu_net_test__ #define __gu_net_test__ #include extern Suite *gu_net_suite(void); #endif /* __gu_net_test__ */ percona-xtradb-cluster-galera/galerautils/tests/gu_spooky_test.c0000644000000000000000000001751412247075736025562 0ustar rootroot00000000000000// Copyright (C) 2012 Codership Oy /*! * Original Bob Jenkins' test implementation: * http://www.burtleburtle.net/bob/c/testspooky.cpp * * $Id: gu_spooky_test.c 2821 2012-06-20 18:42:43Z alex $ */ #include "gu_spooky_test.h" #include "../src/gu_spooky.h" #include "../src/gu_print_buf.h" #define BUFSIZE 512 static uint64_t const expected[BUFSIZE] = { 0xa24295ec, 0xfe3a05ce, 0x257fd8ef, 0x3acd5217, 0xfdccf85c, 0xc7b5f143, 0x3b0c3ff0, 0x5220f13c, 0xa6426724, 0x4d5426b4, 0x43e76b26, 0x051bc437, 0xd8f28a02, 0x23ccc30e, 0x811d1a2d, 0x039128d4, 0x9cd96a73, 0x216e6a8d, 0x97293fe8, 0xe4fc6d09, 0x1ad34423, 0x9722d7e4, 0x5a6fdeca, 0x3c94a7e1, 0x81a9a876, 0xae3f7c0e, 0x624b50ee, 0x875e5771, 0x0095ab74, 0x1a7333fb, 0x056a4221, 0xa38351fa, 0x73f575f1, 0x8fded05b, 0x9097138f, 0xbd74620c, 0x62d3f5f2, 0x07b78bd0, 0xbafdd81e, 0x0638f2ff, 0x1f6e3aeb, 0xa7786473, 0x71700e1d, 0x6b4625ab, 0xf02867e1, 0xb2b2408f, 0x9ce21ce5, 0xa62baaaf, 0x26720461, 0x434813ee, 0x33bc0f14, 0xaaab098a, 0x750af488, 0xc31bf476, 0x9cecbf26, 0x94793cf3, 0xe1a27584, 0xe80c4880, 0x1299f748, 0x25e55ed2, 0x405e3feb, 0x109e2412, 0x3e55f94f, 0x59575864, 0x365c869d, 0xc9852e6a, 0x12c30c62, 0x47f5b286, 0xb47e488d, 0xa6667571, 0x78220d67, 0xa49e30b9, 0x2005ef88, 0xf6d3816d, 0x6926834b, 0xe6116805, 0x694777aa, 0x464af25b, 0x0e0e2d27, 0x0ea92eae, 0x602c2ca9, 0x1d1d79c5, 0x6364f280, 0x939ee1a4, 0x3b851bd8, 0x5bb6f19f, 0x80b9ed54, 0x3496a9f1, 0xdf815033, 0x91612339, 0x14c516d6, 0xa3f0a804, 0x5e78e975, 0xf408bcd9, 0x63d525ed, 0xa1e459c3, 0xfde303af, 0x049fc17f, 0xe7ed4489, 0xfaeefdb6, 0x2b1b2fa8, 0xc67579a6, 0x5505882e, 0xe3e1c7cb, 0xed53bf30, 0x9e628351, 0x8fa12113, 0x7500c30f, 0xde1bee00, 0xf1fefe06, 0xdc759c00, 0x4c75e5ab, 0xf889b069, 0x695bf8ae, 0x47d6600f, 0xd2a84f87, 0xa0ca82a9, 0x8d2b750c, 0xe03d8cd7, 0x581fea33, 0x969b0460, 0x36c7b7de, 0x74b3fd20, 0x2bb8bde6, 0x13b20dec, 0xa2dcee89, 0xca36229d, 0x06fdb74e, 0x6d9a982d, 0x02503496, 0xbdb4e0d9, 0xbd1f94cf, 0x6d26f82d, 0xcf5e41cd, 0x88b67b65, 0x3e1b3ee4, 0xb20e5e53, 0x1d9be438, 0xcef9c692, 0x299bd1b2, 0xb1279627, 0x210b5f3d, 0x5569bd88, 0x9652ed43, 0x7e8e0f8c, 0xdfa01085, 0xcd6d6343, 0xb8739826, 0xa52ce9a0, 0xd33ef231, 0x1b4d92c2, 0xabfa116d, 0xcdf47800, 0x3a4eefdc, 0xd01f3bcf, 0x30a32f46, 0xfb54d851, 0x06a98f67, 0xbdcd0a71, 0x21a00949, 0xfe7049c9, 0x67ef46d2, 0xa1fabcbc, 0xa4c72db4, 0x4a8a910d, 0x85a890ad, 0xc37e9454, 0xfc3d034a, 0x6f46cc52, 0x742be7a8, 0xe94ecbc5, 0x5f993659, 0x98270309, 0x8d1adae9, 0xea6e035e, 0x293d5fae, 0x669955b3, 0x5afe23b5, 0x4c74efbf, 0x98106505, 0xfbe09627, 0x3c00e8df, 0x5b03975d, 0x78edc83c, 0x117c49c6, 0x66cdfc73, 0xfa55c94f, 0x5bf285fe, 0x2db49b7d, 0xfbfeb8f0, 0xb7631bab, 0x837849f3, 0xf77f3ae5, 0x6e5db9bc, 0xfdd76f15, 0x545abf92, 0x8b538102, 0xdd5c9b65, 0xa5adfd55, 0xecbd7bc5, 0x9f99ebdd, 0x67500dcb, 0xf5246d1f, 0x2b0c061c, 0x927a3747, 0xc77ba267, 0x6da9f855, 0x6240d41a, 0xe9d1701d, 0xc69f0c55, 0x2c2c37cf, 0x12d82191, 0x47be40d3, 0x165b35cd, 0xb7db42e1, 0x358786e4, 0x84b8fc4e, 0x92f57c28, 0xf9c8bbd7, 0xab95a33d, 0x11009238, 0xe9770420, 0xd6967e2a, 0x97c1589f, 0x2ee7e7d3, 0x32cc86da, 0xe47767d1, 0x73e9b61e, 0xd35bac45, 0x835a62bb, 0x5d9217b0, 0x43f3f0ed, 0x8a97911e, 0x4ec7eb55, 0x4b5a988c, 0xb9056683, 0x45456f97, 0x1669fe44, 0xafb861b8, 0x8e83a19c, 0x0bab08d6, 0xe6a145a9, 0xc31e5fc2, 0x27621f4c, 0x795692fa, 0xb5e33ab9, 0x1bc786b6, 0x45d1c106, 0x986531c9, 0x40c9a0ec, 0xff0fdf84, 0xa7359a42, 0xfd1c2091, 0xf73463d4, 0x51b0d635, 0x1d602fb4, 0xc56b69b7, 0x6909d3f7, 0xa04d68f4, 0x8d1001a7, 0x8ecace50, 0x21ec4765, 0x3530f6b0, 0x645f3644, 0x9963ef1e, 0x2b3c70d5, 0xa20c823b, 0x8d26dcae, 0x05214e0c, 0x1993896d, 0x62085a35, 0x7b620b67, 0x1dd85da2, 0x09ce9b1d, 0xd7873326, 0x063ff730, 0xf4ff3c14, 0x09a49d69, 0x532062ba, 0x03ba7729, 0xbd9a86cc, 0xe26d02a7, 0x7ccbe5d3, 0x4f662214, 0x8b999a66, 0x3d0b92b4, 0x70b210f0, 0xf5b8f16f, 0x32146d34, 0x430b92bf, 0x8ab6204c, 0x35e6e1ff, 0xc2f6c2fa, 0xa2df8a1a, 0x887413ec, 0x7cb7a69f, 0x7ac6dbe6, 0x9102d1cb, 0x8892a590, 0xc804fe3a, 0xdfc4920a, 0xfc829840, 0x8910d2eb, 0x38a210fd, 0x9d840cc9, 0x7b9c827f, 0x3444ca0c, 0x071735ab, 0x5e9088e4, 0xc995d60e, 0xbe0bb942, 0x17b089ae, 0x050e1054, 0xcf4324f7, 0x1e3e64dd, 0x436414bb, 0xc48fc2e3, 0x6b6b83d4, 0x9f6558ac, 0x781b22c5, 0x7147cfe2, 0x3c221b4d, 0xa5602765, 0x8f01a4f0, 0x2a9f14ae, 0x12158cb8, 0x28177c50, 0x1091a165, 0x39e4e4be, 0x3e451b7a, 0xd965419c, 0x52053005, 0x0798aa53, 0xe6773e13, 0x1207f671, 0xd2ef998b, 0xab88a38f, 0xc77a8482, 0xa88fb031, 0x5199e0cd, 0x01b30536, 0x46eeb0ef, 0x814259ff, 0x9789a8cf, 0x376ec5ac, 0x7087034a, 0x948b6bdd, 0x4281e628, 0x2c848370, 0xd76ce66a, 0xe9b6959e, 0x24321a8e, 0xdeddd622, 0xb890f960, 0xea26c00a, 0x55e7d8b2, 0xeab67f09, 0x9227fb08, 0xeebbed06, 0xcac1b0d1, 0xb6412083, 0x05d2b0e7, 0x9037624a, 0xc9702198, 0x2c8d1a86, 0x3e7d416e, 0xc3f1a39f, 0xf04bdce4, 0xc88cdb61, 0xbdc89587, 0x4d29b63b, 0x6f24c267, 0x4b529c87, 0x573f5a53, 0xdb3316e9, 0x288eb53b, 0xd2c074bd, 0xef44a99a, 0x2b404d2d, 0xf6706464, 0xfe824f4c, 0xc3debaf8, 0x12f44f98, 0x03135e76, 0xb4888e7f, 0xb6b2325d, 0x3a138259, 0x513c83ec, 0x2386d214, 0x94555500, 0xfbd1522d, 0xda2af018, 0x15b054c0, 0x5ad654e6, 0xb6ed00aa, 0xa2f2180e, 0x5f662825, 0xecd11366, 0x1de5e99d, 0x07afd2ad, 0xcf457b04, 0xe631e10b, 0x83ae8a21, 0x709f0d59, 0x3e278bf9, 0x246816db, 0x9f5e8fd3, 0xc5b5b5a2, 0xd54a9d5c, 0x4b6f2856, 0x2eb5a666, 0xfc68bdd4, 0x1ed1a7f8, 0x98a34b75, 0xc895ada9, 0x2907cc69, 0x87b0b455, 0xddaf96d9, 0xe7da15a6, 0x9298c82a, 0x72bd5cab, 0x2e2a6ad4, 0x7f4b6bb8, 0x525225fe, 0x985abe90, 0xac1fd6e1, 0xb8340f23, 0x92985159, 0x7d29501d, 0xe75dc744, 0x687501b4, 0x92077dc3, 0x58281a67, 0xe7e8e9be, 0xd0e64fd1, 0xb2eb0a30, 0x0e1feccd, 0xc0dc4a9e, 0x5c4aeace, 0x2ca5b93c, 0xee0ec34f, 0xad78467b, 0x0830e76e, 0x0df63f8b, 0x2c2dfd95, 0x9b41ed31, 0x9ff4cddc, 0x1590c412, 0x2366fc82, 0x7a83294f, 0x9336c4de, 0x2343823c, 0x5b681096, 0xf320e4c2, 0xc22b70e2, 0xb5fbfb2a, 0x3ebc2fed, 0x11af07bd, 0x429a08c5, 0x42bee387, 0x58629e33, 0xfb63b486, 0x52135fbe, 0xf1380e60, 0x6355de87, 0x2f0bb19a, 0x167f63ac, 0x507224cf, 0xf7c99d00, 0x71646f50, 0x74feb1ca, 0x5f9abfdd, 0x278f7d68, 0x70120cd7, 0x4281b0f2, 0xdc8ebe5c, 0x36c32163, 0x2da1e884, 0x61877598, 0xbef04402, 0x304db695, 0xfa8e9add, 0x503bac31, 0x0fe04722, 0xf0d59f47, 0xcdc5c595, 0x918c39dd, 0x0cad8d05, 0x6b3ed1eb, 0x4d43e089, 0x7ab051f8, 0xdeec371f, 0x0f4816ae, 0xf8a1a240, 0xd15317f6, 0xb8efbf0b, 0xcdd05df8, 0x4fd5633e, 0x7cf19668, 0x25d8f422, 0x72d156f2, 0x2a778502, 0xda7aefb9, 0x4f4f66e8, 0x19db6bff, 0x74e468da, 0xa754f358, 0x7339ec50, 0x139006f6, 0xefbd0b91, 0x217e9a73, 0x939bd79c }; START_TEST (gu_spooky_test) { uint8_t buf[BUFSIZE]; size_t i; for (i = 0; i < BUFSIZE; ++i) { uint32_t res; buf[i] = i+128; /* It looks like values for messages under bufSize are for the "short" * algorithm, incompatible with the real one. */ if (i < _spooky_bufSize) { /* using 128-bit version */ uint64_t h[2]; gu_spooky_short (buf, i, h); res = (uint32_t)gu_le64(h[0]); } else { /* using 32-bit version */ res = gu_spooky32 (buf, i); } if (res != expected[i]) { fail ("%d: expected: 0x%.8lX, found: 0x%.8lX", i, expected[i], res); } } } END_TEST Suite *gu_spooky_suite(void) { Suite *s = suite_create("Spooky hash"); TCase *tc = tcase_create("gu_spooky"); suite_add_tcase (s, tc); tcase_add_test (tc, gu_spooky_test); return s; } percona-xtradb-cluster-galera/galerautils/tests/gu_spooky_test.h0000644000000000000000000000040512247075736025556 0ustar rootroot00000000000000// Copyright (C) 2012 Codership Oy // $Id: gu_spooky_test.h 2799 2012-06-02 06:48:38Z alex $ #ifndef __gu_spooky_test__ #define __gu_spooky_test__ #include extern Suite *gu_spooky_suite(void); #endif /* __gu_spooky_test__ */ percona-xtradb-cluster-galera/galerautils/tests/gu_str_test.c0000644000000000000000000000472612247075736025047 0ustar rootroot00000000000000 #include "gu_str.h" #include START_TEST(test_append) { const char* strs[3] = { "t", "ttt", "tttttttt" }; char* str = NULL; size_t off = 0; size_t i; for (i = 0; i < 3; ++i) { str = gu_str_append(str, &off, strs[i], strlen(strs[i])); } free(str); } END_TEST START_TEST(test_scan) { const char* strs[5] = { "1", "234", "56789abc", "4657777777777", "345" }; char* str = NULL; size_t off = 0; size_t len = 0; size_t i; const char* ptr; for (i = 0; i < 5; ++i) { str = gu_str_append(str, &off, strs[i], strlen(strs[i])); len += strlen(strs[i]) + 1; } ptr = str; for (i = 0; i < 5; ++i) { fail_unless(strcmp(ptr, strs[i]) == 0); ptr = gu_str_next(ptr); } fail_unless(ptr == len + str); for (i = 0; i < 5; ++i) { ptr = gu_str_advance(str, i); fail_unless(strcmp(ptr, strs[i]) == 0); } free(str); } END_TEST START_TEST(test_str_table) { size_t n_cols = 5; char const* col_names[5] = { "col1", "column2", "foo", "bar", "zzz" }; size_t n_rows = 255; const char const* row[5] = {"dddd", "asdfasdf", "sadfdf", "", "a"}; const char* name = "test_table"; char* str = NULL; size_t off = 0; size_t i; str = gu_str_table_set_name(str, &off, name); fail_unless(strcmp(gu_str_table_get_name(str), name) == 0); str = gu_str_table_set_n_cols(str, &off, n_cols); fail_unless(gu_str_table_get_n_cols(str) == n_cols); str = gu_str_table_set_n_rows(str, &off, n_rows); fail_unless(gu_str_table_get_n_rows(str) == n_rows); str = gu_str_table_set_cols(str, &off, n_cols, col_names); for (i = 0; i < n_rows; ++i) { str = gu_str_table_append_row(str, &off, n_cols, row); } mark_point(); FILE* tmp = fopen("/dev/null", "w"); fail_if (NULL == tmp); gu_str_table_print(tmp, str); fclose(tmp); free(str); } END_TEST Suite* gu_str_suite() { Suite* s = suite_create("Galera Str util suite"); TCase* tc; tc = tcase_create("test_append"); tcase_add_test(tc, test_append); suite_add_tcase(s, tc); tc = tcase_create("test_scan"); tcase_add_test(tc, test_scan); suite_add_tcase(s, tc); tc = tcase_create("test_str_table"); tcase_add_test(tc, test_str_table); suite_add_tcase(s, tc); return s; } percona-xtradb-cluster-galera/galerautils/tests/gu_str_test.h0000644000000000000000000000025412247075736025044 0ustar rootroot00000000000000// Copyright (C) 2007 Codership Oy #ifndef __gu_str_test__ #define __gu_str_test__ extern Suite *gu_str_suite(void); #endif /* __gu_str_test__ */ percona-xtradb-cluster-galera/galerautils/tests/gu_string_test.cpp0000644000000000000000000000532412247075736026100 0ustar rootroot00000000000000// Copyright (C) 2009-2010 Codership Oy #include "gu_string.hpp" #include "gu_string_test.hpp" using std::string; using std::vector; START_TEST(test_strsplit) { string str = "foo bar baz"; vector vec = gu::strsplit(str, ' '); fail_unless(vec.size() == 3); fail_unless(vec[0] == "foo"); fail_unless(vec[1] == "bar"); fail_unless(vec[2] == "baz"); } END_TEST START_TEST(test_tokenize) { vector vec = gu::tokenize("", 'a', 'b', false); fail_unless(vec.size() == 0); vec = gu::tokenize("", 'a', 'b', true); fail_unless(vec.size() == 1); fail_unless(vec[0] == ""); vec = gu::tokenize("a", 'a', 'b', false); fail_unless(vec.size() == 0); vec = gu::tokenize("a", 'a', 'b', true); fail_unless(vec.size() == 2); fail_unless(vec[0] == ""); fail_unless(vec[1] == ""); vec = gu::tokenize("foo bar baz"); fail_unless(vec.size() == 3); fail_unless(vec[0] == "foo"); fail_unless(vec[1] == "bar"); fail_unless(vec[2] == "baz"); vec = gu::tokenize("foo\\ bar baz"); fail_unless(vec.size() == 2); fail_unless(vec[0] == "foo bar", "expected 'foo bar', found '%s'", vec[0].c_str()); fail_unless(vec[1] == "baz"); vec = gu::tokenize("foo\\;;bar;;baz;", ';', '\\', false); fail_unless(vec.size() == 3); fail_unless(vec[0] == "foo;"); fail_unless(vec[1] == "bar"); fail_unless(vec[2] == "baz"); vec = gu::tokenize("foo\\;;bar;;baz;", ';', '\\', true); fail_unless(vec.size() == 5, "vetor length %zu, expected 5", vec.size()); fail_unless(vec[0] == "foo;"); fail_unless(vec[1] == "bar"); fail_unless(vec[2] == ""); fail_unless(vec[3] == "baz"); fail_unless(vec[4] == ""); } END_TEST START_TEST(test_trim) { string full1 = ".,wklerf joweji"; string full2 = full1; gu::trim (full2); fail_if (full1 != full2); string part = " part "; gu::trim (part); fail_if (part.length() != 4); fail_if (0 != part.compare("part")); string empty; gu::trim (empty); fail_if (!empty.empty()); empty += ' '; empty += '\t'; empty += '\n'; empty += '\f'; fail_if (empty.empty()); gu::trim (empty); fail_if (!empty.empty(), "string contents: '%s', expected empty", empty.c_str()); } END_TEST Suite* gu_string_suite(void) { Suite* s = suite_create("galerautils++ String"); TCase* tc; tc = tcase_create("strsplit"); tcase_add_test(tc, test_strsplit); suite_add_tcase(s, tc); tc = tcase_create("tokenize"); tcase_add_test(tc, test_tokenize); suite_add_tcase(s, tc); tc = tcase_create("trim"); tcase_add_test(tc, test_trim); suite_add_tcase(s, tc); return s; } percona-xtradb-cluster-galera/galerautils/tests/gu_string_test.hpp0000644000000000000000000000032312247075736026077 0ustar rootroot00000000000000// Copyright (C) 2007 Codership Oy // $Id$ #ifndef __gu_string_test__ #define __gu_string_test__ #include extern Suite* gu_string_suite(void); #endif /* __gu_string_test__ */ percona-xtradb-cluster-galera/galerautils/tests/gu_tests++.cpp0000644000000000000000000000166012247075736025022 0ustar rootroot00000000000000// Copyright (C) 2009 Codership Oy #include #include #include extern "C" { #include "../src/gu_conf.h" } #include "gu_tests++.hpp" int main(int argc, char* argv[]) { bool no_fork = (argc >= 2 && std::string(argv[1]) == "nofork"); FILE* log_file = 0; if (!no_fork) { log_file = fopen (LOG_FILE, "w"); if (!log_file) return EXIT_FAILURE; gu_conf_set_log_file (log_file); } gu_conf_debug_on(); int failed = 0; for (int i = 0; suites[i] != 0; ++i) { SRunner* sr = srunner_create(suites[i]()); if (no_fork) srunner_set_fork_status(sr, CK_NOFORK); srunner_run_all(sr, CK_NORMAL); failed += srunner_ntests_failed(sr); srunner_free(sr); } if (log_file != 0) fclose(log_file); printf ("Total tests failed: %d\n", failed); return failed == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } percona-xtradb-cluster-galera/galerautils/tests/gu_tests++.hpp0000644000000000000000000000116612247075736025030 0ustar rootroot00000000000000// Copyright (C) 2009 Codership Oy // $Id: gu_tests++.hpp 3336 2013-10-28 07:41:56Z teemu $ /*! * @file: package specific part of the main test file. */ #ifndef __gu_testspp_hpp__ #define __gu_testspp_hpp__ #define LOG_FILE "gu_tests++.log" #include "gu_string_test.hpp" #include "gu_uri_test.hpp" #include "gu_net_test.hpp" #include "gu_datetime_test.hpp" #include "gu_vlq_test.hpp" typedef Suite *(*suite_creator_t)(void); static suite_creator_t suites[] = { gu_string_suite, gu_uri_suite, gu_net_suite, gu_datetime_suite, gu_vlq_suite, 0 }; #endif /* __gu_testspp_hpp__ */ percona-xtradb-cluster-galera/galerautils/tests/gu_tests.c0000644000000000000000000000336312247075736024336 0ustar rootroot00000000000000// Copyright (C) 2007-2012 Codership Oy // $Id: gu_tests.c 2812 2012-06-10 20:33:17Z alex $ #include // printf() #include // strcmp() #include // EXIT_SUCCESS | EXIT_FAILURE #include #include "../src/gu_conf.h" #include "gu_mem_test.h" #include "gu_bswap_test.h" #include "gu_fnv_test.h" #include "gu_mmh3_test.h" #include "gu_spooky_test.h" #include "gu_hash_test.h" #include "gu_dbug_test.h" #include "gu_time_test.h" #include "gu_fifo_test.h" #include "gu_uuid_test.h" #include "gu_lock_step_test.h" #include "gu_str_test.h" #include "gu_utils_test.h" typedef Suite *(*suite_creator_t)(void); static suite_creator_t suites[] = { gu_mem_suite, gu_bswap_suite, gu_fnv_suite, gu_mmh3_suite, gu_spooky_suite, gu_hash_suite, gu_dbug_suite, gu_time_suite, gu_fifo_suite, gu_uuid_suite, gu_lock_step_suite, gu_str_suite, gu_utils_suite, NULL }; int main(int argc, char* argv[]) { int no_fork = ((argc > 1) && !strcmp(argv[1], "nofork")) ? 1 : 0; int i = 0; int failed = 0; FILE* log_file = NULL; if (!no_fork) { log_file = fopen ("gu_tests.log", "w"); if (!log_file) return EXIT_FAILURE; gu_conf_set_log_file (log_file); } gu_conf_debug_on(); while (suites[i]) { SRunner* sr = srunner_create(suites[i]()); if (no_fork) srunner_set_fork_status(sr, CK_NOFORK); srunner_run_all (sr, CK_NORMAL); failed += srunner_ntests_failed (sr); srunner_free (sr); i++; } if (log_file) { fclose (log_file); } printf ("Total tests failed: %d\n", failed); return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } percona-xtradb-cluster-galera/galerautils/tests/gu_time_test.c0000644000000000000000000000165312247075736025171 0ustar rootroot00000000000000// Copyright (C) 2007 Codership Oy // $Id: gu_time_test.c 986 2009-08-23 18:02:01Z alex $ #include #include #include "gu_time_test.h" #include "../src/gu_time.h" START_TEST (gu_time_test) { struct timeval left = { 1, 900000 }; // 1.9 sec struct timeval right = { 5, 400000 }; // 5.4 sec double diff, tolerance = 1.0e-15; // double precision tolerance diff = gu_timeval_diff (&left, &right); fail_if (fabs(3.5 + diff) > tolerance, "Expected %f, got %f, delta: %e", -3.5, diff, 3.5 + diff); diff = gu_timeval_diff (&right, &left); fail_if (fabs(3.5 - diff) > tolerance, "Expected %f, got %f, delta: %e", 3.5, diff, 3.5 - diff); } END_TEST Suite *gu_time_suite(void) { Suite *s = suite_create("Galera time functions"); TCase *tc = tcase_create("gu_time"); suite_add_tcase (s, tc); tcase_add_test (tc, gu_time_test); return s; } percona-xtradb-cluster-galera/galerautils/tests/gu_time_test.h0000644000000000000000000000033712247075736025174 0ustar rootroot00000000000000// Copyright (C) 2007 Codership Oy // $Id: gu_time_test.h 248 2008-03-23 16:32:00Z alex $ #ifndef __gu_time_test__ #define __gu_time_test__ Suite *gu_time_suite(void); #endif /* __gu_time_test__ */ percona-xtradb-cluster-galera/galerautils/tests/gu_to_test.c0000644000000000000000000002212412247075736024651 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gu_to_test.c 3336 2013-10-28 07:41:56Z teemu $ */ #include #include #include // printf() #include // strerror() #include // strtol(), exit(), EXIT_SUCCESS, EXIT_FAILURE #include // errno #include // gettimeofday() #include // usleep() #include #include struct thread_ctx { pthread_t thread; long thread_id; long stat_grabs; // how many times gcs_to_grab() was successful long stat_cancels;// how many times gcs_to_cancel() was called long stat_fails; // how many times gcs_to_grab() failed long stat_self; // how many times gcs_self_cancel() was called }; /* returns a semirandom number (hash) from seqno */ static inline ulong my_rnd (uint64_t x) { x = 2654435761U * x; // http://www.concentric.net/~Ttwang/tech/inthash.htm return (ulong)(x ^ (x >> 32)); // combine upper and lower halfs for better // randomness } /* whether to cancel self */ static inline ulong self_cancel (ulong rnd) { return !(rnd & 0xf); // will return TRUE once in 16 } /* how many other seqnos to cancel */ static inline ulong cancel (ulong rnd) { #if 0 // this causes probablity of conflict 88% // and average conflicts per seqno 3.5. Reveals a lot of corner cases return (rnd & 0x70) >> 4; // returns 0..7 #else // this is more realistic. // probability of conflict 25%, conflict rate 0.375 register ulong ret = (rnd & 0x70) >> 4; // returns 0,0,0,0,0,0,1,2 if (gu_likely(ret < 5)) return 0; else return (ret - 5); #endif } /* offset of seqnos to cancel */ static inline ulong cancel_offset (ulong rnd) { return ((rnd & 0x700) >> 8) + 1; // returns 1 - 8 } static gu_to_t* to = NULL; static ulong thread_max = 16; // default number of threads static gu_seqno_t seqno_max = 1<<20; // default number of seqnos to check /* mutex to synchronize threads start */ static pthread_mutex_t start = PTHREAD_MUTEX_INITIALIZER; static const unsigned int t = 10; // optimal sleep time static const struct timespec tsleep = { 0, 10000000 }; // 10 ms void* run_thread(void* ctx) { struct thread_ctx* thd = ctx; gu_seqno_t seqno = thd->thread_id; // each thread starts with own offset // to guarantee uniqueness of seqnos // without having to lock mutex pthread_mutex_lock (&start); // wait for start signal pthread_mutex_unlock (&start); while (seqno < seqno_max) { long ret; ulong rnd = my_rnd(seqno); if (gu_unlikely(self_cancel(rnd))) { // printf("Self-cancelling %8llu\n", (unsigned long long)seqno); while ((ret = gu_to_self_cancel(to, seqno)) == -EAGAIN) usleep (t); if (gu_unlikely(ret)) { fprintf (stderr, "gu_to_self_cancel(%llu) returned %ld (%s)\n", (unsigned long long)seqno, ret, strerror(-ret)); exit (EXIT_FAILURE); } else { // printf ("Self-cancel success (%llu)\n", (unsigned long long)seqno); thd->stat_self++; } } else { // printf("Grabbing %8llu\n", (unsigned long long)seqno); while ((ret = gu_to_grab (to, seqno)) == -EAGAIN) nanosleep (&tsleep, NULL); if (gu_unlikely(ret)) { if (gu_likely(-ECANCELED == ret)) { // printf ("canceled (%llu)\n", (unsigned long long)seqno); thd->stat_fails++; } else { fprintf (stderr, "gu_to_grab(%llu) returned %ld (%s)\n", (unsigned long long)seqno, ret, strerror(-ret)); exit (EXIT_FAILURE); } } else { long cancels = cancel(rnd); // printf ("success (%llu), cancels = %ld\n", (unsigned long long)seqno, cancels); if (gu_likely(cancels)) { long offset = cancel_offset (rnd); gu_seqno_t cancel_seqno = seqno + offset; while (cancels-- && (cancel_seqno < seqno_max)) { ret = gu_to_cancel(to, cancel_seqno); if (gu_unlikely(ret)) { fprintf (stderr, "gu_to_cancel(%llu) by %llu " "failed: %s\n", (unsigned long long)cancel_seqno, (unsigned long long)seqno, strerror (-ret)); exit (EXIT_FAILURE); } else { // printf ("%llu canceled %llu\n", // seqno, cancel_seqno); cancel_seqno += offset; thd->stat_cancels++; } } } thd->stat_grabs++; ret = gu_to_release(to, seqno); if (gu_unlikely(ret)) { fprintf (stderr, "gu_to_release(%llu) failed: %ld(%s)\n", (unsigned long long)seqno, ret, strerror(-ret)); exit (EXIT_FAILURE); } } } seqno += thread_max; // this together with unique starting point // guarantees that seqnos are unique } // printf ("Thread %ld exiting. Last seqno = %llu\n", // thd->thread_id, (unsigned long long)(seqno - thread_max)); return NULL; } int main (int argc, char* argv[]) { // minimum to length required by internal logic ulong to_len = cancel(0xffffffff) * cancel_offset(0xffffffff); errno = 0; if (argc > 1) seqno_max = (1 << atol(argv[0])); if (argc > 2) thread_max = (1 << atol(argv[1])); if (errno) { fprintf (stderr, "Usage: %s [seqno [threads]]\nBoth seqno and threads" "are exponents of 2^n.\n", argv[0]); exit(errno); } printf ("Starting with %lu threads and %llu maximum seqno.\n", thread_max, (unsigned long long)seqno_max); /* starting with 0, enough space for all threads and cancels */ // 4 is a magic number to get it working without excessive sleep on amd64 to_len = to_len > thread_max ? to_len : thread_max; to_len *= 4; to = gu_to_create (to_len, 0); if (to != NULL) { printf ("Created TO monitor of length %lu\n", to_len); } else { exit (-ENOMEM); } /* main block */ { long i, ret; clock_t start_clock, stop_clock; double time_spent; struct thread_ctx thread[thread_max]; pthread_mutex_lock (&start); { /* initialize threads */ for (i = 0; i < thread_max; i++) { thread[i].thread_id = i; thread[i].stat_grabs = 0; thread[i].stat_cancels = 0; thread[i].stat_fails = 0; thread[i].stat_self = 0; ret = pthread_create(&(thread[i].thread), NULL, run_thread, &thread[i]); if (ret) { fprintf (stderr, "Failed to create thread %ld: %s", i, strerror(ret)); exit (EXIT_FAILURE); } } start_clock = clock(); } pthread_mutex_unlock (&start); // release threads /* wait for threads to complete and accumulate statistics */ pthread_join (thread[0].thread, NULL); for (i = 1; i < thread_max; i++) { pthread_join (thread[i].thread, NULL); thread[0].stat_grabs += thread[i].stat_grabs; thread[0].stat_cancels += thread[i].stat_cancels; thread[0].stat_fails += thread[i].stat_fails; thread[0].stat_self += thread[i].stat_self; } stop_clock = clock(); time_spent = gu_clock_diff (stop_clock,start_clock); /* print statistics */ printf ("%llu seqnos in %.3f seconds (%.3f seqno/sec)\n", (unsigned long long)seqno_max, time_spent, ((double) seqno_max)/time_spent); printf ("Overhead at 10000 actions/second: %.2f%%\n", (time_spent * 10000 * 100/* for % */)/seqno_max); printf ("Grabbed: %9lu\n" "Failed: %9lu\n" "Self-cancelled: %9lu\n" "Canceled: %9lu (can exceed total number of seqnos)\n", thread[0].stat_grabs, thread[0].stat_fails, thread[0].stat_self, thread[0].stat_cancels ); if (seqno_max != (thread[0].stat_grabs+thread[0].stat_fails+thread[0].stat_self)) { fprintf (stderr, "Error: total number of grabbed, failed and " "self-cancelled waiters does not match total seqnos.\n"); exit (EXIT_FAILURE); } } return 0; } percona-xtradb-cluster-galera/galerautils/tests/gu_uri_test.cpp0000644000000000000000000003144112247075736025370 0ustar rootroot00000000000000// Copyright (C) 2007 Codership Oy // $Id: gu_uri_test.cpp 3336 2013-10-28 07:41:56Z teemu $ #include #include #include "../src/gu_uri.hpp" #include "../src/gu_exception.hpp" #include "../src/gu_logger.hpp" #include "gu_uri_test.hpp" using std::string; using std::pair; using gu::URI; using gu::URIQueryList; using gu::NotSet; using gu::NotFound; using gu::Exception; START_TEST (uri_test1) // checking normal URI { const string scheme("scheme"); const string user ("user:pswd"); const string host ("[::ffff:192.168.0.1]"); // IPv4 over IPv6 const string port ("4567"); const string path ("/path1/path2"); const string opt1 ("opt1"); const string val1 ("val1"); const string opt2 ("opt2"); const string val2 ("val2"); const string query (opt1 + '=' + val1 + '&' + opt2 + '=' + val2); const string frag ("frag"); string auth = user + "@" + host + ":" + port; string uri_str = scheme + "://" + auth + path + "?" + query + "#" + frag; try { URI uri(uri_str); try { fail_if (scheme != uri.get_scheme(), "Scheme '%s' != '%s'", scheme.c_str(), uri.get_scheme().c_str()); } catch (NotSet&) { fail ("Scheme not set in '%s'", uri_str.c_str()); } try { fail_if (user != uri.get_user(), "User info '%s' != '%s'", user.c_str(), uri.get_user().c_str()); } catch (NotSet&) { fail ("User info not set in '%s'", uri_str.c_str()); } try { fail_if (host != uri.get_host(), "Host '%s' != '%s'", host.c_str(), uri.get_host().c_str()); } catch (NotSet&) { fail ("Host not set in '%s'", uri_str.c_str()); } try { fail_if (port != uri.get_port(), "Port '%s' != '%s'", port.c_str(), uri.get_port().c_str()); } catch (NotSet&) { fail ("Port not set in '%s'", uri_str.c_str()); } try { fail_if (path != uri.get_path(), "Path '%s' != '%s'", path.c_str(), uri.get_path().c_str()); } catch (NotSet&) { fail ("Path not set in '%s'", uri_str.c_str()); } try { fail_if (frag != uri.get_fragment(), "Fragment '%s' != '%s'", frag.c_str(), uri.get_fragment().c_str()); } catch (NotSet&) { fail ("Fragment not set in '%s'", uri_str.c_str()); } try { fail_if (auth != uri.get_authority(), "Authority '%s' != '%s'", auth.c_str(), uri.get_authority().c_str()); } catch (NotSet&) { fail ("Authority not set in '%s'", uri_str.c_str()); } URIQueryList ql = uri.get_query_list(); fail_if (ql.size() != 2, "Query list size %zu, expected 2", ql.size()); URIQueryList::const_iterator i = ql.begin(); fail_if (i->first != opt1, "got option '%s', expected '%s'", i->first.c_str(), opt1.c_str()); fail_if (i->second != val1, "got value '%s', expected '%s'", i->second.c_str(), val1.c_str()); ++i; fail_if (i->first != opt2, "got option '%s', expected '%s'", i->first.c_str(), opt2.c_str()); fail_if (i->second != val2, "got value '%s', expected '%s'", i->second.c_str(), val2.c_str()); fail_if (val1 != uri.get_option(opt1)); fail_if (val2 != uri.get_option(opt2)); try { uri.get_option("xxx"); fail ("Expected NotFound exception"); } catch (NotFound&) {} URI simple ("gcomm+pc://192.168.0.1"); } catch (Exception& e) { fail (e.what()); } } END_TEST START_TEST (uri_test2) // checking corner cases { #ifdef NDEBUG try { URI uri(""); fail ("URI should have failed."); } catch (Exception& e) {} #endif mark_point(); try { URI uri("scheme:"); } catch (Exception& e) { fail ("URI should be valid."); } mark_point(); #ifdef NDEBUG try { URI uri(":path"); fail ("URI should have failed."); } catch (Exception& e) {} #endif mark_point(); try { URI uri("a://b:c?d=e#f"); fail ("URI should have failed."); } catch (Exception& e) {} mark_point(); try { URI uri("a://b:99999?d=e#f"); fail ("URI should have failed."); } catch (Exception& e) {} mark_point(); #ifdef NDEBUG try { URI uri("?query"); fail ("URI should have failed."); } catch (Exception& e) {} #endif mark_point(); try { URI uri("scheme:path"); try { uri.get_user(); fail ("User should be unset"); } catch (NotSet&) {} try { uri.get_host(); fail ("Host should be unset"); } catch (NotSet&) {} try { uri.get_port(); fail ("Port should be unset"); } catch (NotSet&) {} try { uri.get_authority(); fail ("Authority should be unset"); } catch (NotSet&) {} try { uri.get_fragment(); fail ("Fragment should be unset"); } catch (NotSet&) {} fail_if (uri.get_query_list().size() != 0, "Query list must be empty"); } catch (Exception& e) { fail (e.what()); } mark_point(); try { URI uri("scheme:///path"); try { fail_if (uri.get_authority() != ""); } catch (NotSet&) { fail ("Authority should be set"); } try { uri.get_host(); fail("Host should be unset"); } catch (NotSet&) { } try { uri.get_user(); fail ("User should be unset"); } catch (NotSet&) {} try { uri.get_port(); fail ("Port should be unset"); } catch (NotSet&) {} try { fail_if (uri.get_path().length() != 5); } catch (NotSet&) { fail ("Path should be 5 characters long"); } } catch (Exception& e) { fail (e.what()); } mark_point(); try { URI uri("scheme://@/path"); try { fail_if (uri.get_authority() != "@"); } catch (NotSet&) { fail ("Authority should be set"); } try { fail_if (uri.get_user() != ""); } catch (NotSet&) { fail ("User should be set"); } try { fail_if (uri.get_host() != ""); } catch (NotSet&) { fail ("Host should be set"); } try { uri.get_port(); fail ("Port should be unset"); } catch (NotSet&) {} } catch (Exception& e) { fail (e.what()); } mark_point(); try { URI uri("scheme://@:/path"); try { fail_if (uri.get_authority() != "@"); } catch (NotSet&) { fail ("Authority should be set"); } try { fail_if (uri.get_user() != ""); } catch (NotSet&) { fail ("User should be set"); } try { fail_if (uri.get_host() != ""); } catch (NotSet&) { fail ("Host should be set"); } try { uri.get_port(); fail ("Port should be unset"); } catch (NotSet&) {} } catch (Exception& e) { fail (e.what()); } mark_point(); try { URI uri("scheme://"); try { fail_if (uri.get_authority() != ""); } catch (NotSet&) { fail ("Authority should be set"); } try { uri.get_user(); fail ("User should be unset"); } catch (NotSet&) {} try { uri.get_host(); fail("Host should be unset"); } catch (NotSet&) { } try { uri.get_port(); fail ("Port should be unset"); } catch (NotSet&) {} // According to http://tools.ietf.org/html/rfc3986#section-3.3 try { fail_if (uri.get_path() != ""); } catch (NotSet&) { fail ("Path should be set to empty"); } } catch (Exception& e) { fail (e.what()); } } END_TEST START_TEST (uri_test3) // Test from gcomm { #ifdef NDEBUG try { URI too_simple("http"); fail("too simple accepted"); } catch (gu::Exception& e) { fail_if (e.get_errno() != EINVAL); } #endif URI empty_auth("http://"); fail_unless(empty_auth.get_scheme() == "http"); fail_unless(empty_auth.get_authority() == ""); URI simple_valid1("http://example.com"); fail_unless(simple_valid1.get_scheme() == "http"); fail_unless(simple_valid1.get_authority() == "example.com"); fail_unless(simple_valid1.get_path() == ""); fail_unless(simple_valid1.get_query_list().size() == 0); URI with_path("http://example.com/path/to/file.html"); fail_unless(with_path.get_scheme() == "http"); fail_unless(with_path.get_authority() == "example.com"); fail_unless(with_path.get_path() == "/path/to/file.html"); fail_unless(with_path.get_query_list().size() == 0); URI with_query("http://example.com?key1=val1&key2=val2"); fail_unless(with_query.get_scheme() == "http"); fail_unless(with_query.get_authority() == "example.com"); fail_unless(with_query.get_path() == ""); const URIQueryList& qlist = with_query.get_query_list(); fail_unless(qlist.size() == 2); URIQueryList::const_iterator i; i = qlist.find("key1"); fail_unless(i != qlist.end() && i->second == "val1"); i = qlist.find("key2"); fail_unless(i != qlist.end() && i->second == "val2"); URI with_uri_in_query("gcomm+gmcast://localhost:10001?gmcast.node=gcomm+tcp://localhost:10002&gmcast.node=gcomm+tcp://localhost:10003"); fail_unless(with_uri_in_query.get_scheme() == "gcomm+gmcast"); fail_unless(with_uri_in_query.get_authority() == "localhost:10001"); const URIQueryList& qlist2 = with_uri_in_query.get_query_list(); fail_unless(qlist2.size() == 2); pair ii; ii = qlist2.equal_range("gmcast.node"); fail_unless(ii.first != qlist2.end()); for (i = ii.first; i != ii.second; ++i) { fail_unless(i->first == "gmcast.node"); URI quri(i->second); fail_unless(quri.get_scheme() == "gcomm+tcp"); fail_unless(quri.get_authority().substr(0, string("localhost:1000").size()) == "localhost:1000"); } try { URI invalid1("http://example.com/?key1"); fail("invalid query accepted"); } catch (gu::Exception& e) { fail_if (e.get_errno() != EINVAL); } } END_TEST START_TEST(uri_non_strict) { std::string const ip("1.2.3.4"); std::string const port("789"); std::string const addr(ip + ':' + port); try { URI u(ip); fail("Strict mode passed without scheme"); } catch (gu::Exception& e) { fail_if (e.get_errno() != EINVAL, "Expected errno %d, got %d", EINVAL, e.get_errno()); } try { URI u(addr, false); fail_if (u.get_host() != ip); fail_if (u.get_port() != port); try { u.get_scheme(); fail("Scheme is '%s', should be unset", u.get_scheme().c_str()); } catch (gu::NotSet&) {} } catch (gu::Exception& e) { fail_if (e.get_errno() != EINVAL); } } END_TEST START_TEST(uri_test_multihost) { try { gu::URI uri("tcp://host1,host2"); fail_unless(uri.get_authority_list().size() == 2); try { uri.get_authority_list()[0].user(); fail("User should not be set"); } catch (NotSet&) { } fail_unless(uri.get_authority_list()[0].host() == "host1"); try { uri.get_authority_list()[0].port(); fail("Port should not be set"); } catch (NotSet&) { } fail_unless(uri.get_authority_list()[1].host() == "host2"); } catch (gu::Exception& e) { fail(e.what()); } try { gu::URI uri("tcp://host1:1234,host2:,host3:3456"); fail_unless(uri.get_authority_list().size() == 3); try { uri.get_authority_list()[0].user(); fail("User should not be set"); } catch (NotSet&) { } fail_unless(uri.get_authority_list()[0].host() == "host1"); fail_unless(uri.get_authority_list()[0].port() == "1234"); fail_unless(uri.get_authority_list()[1].host() == "host2"); } catch (gu::Exception& e) { fail(e.what()); } } END_TEST Suite *gu_uri_suite(void) { Suite *s = suite_create("galerautils++ URI"); TCase *tc = tcase_create("URI"); suite_add_tcase (s, tc); tcase_add_test (tc, uri_test1); tcase_add_test (tc, uri_test2); tcase_add_test (tc, uri_test3); tcase_add_test (tc, uri_non_strict); tcase_add_test (tc, uri_test_multihost); return s; } percona-xtradb-cluster-galera/galerautils/tests/gu_uri_test.hpp0000644000000000000000000000037112247075736025373 0ustar rootroot00000000000000// Copyright (C) 2007 Codership Oy // $Id: gu_uri_test.hpp 3336 2013-10-28 07:41:56Z teemu $ #ifndef __gu_uri_test__ #define __gu_uri_test__ #include extern Suite *gu_uri_suite(void); #endif /* __gu_uri_test__ */ percona-xtradb-cluster-galera/galerautils/tests/gu_utils_test.c0000644000000000000000000000576712247075736025405 0ustar rootroot00000000000000// Copyright (C) 2010 Codership Oy // $Id: gu_utils_test.c 1916 2010-09-23 09:08:27Z alex $ #include #include "gu_utils_test.h" #include "../src/gu_utils.h" #include #include START_TEST (gu_strconv_test) { long long llret; const char* strret; strret = gu_str2ll ("-1a", &llret); fail_if (strret[0] != 'a'); fail_if (-1 != llret); strret = gu_str2ll ("1K", &llret); fail_if (strret[0] != '\0'); fail_if ((1 << 10) != llret); strret = gu_str2ll ("-1m", &llret); fail_if (strret[0] != '\0'); fail_if (-(1 << 20) != llret); strret = gu_str2ll ("354G0", &llret); fail_if (strret[0] != '0'); fail_if ((354LL << 30) != llret); strret = gu_str2ll ("0m", &llret); fail_if (strret[0] != '\0'); fail_if (0 != llret); strret = gu_str2ll ("-999999999999999g", &llret); fail_if (strret[0] != '\0'); fail_if (LLONG_MIN != llret); bool b; strret = gu_str2bool ("-1a", &b); fail_if (strret[0] != '-'); fail_if (false != b); strret = gu_str2bool ("-1", &b); fail_if (strret[0] != '-'); fail_if (false != b); strret = gu_str2bool ("1a", &b); fail_if (strret[0] != '1'); fail_if (false != b); strret = gu_str2bool ("35", &b); fail_if (strret[0] != '3'); fail_if (false != b); strret = gu_str2bool ("0k", &b); fail_if (strret[0] != '0'); fail_if (false != b); strret = gu_str2bool ("1", &b); fail_if (strret[0] != '\0'); fail_if (true != b); strret = gu_str2bool ("0", &b); fail_if (strret[0] != '\0'); fail_if (false != b); strret = gu_str2bool ("Onn", &b); fail_if (strret[0] != 'O'); fail_if (false != b); strret = gu_str2bool ("oFf", &b); fail_if (strret[0] != '\0'); fail_if (false != b); strret = gu_str2bool ("offt", &b); fail_if (strret[0] != 'o'); fail_if (false != b); strret = gu_str2bool ("On", &b); fail_if (strret[0] != '\0'); fail_if (true != b); strret = gu_str2bool ("tru", &b); fail_if (strret[0] != 't'); fail_if (false != b); strret = gu_str2bool ("trUE", &b); fail_if (strret[0] != '\0'); fail_if (true != b); strret = gu_str2bool ("truEth", &b); fail_if (strret[0] != 't'); fail_if (false != b); strret = gu_str2bool (" fALsE", &b); fail_if (strret[0] != ' '); fail_if (false != b); strret = gu_str2bool ("fALsE", &b); fail_if (strret[0] != '\0'); fail_if (false != b); strret = gu_str2bool ("fALsEth", &b); fail_if (strret[0] != 'f'); fail_if (false != b); void* ptr; strret = gu_str2ptr ("-01234abc", &ptr); fail_if (strret[0] != '\0'); fail_if (-0x1234abcLL != (intptr_t)ptr, "Expected %lld, got %lld", -0x1234abcLL, (intptr_t)ptr); } END_TEST Suite *gu_utils_suite(void) { Suite *s = suite_create("Galera misc utils functions"); TCase *tc = tcase_create("gu_utils"); suite_add_tcase (s, tc); tcase_add_test (tc, gu_strconv_test); return s; } percona-xtradb-cluster-galera/galerautils/tests/gu_utils_test.h0000644000000000000000000000034512247075736025375 0ustar rootroot00000000000000// Copyright (C) 2010 Codership Oy // $Id: gu_utils_test.h 1869 2010-08-20 16:20:42Z alex $ #ifndef __gu_utils_test__ #define __gu_utils_test__ Suite *gu_utils_suite(void); #endif /* __gu_utils_test__ */ percona-xtradb-cluster-galera/galerautils/tests/gu_uuid_test.c0000644000000000000000000000222612247075736025176 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gu_uuid_test.c 495 2008-11-18 13:16:31Z alex $ */ #include #include #include #include #include "../src/gu_log.h" #include "../src/gu_uuid.h" #include "gu_uuid_test.h" START_TEST (gu_uuid_test) { size_t uuid_num = 10; gu_uuid_t uuid[uuid_num]; size_t i; uuid[0] = GU_UUID_NIL; gu_uuid_generate (&uuid[0], NULL, 0); fail_if (!memcmp (&uuid[0], &GU_UUID_NIL, sizeof(gu_uuid_t))); fail_if (!gu_uuid_compare(&uuid[0], &GU_UUID_NIL)); for (i = 1; i < uuid_num; i++) { uuid[i] = GU_UUID_NIL; gu_uuid_generate (&uuid[i], NULL, 0); fail_if (!gu_uuid_compare(&uuid[i], &GU_UUID_NIL)); fail_if (!gu_uuid_compare(&uuid[i], &uuid[i - 1])); fail_if (1 != gu_uuid_older (&uuid[i - 1], &uuid[i])); fail_if (-1 != gu_uuid_older (&uuid[i], &uuid[i - 1])); } } END_TEST Suite *gu_uuid_suite(void) { Suite *suite = suite_create("Galera UUID utils"); TCase *tcase = tcase_create("gu_uuid"); suite_add_tcase (suite, tcase); tcase_add_test (tcase, gu_uuid_test); return suite; } percona-xtradb-cluster-galera/galerautils/tests/gu_uuid_test.h0000644000000000000000000000035712247075736025206 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gu_uuid_test.h 495 2008-11-18 13:16:31Z alex $ */ #ifndef __gu_uuid_test__ #define __gu_uuid_test__ extern Suite *gu_uuid_suite(void); #endif /* __gu_uuid_test__ */ percona-xtradb-cluster-galera/galerautils/tests/gu_vlq_test.cpp0000644000000000000000000001774712247075736025410 0ustar rootroot00000000000000// // Copyright (C) 2011 Codership Oy // #include "gu_vlq.hpp" #include "gu_vlq_test.hpp" #include #include #include #include #include static struct valval { const unsigned long long val; const size_t size; } valarr[] = { {0x00 , 1}, {0x01 , 1}, {0x7fULL , 1}, {0x80ULL , 2}, {0x3fffULL , 2}, {0x4000ULL , 3}, {0x1fffffULL , 3}, {0x200000ULL , 4}, {0x0fffffffULL , 4}, {0x10000000ULL , 5}, {0x07ffffffffULL , 5}, {0x0800000000ULL , 6}, {0x03ffffffffffULL , 6}, {0x040000000000ULL , 7}, {0x01ffffffffffffULL , 7}, {0x02000000000000ULL , 8}, {0x00ffffffffffffffULL, 8}, {0x0100000000000000ULL, 9}, {0x7fffffffffffffffULL, 9}, {0x8000000000000000ULL, 10}, {0xffffffffffffffffULL, 10} }; START_TEST(test_uleb128_size) { for (size_t i(0); i < sizeof(valarr)/sizeof(struct valval); ++i) { size_t size(gu::uleb128_size(valarr[i].val)); fail_unless(size == valarr[i].size, "got size %z, expected %z for value 0x%llx", size, valarr[i].size, valarr[i].val); } } END_TEST START_TEST(test_uleb128_encode) { std::vector buf; for (size_t i(0); i < sizeof(valarr)/sizeof(struct valval); ++i) { buf.resize(valarr[i].size); size_t offset(gu::uleb128_encode(valarr[i].val, &buf[0], buf.size(), 0)); fail_unless(offset == valarr[i].size, "got offset %zu, expected %zu for value 0x%llx", offset, valarr[i].size, valarr[i].val); } } END_TEST START_TEST(test_uleb128_decode) { std::vector buf; for (size_t i(0); i < sizeof(valarr)/sizeof(struct valval); ++i) { buf.resize(valarr[i].size); size_t offset(gu::uleb128_encode(valarr[i].val, &buf[0], buf.size(), 0)); unsigned long long val; try { offset = gu::uleb128_decode(&buf[0], buf.size(), 0, val); fail_unless(offset == valarr[i].size, "got offset %zu, expected %zu for value 0x%llx", offset, valarr[i].size, valarr[i].val); fail_unless(val == valarr[i].val, "got value 0x%llx, expected 0x%llx", val, valarr[i].val); } catch (gu::Exception& e) { fail("Exception in round %zu for encoding of size %zu: %s", i, valarr[i].size, e.what()); } } } END_TEST START_TEST(test_uleb128_misc) { std::vector buf(10); // check uint8_t whole range for (size_t i(0); i <= std::numeric_limits::max(); ++i) { (void)gu::uleb128_encode(static_cast(i), &buf[0], buf.size(), 0); uint8_t val; (void)gu::uleb128_decode(&buf[0], buf.size(), 0, val); if (i != val) fail("0x%x != 0x%x", i, val); } // check uint16_t whole range for (size_t i(0); i <= std::numeric_limits::max(); ++i) { (void)gu::uleb128_encode(static_cast(i), &buf[0], buf.size(), 0); uint16_t val; (void)gu::uleb128_decode(&buf[0], buf.size(), 0, val); if (i != val) fail("0x%x != 0x%x", i, val); } // check uint32_t: 0 -> 1^20 for (size_t i(0); i < (1 << 20); ++i) { (void)gu::uleb128_encode(static_cast(i), &buf[0], buf.size(), 0); uint32_t val; (void)gu::uleb128_decode(&buf[0], buf.size(), 0, val); if (i != val) fail("0x%x != 0x%x", i, val); } // check uin32_t: max - 1^20 -> max for (uint64_t i(std::numeric_limits::max() - (1 << 20)); i <= std::numeric_limits::max(); ++i) { (void)gu::uleb128_encode(static_cast(i), &buf[0], buf.size(), 0); uint32_t val; (void)gu::uleb128_decode(&buf[0], buf.size(), 0, val); if (i != val) fail("0x%x != 0x%x", i, val); } // uint64_t is tested for representation byte boundaries earlier, // run test just for random values for (size_t i(0); i < (1 << 16); ++i) { unsigned long long val(static_cast(rand()) * static_cast(rand())); (void)gu::uleb128_encode(val, &buf[0], buf.size(), 0); unsigned long long val2; (void)gu::uleb128_decode(&buf[0], buf.size(), 0, val2); if (val != val2) fail("0x%llx != 0x%llx", val, val2); } { // check that exception is thrown if target type is not // wide enough // uint8_t uint64_t val(static_cast(std::numeric_limits::max()) + 1); buf.resize(gu::uleb128_size(val)); (void)gu::uleb128_encode(val, &buf[0], buf.size(), 0); try { uint8_t cval; (void)gu::uleb128_decode(&buf[0], buf.size(), 0, cval); fail("exception was not thrown"); } catch (gu::Exception& e) { log_info << "expected exception: " << e.what(); } // uint16_t val = static_cast(std::numeric_limits::max()) + 1; buf.resize(gu::uleb128_size(val)); (void)gu::uleb128_encode(val, &buf[0], buf.size(), 0); try { uint16_t cval; (void)gu::uleb128_decode(&buf[0], buf.size(), 0, cval); fail("exception was not thrown"); } catch (gu::Exception& e) { log_info << "expected exception: " << e.what(); } // uint32_t val = static_cast(std::numeric_limits::max()) + 1; buf.resize(gu::uleb128_size(val)); (void)gu::uleb128_encode(val, &buf[0], buf.size(), 0); try { uint32_t cval; (void)gu::uleb128_decode(&buf[0], buf.size(), 0, cval); fail("exception was not thrown"); } catch (gu::Exception& e) { log_info << "expected exception: " << e.what(); } // check that exception is thrown if terminating byte is missing buf.resize(buf.size() - 1); try { uint64_t cval; (void)gu::uleb128_decode(&buf[0], buf.size(), 0, cval); fail("exception was not thrown"); } catch (gu::Exception& e) { log_info << "expected exception: " << e.what(); } // finally check the representation that cannot be stored with // uint64_t gu::byte_t b[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // <--- up here 9 * 7 = 63 bits 0x02}; // <--- requires two additional bits try { uint64_t cval; (void)gu::uleb128_decode(b, sizeof(b), 0, cval); fail("exception was not thrown"); } catch (gu::Exception& e) { log_info << "expected exception: " << e.what(); } } } END_TEST Suite* gu_vlq_suite() { Suite* s(suite_create("gu::vlq")); TCase* tc; tc = tcase_create("test_uleb128_size"); tcase_add_test(tc, test_uleb128_size); suite_add_tcase(s, tc); tc = tcase_create("test_uleb128_encode"); tcase_add_test(tc, test_uleb128_encode); suite_add_tcase(s, tc); tc = tcase_create("test_uleb128_decode"); tcase_add_test(tc, test_uleb128_decode); suite_add_tcase(s, tc); tc = tcase_create("test_uleb128_misc"); tcase_add_test(tc, test_uleb128_misc); suite_add_tcase(s, tc); return s; } percona-xtradb-cluster-galera/galerautils/tests/gu_vlq_test.hpp0000644000000000000000000000026612247075736025401 0ustar rootroot00000000000000// // Copyright (C) 2011 Codership Oy // #ifndef GU_VLQ_TEST_HPP #define GU_VLQ_TEST_HPP #include Suite* gu_vlq_suite(); #endif // GU_VLQ_TEST_HPP percona-xtradb-cluster-galera/garb/SConscript0000644000000000000000000000270112247075736021577 0ustar rootroot00000000000000# Copyright (C) 2011 Codership Oy Import('env', 'libboost_program_options', 'static_ssl', 'with_ssl') garb_env = env.Clone() garb_env.Prepend(LIBS=File('#/galerautils/src/libgalerautils.a')) garb_env.Prepend(LIBS=File('#/galerautils/src/libgalerautils++.a')) garb_env.Prepend(LIBS=File('#/gcomm/src/libgcomm.a')) garb_env.Prepend(LIBS=File('#/gcs/src/libgcs4garb.a')) if libboost_program_options: garb_env.Append(LIBS=libboost_program_options) # special environment for garb_config.cpp conf_env = garb_env.Clone() Import('GALERA_VER', 'GALERA_REV') conf_env.Append(CPPFLAGS = ' -DGALERA_VER=\\"' + GALERA_VER + '\\"') conf_env.Append(CPPFLAGS = ' -DGALERA_REV=\\"' + GALERA_REV + '\\"') if static_ssl == 1: garb_env.Append(LIBPATH = [with_ssl]) garb_env.Append(LIBS=File('%s/libssl.a' %(with_ssl))) garb_env.Append(LIBS=File('%s/libcrypto.a' %(with_ssl))) garb_env.Append(LIBS=File('%s/libz.a' %(with_ssl))) garb_env.Append(LIBS=['dl']) garb = garb_env.Program(target = 'garbd', source = Split(''' garb_logger.cpp garb_gcs.cpp garb_recv_loop.cpp garb_main.cpp ''') + conf_env.SharedObject(['garb_config.cpp']) ) percona-xtradb-cluster-galera/garb/files/0000755000000000000000000000000012247075736020667 5ustar rootroot00000000000000percona-xtradb-cluster-galera/garb/garb_config.cpp0000644000000000000000000000715612247075736022542 0ustar rootroot00000000000000/* Copyright (C) 2011 Codership Oy */ #include "garb_config.hpp" #include "garb_logger.hpp" #include #include namespace po = boost::program_options; #include #include namespace garb { std::string const Config::DEFAULT_SST(WSREP_STATE_TRANSFER_TRIVIAL); Config::Config (int argc, char* argv[]) : daemon_ (false), address_ (), group_ ("my_test_cluster"), sst_ (DEFAULT_SST), donor_ (), options_ (), log_ (), cfg_ () { po::options_description other ("Other options"); other.add_options() ("version,v", "Print version") ("help,h", "Show help message") ; // only these are read from cfg file po::options_description config ("Configuration"); config.add_options() ("daemon,d", "Become daemon") ("address,a",po::value(&address_), "Group address") ("group,g", po::value(&group_), "Group name") ("sst", po::value(&sst_), "SST request string") ("donor", po::value(&donor_), "SST donor name") ("options,o",po::value(&options_), "GCS/GCOMM option list") ("log,l", po::value(&log_), "Log file") ; po::options_description cfg_opt; config.add_options() ("cfg,c", po::value(&cfg_), "Configuration file") ; // these are accepted on the command line po::options_description cmdline_opts; cmdline_opts.add(config).add(cfg_opt).add(other); // we can submit address without option po::positional_options_description p; p.add("address", -1); po::variables_map vm; store(po::command_line_parser(argc, argv). options(cmdline_opts).positional(p).run(), vm); notify(vm); if (vm.count("help")) { std::cerr << "\nUsage: " << argv[0] << " [options] [group address]\n" << cmdline_opts << std::endl; throw gu::Exception("Exit", 0); } if (vm.count("version")) { log_info << GALERA_VER << ".r" << GALERA_REV; } if (vm.count("cfg")) { std::ifstream ifs(cfg_.c_str()); if (!ifs.good()) { gu_throw_error(ENOENT) << "Failed to open configuration file '" << cfg_ << "' for reading."; } store(parse_config_file(ifs, config), vm); notify(vm); } if (!vm.count("address")) { gu_throw_error(EDESTADDRREQ) << "Group address not specified"; } if (!vm.count("group")) { gu_throw_error(EDESTADDRREQ) << "Group name not specified"; } if (vm.count("daemon")) { daemon_ = true; } if (options_.length() > 0) options_ += "; "; options_ += "gcs.fc_limit=9999999; gcs.fc_factor=1.0; gcs.fc_master_slave=yes"; // this block must be the very last. gu_conf_self_tstamp_on(); if (vm.count("log")) { set_logfile (log_); } else if (daemon_) /* if no log file given AND daemon operation requested - * log to syslog */ { gu_conf_self_tstamp_off(); set_syslog(); } } std::ostream& operator << (std::ostream& os, const Config& c) { os << "\n\tdaemon: " << c.daemon() << "\n\taddress: " << c.address() << "\n\tgroup: " << c.group() << "\n\tsst: " << c.sst() << "\n\tdonor: " << c.donor() << "\n\toptions: " << c.options() << "\n\tcfg: " << c.cfg() << "\n\tlog: " << c.log(); return os; } } percona-xtradb-cluster-galera/garb/garb_config.hpp0000644000000000000000000000222412247075736022536 0ustar rootroot00000000000000/* Copyright (C) 2011 Codership Oy */ #ifndef _GARB_CONFIG_HPP_ #define _GARB_CONFIG_HPP_ #include #include #include namespace garb { class Config { public: static std::string const DEFAULT_SST; // default (empty) SST request Config (int argc, char* argv[]); ~Config () {} bool daemon() const { return daemon_ ; } const std::string& address() const { return address_; } const std::string& group() const { return group_ ; } const std::string& sst() const { return sst_ ; } const std::string& donor() const { return donor_ ; } const std::string& options() const { return options_; } const std::string& cfg() const { return cfg_ ; } const std::string& log() const { return log_ ; } private: bool daemon_; std::string address_; std::string group_; std::string sst_; std::string donor_; std::string options_; std::string log_; std::string cfg_; }; /* class Config */ std::ostream& operator << (std::ostream&, const Config&); } /* namespace garb */ #endif /* _GARB_CONFIG_HPP_ */ percona-xtradb-cluster-galera/garb/garb_gcs.cpp0000644000000000000000000000667012247075736022051 0ustar rootroot00000000000000/* * Copyright (C) 2011-2012 Codership Oy */ #include "garb_gcs.hpp" namespace garb { static int const REPL_PROTO_VER(127); static int const APPL_PROTO_VER(127); Gcs::Gcs (gu::Config& gconf, const std::string& address, const std::string& group) : closed_ (true), gcs_ (gcs_create (reinterpret_cast(&gconf), NULL, GCS_ARBITRATOR_NAME, "", REPL_PROTO_VER, APPL_PROTO_VER)) { if (!gcs_) { gu_throw_fatal << "Failed to create GCS object"; } ssize_t ret = gcs_open (gcs_, group.c_str(), address.c_str(), false); if (ret < 0) { gcs_destroy (gcs_); gu_throw_error(-ret) << "Failed to open connection to group"; } closed_ = false; } Gcs::~Gcs () { if (!closed_) { log_warn << "Destroying non-closed object, bad idea"; close (); } gcs_destroy (gcs_); } void Gcs::recv (gcs_action& act) { again: ssize_t ret = gcs_recv(gcs_, &act); if (gu_unlikely(ret < 0)) { if (-ECANCELED == ret) { ret = gcs_resume_recv (gcs_); if (0 == ret) goto again; } log_fatal << "Receiving from group failed: " << ret << " (" << strerror(-ret) << ")"; gu_throw_error(-ret) << "Receiving from group failed"; } } void Gcs::request_state_transfer (const std::string& request, const std::string& donor) { gcs_seqno_t order; log_info << "Sending state transfer request: '" << request << "', size: " << request.length(); /* Need to substitute the first ':' for \0 */ ssize_t req_len = request.length() + 1 /* \0 */; char* const req_str(reinterpret_cast(::malloc( req_len + 1 /* potentially need one more \0 */))); // cppcheck-suppress nullPointer if (!req_str) { gu_throw_error (ENOMEM) << "Cannot allocate " << req_len << " bytes for state transfer request"; } ::strcpy(req_str, request.c_str()); char* column_ptr = ::strchr(req_str, ':'); if (column_ptr) { *column_ptr = '\0'; } else /* append an empty string */ { req_str[req_len] = '\0'; req_len++; } ssize_t ret; do { ret = gcs_request_state_transfer (gcs_, req_str, req_len, donor.c_str(), &order); } while (-EAGAIN == ret && (usleep(1000000), true)); free (req_str); if (ret < 0) { log_fatal << "State transfer request failed: " << ret << " (" << strerror(-ret) << ")"; gu_throw_error(-ret) << "State transfer request failed"; } } void Gcs::join (gcs_seqno_t seqno) { ssize_t ret = gcs_join (gcs_, seqno); if (ret < 0) { log_fatal << "Joining group failed: " << ret << " (" << strerror(-ret) << ")"; gu_throw_error(-ret) << "Joining group failed"; } } void Gcs::set_last_applied (gcs_seqno_t seqno) { (void) gcs_set_last_applied(gcs_, seqno); } void Gcs::close () { if (!closed_) { ssize_t ret = gcs_close (gcs_); if (ret < 0) { log_error << "Failed to close connection to group"; } else { closed_ = true; } } else { log_warn << "Attempt to close a closed connection"; } } } /* namespace garb */ percona-xtradb-cluster-galera/garb/garb_gcs.hpp0000644000000000000000000000135212247075736022046 0ustar rootroot00000000000000/* Copyright (C) 2011 Codership Oy */ #ifndef _GARB_GCS_HPP_ #define _GARB_GCS_HPP_ #include #include namespace garb { class Gcs { public: Gcs (gu::Config& conf, const std::string& address, const std::string& group); ~Gcs (); void recv (gcs_action& act); void request_state_transfer (const std::string& request, const std::string& donor); void join (gcs_seqno_t); void set_last_applied(gcs_seqno_t); void close (); private: bool closed_; gcs_conn_t* gcs_; Gcs (const Gcs&); Gcs& operator= (const Gcs&); }; /* class Gcs */ } /* namespace garb */ #endif /* _GARB_GCS_HPP_ */ percona-xtradb-cluster-galera/garb/garb_logger.cpp0000644000000000000000000000205712247075736022547 0ustar rootroot00000000000000/* Copyright (C) 2011 Codership Oy */ #include "garb_logger.hpp" #include #include namespace garb { void set_logfile (const std::string& fname) { FILE* log_file = fopen (fname.c_str(), "a"); if (!log_file) { gu_throw_error (ENOENT) << "Failed to open '" << fname << "' for appending"; } gu_conf_set_log_file (log_file); } static void log_to_syslog (int level, const char* msg) { int p = LOG_NOTICE; switch (level) { case GU_LOG_FATAL: p = LOG_CRIT; break; case GU_LOG_ERROR: p = LOG_ERR; break; case GU_LOG_WARN: p = LOG_WARNING; break; case GU_LOG_INFO: p = LOG_INFO; break; case GU_LOG_DEBUG: p = LOG_DEBUG; break; } syslog (p | LOG_DAEMON, "%s", msg); } void set_syslog () { openlog ("garbd", LOG_PID, LOG_DAEMON); gu_conf_set_log_callback (log_to_syslog); } } /* namespace garb */ percona-xtradb-cluster-galera/garb/garb_logger.hpp0000644000000000000000000000050012247075736022543 0ustar rootroot00000000000000/* Copyright (C) 2011 Codership Oy */ #ifndef _GARB_LOGGER_HPP_ #define _GARB_LOGGER_HPP_ #include #include namespace garb { extern void set_logfile (const std::string& fname); extern void set_syslog (); } /* namespace garb */ #endif /* _GARB_LOGGER_HPP_ */ percona-xtradb-cluster-galera/garb/garb_main.cpp0000644000000000000000000000411212247075736022206 0ustar rootroot00000000000000/* Copyright (C) 2011 Codership Oy */ #include "garb_config.hpp" #include "garb_recv_loop.hpp" #include #include #include // exit() #include // setsid(), chdir() #include // open() namespace garb { void become_daemon () { if (pid_t pid = fork()) { if (pid > 0) // parent { exit(0); } else { // I guess we want this to go to stderr as well; std::cerr << "Failed to fork daemon process: " << errno << " (" << strerror(errno) << ")"; gu_throw_error(errno) << "Failed to fork daemon process"; } } // child if (setsid()<0) // become a new process leader, detach from terminal { gu_throw_error(errno) << "setsid() failed"; } if (chdir("/")) // detach from potentially removable block devices { gu_throw_error(errno) << "chdir(\"/\") failed"; } // umask(0); // A second fork ensures the process cannot acquire a controlling // terminal. if (pid_t pid = fork()) { if (pid > 0) { exit(0); } else { gu_throw_error(errno) << "Second fork failed"; } } // Close the standard streams. This decouples the daemon from the // terminal that started it. close(0); close(1); close(2); // Bind standard fds (0, 1, 2) to /dev/null for (int fd = 0; fd < 3; ++fd) { if (open("/dev/null", O_RDONLY) < 0) { gu_throw_error(errno) << "Unable to open /dev/null for fd " << fd; } } } int main (int argc, char* argv[]) { Config config(argc, argv); log_info << "Read config: " << config << std::endl; if (config.daemon()) become_daemon(); RecvLoop loop (config); return 0; } } /* namespace garb */ int main (int argc, char* argv[]) { try { return garb::main (argc, argv); } catch (std::exception& e) { log_fatal << e.what(); return 1; } } percona-xtradb-cluster-galera/garb/garb_recv_loop.cpp0000644000000000000000000000506412247075736023261 0ustar rootroot00000000000000/* Copyright (C) 2011 Codership Oy */ #include "garb_recv_loop.hpp" #include namespace garb { static Gcs* global_gcs(0); void signal_handler (int signum) { log_info << "Received signal " << signum; global_gcs->close(); } RecvLoop::RecvLoop (const Config& config) : config_(config), gconf_ (config_.options()), gcs_ (gconf_, config_.address(), config_.group()) { /* set up signal handlers */ global_gcs = &gcs_; struct sigaction sa; memset (&sa, 0, sizeof(sa)); sa.sa_handler = signal_handler; if (sigaction (SIGTERM, &sa, NULL)) { gu_throw_error(errno) << "Falied to install signal hadler for signal " << "SIGTERM"; } if (sigaction (SIGINT, &sa, NULL)) { gu_throw_error(errno) << "Falied to install signal hadler for signal " << "SIGINT"; } loop(); } void RecvLoop::loop() { while (1) { gcs_action act; gcs_.recv (act); switch (act.type) { case GCS_ACT_TORDERED: if (gu_unlikely(!(act.seqno_g & 127))) /* == report_interval_ of 128 */ { gcs_.set_last_applied (act.seqno_g); } break; case GCS_ACT_COMMIT_CUT: break; case GCS_ACT_STATE_REQ: gcs_.join (-ENOSYS); /* we can't donate state */ break; case GCS_ACT_CONF: { const gcs_act_conf_t* const cc (reinterpret_cast(act.buf)); if (cc->conf_id > 0) /* PC */ { if (GCS_NODE_STATE_PRIM == cc->my_state) { gcs_.request_state_transfer (config_.sst(),config_.donor()); gcs_.join(cc->seqno); } } else if (cc->memb_num == 0) // SELF-LEAVE after closing connection { log_info << "Exiting main loop"; return; } if (config_.sst() != Config::DEFAULT_SST) { // we requested custom SST, so we're done here gcs_.close(); } break; } case GCS_ACT_JOIN: case GCS_ACT_SYNC: case GCS_ACT_FLOW: case GCS_ACT_SERVICE: case GCS_ACT_ERROR: case GCS_ACT_UNKNOWN: break; } if (act.buf) { free (const_cast(act.buf)); } } } } /* namespace garb */ percona-xtradb-cluster-galera/garb/garb_recv_loop.hpp0000644000000000000000000000076312247075736023267 0ustar rootroot00000000000000/* Copyright (C) 2011 Codership Oy */ #ifndef _GARB_RECV_LOOP_HPP_ #define _GARB_RECV_LOOP_HPP_ #include "garb_gcs.hpp" #include "garb_config.hpp" #include #include namespace garb { class RecvLoop { public: RecvLoop (const Config&); ~RecvLoop () {} private: void loop(); const Config& config_; gu::Config gconf_; Gcs gcs_; }; /* RecvLoop */ } /* namespace garb */ #endif /* _GARB_RECV_LOOP_HPP_ */ percona-xtradb-cluster-galera/garb/files/freebsd/0000755000000000000000000000000012247075736022301 5ustar rootroot00000000000000percona-xtradb-cluster-galera/garb/files/garb.cnf0000644000000000000000000000076612247075736022303 0ustar rootroot00000000000000# Copyright (C) 2012 Coedership Oy # This config file is to be sourced by garb service script. # A space-separated list of node addresses (address[:port]) in the cluster # GALERA_NODES="" # Galera cluster name, should be the same as on the rest of the nodes. # GALERA_GROUP="" # Optional Galera internal options string (e.g. SSL settings) # see http://www.codership.com/wiki/doku.php?id=galera_parameters # GALERA_OPTIONS="" # Log file for garbd. Optional, by default logs to syslog # LOG_FILE="" percona-xtradb-cluster-galera/garb/files/garb.sh0000755000000000000000000000656412247075736022154 0ustar rootroot00000000000000#!/bin/bash # # Copyright (C) 2012-2013 Codership Oy # # init.d script for garbd # # chkconfig: - 99 01 # config: /etc/sysconfig/garb | /etc/default/garb # #### BEGIN INIT INFO # Provides: garbd # Required-Start: $network # Should-Start: # Required-Stop: $network # Should-Stop: # Default-Start: 3 4 5 # Default-Stop: 0 1 2 6 # Short-Description: Galera Arbitrator Daemon # Description: Galera Arbitrator Daemon ### END INIT INFO # Source function library. if [ -f /etc/redhat-release ]; then . /etc/init.d/functions . /etc/sysconfig/network config=/etc/sysconfig/garb else . /lib/lsb/init-functions config=/etc/default/garb fi log_failure() { if [ -f /etc/redhat-release ]; then echo -n $* failure "$*" echo else log_failure_msg "$*" fi } PIDFILE=/var/run/garbd prog=$(which garbd) program_start() { local rcode if [ -f /etc/redhat-release ]; then echo -n $"Starting $prog: " sudo -u nobody $prog $* >/dev/null rcode=$? [ $rcode -eq 0 ] && pidof $prog > $PIDFILE \ && echo_success || echo_failure echo else log_daemon_msg "Starting $prog: " start-stop-daemon --start --quiet --background \ --exec $prog -- $* rcode=$? # Hack: sleep a bit to give garbd some time to fork sleep 1 [ $rcode -eq 0 ] && pidof $prog > $PIDFILE log_end_msg $rcode fi return $rcode } program_stop() { local rcode if [ -f /etc/redhat-release ]; then echo -n $"Shutting down $prog: " killproc -p $PIDFILE rcode=$? [ $rcode -eq 0 ] && echo_success || echo_failure # echo else start-stop-daemon --stop --quiet --oknodo --retry TERM/30/KILL/5 \ --pidfile $PIDFILE rcode=$? log_end_msg $rcode fi [ $rcode -eq 0 ] && rm -f $PIDFILE return $rcode } program_status() { if [ -f /etc/redhat-release ]; then status $prog else status_of_proc -p $PIDFILE "$prog" garb fi } start() { [ "$EUID" != "0" ] && return 4 [ "$NETWORKING" = "no" ] && return 1 if [ -r $PIDFILE ]; then log_failure "$prog is already running with PID $(cat ${PIDFILE})" return 0 fi [ -x $prog ] || return 5 [ -f $config ] && . $config # Check that node addresses are configured if [ -z "$GALERA_NODES" ]; then log_failure "List of GALERA_NODES is not configured" return 6 fi if [ -z "$GALERA_GROUP" ]; then log_failure "GALERA_GROUP name is not configured" return 6 fi GALERA_PORT=${GALERA_PORT:-4567} # Find a working node for ADDRESS in ${GALERA_NODES} 0; do HOST=$(echo $ADDRESS | cut -d \: -f 1 ) PORT=$(echo $ADDRESS | cut -d \: -f 2 ) PORT=${PORT:-$GALERA_PORT} nc -z $HOST $PORT >/dev/null && break done if [ ${ADDRESS} == "0" ]; then log_failure "None of the nodes in $GALERA_NODES is accessible" return 1 fi OPTIONS="-d -a gcomm://$ADDRESS" [ -n "$GALERA_GROUP" ] && OPTIONS="$OPTIONS -g $GALERA_GROUP" [ -n "$GALERA_OPTIONS" ] && OPTIONS="$OPTIONS -o $GALERA_OPTIONS" [ -n "$LOG_FILE" ] && OPTIONS="$OPTIONS -l $LOG_FILE" program_start $OPTIONS } stop() { [ "$EUID" != "0" ] && return 4 [ -r $PIDFILE ] || return 0 program_stop } restart() { stop start } # See how we were called. case "$1" in start) start ;; stop) stop ;; status) program_status ;; restart|reload) restart ;; condrestart) if status $prog > /dev/null; then stop start fi ;; *) echo $"Usage: $0 {start|stop|status|restart|reload}" exit 2 esac exit $? percona-xtradb-cluster-galera/garb/files/freebsd/garb.sh0000644000000000000000000000617712247075736023563 0ustar rootroot00000000000000#!/bin/sh # # garb.sh for rc.d usage (c) 2013 Codership Oy # $Id$ # PROVIDE: garb # REQUIRE: LOGIN # KEYWORD: shutdown # # Add the following line to /etc/rc.conf to enable Galera Arbitrator Daemon (garbd): # garb_enable (bool): Set to "NO" by default. # Set it to "YES" to enable Galera Arbitrator Daemon. # garb_galera_nodes (str): A space-separated list of node addresses (address[:port]) in the cluster # (default empty). # garb_galera_group (str): Galera cluster name, should be the same as on the rest of the nodes. # (default empty). # Optional: # garb_galera_options (str): Optional Galera internal options string (e.g. SSL settings) # see http://www.codership.com/wiki/doku.php?id=galera_parameters # (default empty). # garb_log_file (str): Log file for garbd (default empty). Optional, by default logs to syslog # garb_pid_file (str): Custum PID file path and name. # Default to "/var/run/garb.pid". # . /etc/rc.subr name="garb" rcvar=garb_enable load_rc_config $name # set defaults : ${garb_enable="NO"} : ${garb_galera_nodes=""} : ${garb_galera_group=""} : ${garb_galera_options=""} : ${garb_log_file=""} : ${garb_pid_file="/var/run/garb.pid"} procname="/usr/local/bin/garbd" command="/usr/sbin/daemon" command_args="-c -f -u nobody -p $garb_pid_file $procname" start_precmd="${name}_prestart" #start_cmd="${name}_start" start_postcmd="${name}_poststart" stop_precmd="${name}_prestop" #stop_cmd="${name}_stop" #stop_postcmd="${name}_poststop" #extra_commands="reload" #reload_cmd="${name}_reload" export LD_LIBRARY_PATH=/usr/local/lib/gcc44 garb_prestart() { [ "$(id -ur)" != "0" ] && err 4 "root rights are required to start $name" [ -r "$garb_pid_file" ] && err 0 "$procname is already running with PID $(cat $garb_pid_file)" [ -x "$procname" ] || err 5 "$procname is not found" # check that node addresses are configured [ -z "$garb_galera_nodes" ] && err 6 "List of garb_galera_nodes is not configured" [ -z "$garb_galera_group" ] && err 6 "garb_galera_group name is not configured" GALERA_PORT=${GALERA_PORT:-4567} # Find a working node for ADDRESS in ${garb_galera_nodes} 0; do HOST=$(echo $ADDRESS | cut -d \: -f 1) PORT=$(echo $ADDRESS | cut -d \: -f 2) PORT=${PORT:-$GALERA_PORT} nc -z $HOST $PORT >/dev/null 2>&1 && break done [ ${ADDRESS} == "0" ] && err 1 "None of the nodes in $garb_galera_nodes is accessible" command_args="$command_args -a gcomm://$ADDRESS" [ -n "$garb_galera_group" ] && command_args="$command_args -g $garb_galera_group" [ -n "$garb_galera_options" ] && command_args="$command_args -o $garb_galera_options" [ -n "$garb_log_file" ] && command_args="$command_args -l $garb_log_file" return 0 } garb_poststart() { local timeout=15 while [ ! -f "$garb_pid_file" -a $timeout -gt 0 ]; do timeout=$(( timeout - 1 )) sleep 1 done return 0 } garb_prestop() { [ "$(id -ur)" != "0" ] && err 4 "root rights are required to stop $name" [ -r $garb_pid_file ] || err 0 "" return 0 } run_rc_command "$1" percona-xtradb-cluster-galera/gcache/AUTHORS0000644000000000000000000000004212247075736021130 0ustar rootroot00000000000000Codership Oy percona-xtradb-cluster-galera/gcache/ChangeLog0000644000000000000000000000000012247075736021624 0ustar rootroot00000000000000percona-xtradb-cluster-galera/gcache/Makefile.am0000644000000000000000000000101112247075736022111 0ustar rootroot00000000000000# Copyright (C) 2009 Codership Oy # # This file is free software; as a special exception the author gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY, to the extent permitted by law; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # #EXTRA_DIST = reconf configure SUBDIRS = src percona-xtradb-cluster-galera/gcache/NEWS0000644000000000000000000000000012247075736020551 0ustar rootroot00000000000000percona-xtradb-cluster-galera/gcache/README0000644000000000000000000000151512247075736020746 0ustar rootroot00000000000000GCache is a library to provide transparent on-disk memory buffer cache. The purpose is to allow (almost) arbitrarily big action cache without RAM consumption. It provides the usual malloc(), realloc(), free() calls plus: void seqno_assign(void*, int64_t) - assign GCS seqno to a buffer pointed to. int64_t seqno_lock_min() - get the lowest seqno present in cache, return its value. void* seqno_get_buf(int64_t) - get a pointer to buffer with a given seqno, unlock previously locked seqno and lock the current one. void seqno_release() - release currently locked seqno. Details will be determined during development. It exploits the fact that action buffers are allocated and discarded in order close to their TO. percona-xtradb-cluster-galera/gcache/SConscript0000644000000000000000000000014012247075736022071 0ustar rootroot00000000000000# SConscript for building galerautils SConscript(Split('''src/SConscript tests/SConscript''')) percona-xtradb-cluster-galera/gcache/bootstrap.sh0000755000000000000000000000125012247075736022436 0ustar rootroot00000000000000#!/bin/sh # This script bootraps the build process for the freshly checked # working copy LOG=$0.log run_prog() { echo -n "Running $1... " $* 1>$LOG 2>&1 && echo "Ok" && rm -f $LOG || \ (echo "Failed. See $LOG"; return 1) } set -e # Make aclocal to search for m4 macros in /usr/local if test -d /usr/local/share/aclocal then ACLOCAL_OPTS=" -I /usr/local/share/aclocal " fi if test -x "$(which autoreconf)" then export ACLOCAL="aclocal $ACLOCAL_OPTS" run_prog autoreconf -fisv else run_prog libtoolize && \ run_prog aclocal $ACLOCAL_OPTS && \ run_prog autoheader configure.ac && \ run_prog automake -af && \ run_prog autoconf fi # percona-xtradb-cluster-galera/gcache/configure.ac0000644000000000000000000000612212247075736022353 0ustar rootroot00000000000000# Copyright (C) 2009 Codership Oy # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. # AC_PREREQ(2.50) AC_INIT([libgcache], [0.1.0], [info@codership.com]) AC_CONFIG_SRCDIR([config.h.in]) AC_CANONICAL_SYSTEM AC_CONFIG_HEADER([config.h]) AM_INIT_AUTOMAKE AC_PREFIX_DEFAULT(/usr/local) # Prevent configure from guessing default CFLAGS CFLAGS="$CFLAGS" CXXFLAGS="$CXXFLAGS" # Check for debug AC_ARG_ENABLE(debug, AC_HELP_STRING([--disable-debug], [disable debugging code [[default=enabled]]]),, enable_debug="yes") if test "$enable_debug" != "no" then AM_CFLAGS="-O1 -g -fno-inline" AM_CPPFLAGS="-D_FORTIFY_SOURCE=1" else AM_CFLAGS="-O3 -g" AM_CPPFLAGS="-DNDEBUG" fi AM_CONDITIONAL(ENABLE_DEBUG, test "$enable_debug" != "no") # Checks for programs. AC_PROG_AWK AC_LANG([C++]) AC_PROG_CXX AC_REQUIRE_CPP AC_PROG_LIBTOOL AC_LANG_PUSH([C]) # AM_PATH_CHECK() is broken and doesn't #include # m4-1.4.13 can no longer handle obsolete AM_PATH_CHECK so we have to switch to # PKG_CHECK_MODULES. However CentOS-5.0 has an outdated check version, so # by checking m4 version we're trying to deduce which check macro to use. m4_define(m4_version, m4_esyscmd(m4 --version | head -n1 | cut -d \ -f 4)) m4_if(m4_version_compare(m4_version,1.4.10), 1, [PKG_CHECK_MODULES([CHECK], [check >= 0.9.4])], [AM_PATH_CHECK()] ) AC_LANG_POP([C]) # Checks for libraries. AC_CHECK_LIB([pthread], [pthread_testcancel],, AC_MSG_ERROR([*** POSIX threads not found! ***])) AC_CHECK_LIB([galerautils], [gu_malloc_dbg],, AC_MSG_ERROR([*** galerautils not found! ***])) AC_CHECK_LIB([galerautils++], [main],, AC_MSG_ERROR([*** galerautils++ not found! ***])) # Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS([stdint.h stdlib.h string.h sys/time.h unistd.h endian.h byteswap.h]) # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST AC_C_INLINE AC_TYPE_SIZE_T AC_HEADER_TIME AC_STRUCT_TM AC_C_VOLATILE # Checks for library functions. AC_FUNC_ERROR_AT_LINE AC_FUNC_MALLOC AC_FUNC_REALLOC AC_CHECK_FUNCS([gettimeofday localtime_r memset strdup strerror strrchr strtol]) AC_CONFIG_FILES([Makefile src/Makefile]) AM_CFLAGS="$AM_CFLAGS -Wall -Werror -Wextra -pedantic -Wno-unused-parameter" AM_CXXFLAGS="$AM_CFLAGS -ansi -Weffc++ -Wold-style-cast -Wconversion" AM_CXXFLAGS="$AM_CXXFLAGS -fno-rtti -Wno-long-long" AM_CFLAGS="$AM_CFLAGS -std=c99" AM_LDFLAGS="-Wl,--warn-common -Wl,--fatal-warnings" AC_SUBST(AM_CFLAGS) AC_SUBST(AM_CXXFLAGS) AC_SUBST(AM_CPPFLAGS) AC_SUBST(AM_LDFLAGS) AC_OUTPUT AC_MSG_NOTICE([]) AC_MSG_NOTICE([ CFLAGS: $CFLAGS]) AC_MSG_NOTICE([AM_CFLAGS: $AM_CFLAGS]) AC_MSG_NOTICE([ CXXFLAGS: $CXXFLAGS]) AC_MSG_NOTICE([AM_CXXFLAGS: $AM_CXXFLAGS]) AC_MSG_NOTICE([ CPPFLAGS: $CPPFLAGS]) AC_MSG_NOTICE([AM_CPPFLAGS: $AM_CPPFLAGS]) AC_MSG_NOTICE([ LDFLAGS: $LDFLAGS]) AC_MSG_NOTICE([AM_LDFLAGS: $AM_LDFLAGS]) AC_MSG_NOTICE([ LIBS: $LIBS]) AC_MSG_NOTICE([]) percona-xtradb-cluster-galera/gcache/doc/0000755000000000000000000000000012247075736020631 5ustar rootroot00000000000000percona-xtradb-cluster-galera/gcache/src/0000755000000000000000000000000012247075736020653 5ustar rootroot00000000000000percona-xtradb-cluster-galera/gcache/tests/0000755000000000000000000000000012247075736021226 5ustar rootroot00000000000000percona-xtradb-cluster-galera/gcache/src/GCache.cpp0000644000000000000000000000573512247075736022503 0ustar rootroot00000000000000/* * Copyright (C) 2009-2010 Codership Oy */ #include #include #include #include "gcache_bh.hpp" #include "GCache.hpp" namespace gcache { void GCache::reset() { mem.reset(); rb.reset(); ps.reset(); mallocs = 0; reallocs = 0; seqno_locked = SEQNO_NONE; seqno_max = SEQNO_NONE; seqno2ptr.clear(); #ifndef NDEBUG buf_tracker.clear(); #endif } void GCache::constructor_common() {} GCache::GCache (gu::Config& cfg, const std::string& data_dir) : config (cfg), params (config, data_dir), mtx (), cond (), seqno2ptr (), mem (params.mem_size, seqno2ptr), rb (params.rb_name, params.rb_size, seqno2ptr), ps (params.dir_name, params.keep_pages_size, params.page_size, /* keep last page if PS is the only storage */ !((params.mem_size + params.rb_size) > 0)), mallocs (0), reallocs (0), frees (0), seqno_locked(SEQNO_NONE), seqno_max (SEQNO_NONE) #ifndef NDEBUG ,buf_tracker() #endif { constructor_common (); } GCache::~GCache () { gu::Lock lock(mtx); log_debug << "\n" << "GCache mallocs : " << mallocs << "\n" << "GCache reallocs: " << reallocs << "\n" << "GCache frees : " << frees; } /*! prints object properties */ void print (std::ostream& os) {} } #include "gcache.h" gcache_t* gcache_create (gu_config_t* conf, const char* data_dir) { gcache::GCache* gc = new gcache::GCache ( *reinterpret_cast(conf), data_dir); return reinterpret_cast(gc); } void gcache_destroy (gcache_t* gc) { gcache::GCache* gcache = reinterpret_cast(gc); delete gcache; } void* gcache_malloc (gcache_t* gc, size_t size) { gcache::GCache* gcache = reinterpret_cast(gc); return gcache->malloc (size); } void gcache_free (gcache_t* gc, const void* ptr) { gcache::GCache* gcache = reinterpret_cast(gc); gcache->free (ptr); } void* gcache_realloc (gcache_t* gc, void* ptr, size_t size) { gcache::GCache* gcache = reinterpret_cast(gc); return gcache->realloc (ptr, size); } #if DEPRECATED void gcache_seqno_init (gcache_t* gc, int64_t seqno) { gcache::GCache* gcache = reinterpret_cast(gc); gcache->seqno_init (seqno); } void gcache_seqno_assign(gcache_t* gc, const void* ptr, int64_t seqno) { gcache::GCache* gcache = reinterpret_cast(gc); gcache->seqno_assign (ptr, seqno, -1, false); } void gcache_seqno_release(gcache_t* gc, const void* ptr) { gcache::GCache* gcache = reinterpret_cast(gc); gcache->seqno_release (); } #endif percona-xtradb-cluster-galera/gcache/src/GCache.hpp0000644000000000000000000001403712247075736022503 0ustar rootroot00000000000000/* * Copyright (C) 2009-2010 Codership Oy */ #ifndef __GCACHE_H__ #define __GCACHE_H__ #include "gcache_mem_store.hpp" #include "gcache_rb_store.hpp" #include "gcache_page_store.hpp" #include #include #include #include #ifndef NDEBUG #include #endif #include namespace gcache { class GCache : public MemOps { public: /*! * Creates a new gcache file in "gcache.name" conf parameter or * in data_dir. If file already exists, it gets overwritten. */ GCache (gu::Config& cfg, const std::string& data_dir); virtual ~GCache(); /*! prints object properties */ void print (std::ostream& os); /* Resets storage */ void reset(); /* Memory allocation functions */ void* malloc (ssize_t size); void free (const void* ptr); void* realloc (void* ptr, ssize_t size); /* Seqno related functions */ /*! * Reinitialize seqno sequence (after SST or such) * Clears seqno->ptr map // and sets seqno_min to seqno. */ void seqno_reset (/*int64_t seqno*/); /*! * Assign sequence number to buffer pointed to by ptr */ void seqno_assign (const void* ptr, int64_t seqno_g, int64_t seqno_d, bool release); #if DEPRECATED /*! * Get the smallest seqno present in the cache. * Locks seqno from removal. */ int64_t seqno_get_min (); #endif /*! * Move lock to a given seqno. * @throws gu::NotFound if seqno is not in the cache. */ void seqno_lock (int64_t const seqno_g); /*! DEPRECATED * Get pointer to buffer identified by seqno. * Moves lock to the given seqno. * @throws NotFound */ const void* seqno_get_ptr (int64_t seqno_g, int64_t& seqno_d, ssize_t& size); class Buffer { public: Buffer() : ptr_(), size_(), seqno_g_(), seqno_d_() { } Buffer (const Buffer& other) : ptr_(other.ptr_), size_(other.size_), seqno_g_(other.seqno_g_), seqno_d_(other.seqno_d_) { } Buffer& operator= (const Buffer& other) { ptr_ = other.ptr_; size_ = other.size_; seqno_g_ = other.seqno_g_; seqno_d_ = other.seqno_d_; return *this; } const void* ptr() const { return ptr_; } ssize_t size() const { return size_; } int64_t seqno_g() const { return seqno_g_; } int64_t seqno_d() const { return seqno_d_; } protected: void set_ptr (const void* p) { ptr_ = p; } void set_other (ssize_t s, int64_t g, int64_t d) { size_ = s; seqno_g_ = g; seqno_d_ = d; } private: const void* ptr_; ssize_t size_; int64_t seqno_g_; int64_t seqno_d_; friend class GCache; }; /*! * Fills a vector with Buffer objects starting with seqno start * until either vector length or seqno map is exhausted. * Moves seqno lock to start. * * @retval number of buffers filled (<= v.size()) */ ssize_t seqno_get_buffers (std::vector& v, int64_t start); /*! * Releases any seqno locks present. */ void seqno_release (); /*! @throws NotFound */ void param_set (const std::string& key, const std::string& val); static size_t const PREAMBLE_LEN; private: void discard (BufferHeader*) {} void free_common (BufferHeader*bh) { void* const ptr(bh + 1); #ifndef NDEBUG std::set::iterator it = buf_tracker.find(ptr); if (it == buf_tracker.end()) { log_fatal << "Have not allocated this ptr: " << ptr; abort(); } buf_tracker.erase(it); #endif frees++; switch (bh->store) { case BUFFER_IN_MEM: mem.free (ptr); break; case BUFFER_IN_RB: rb.free (ptr); break; case BUFFER_IN_PAGE: if (gu_likely(bh->seqno_g > 0)) { discard_seqno (bh->seqno_g); } ps.free (ptr); break; } } gu::Config& config; class Params { public: Params(gu::Config&, const std::string&); std::string const rb_name; std::string const dir_name; ssize_t mem_size; ssize_t const rb_size; ssize_t page_size; ssize_t keep_pages_size; } params; gu::Mutex mtx; gu::Cond cond; typedef std::map seqno2ptr_t; seqno2ptr_t seqno2ptr; typedef seqno2ptr_t::iterator seqno2ptr_iter_t; typedef std::pair seqno2ptr_pair_t; MemStore mem; RingBuffer rb; PageStore ps; long long mallocs; long long reallocs; long long frees; int64_t seqno_locked; // int64_t seqno_min; int64_t seqno_max; #ifndef NDEBUG std::set buf_tracker; #endif void constructor_common(); void discard_seqno (int64_t); // disable copying GCache (const GCache&); GCache& operator = (const GCache&); }; } #endif /* __GCACHE_H__ */ percona-xtradb-cluster-galera/gcache/src/GCache_memops.cpp0000644000000000000000000000574112247075736024060 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #include "GCache.hpp" #include namespace gcache { void GCache::discard_seqno (int64_t seqno) { for (seqno2ptr_t::iterator i = seqno2ptr.begin(); i != seqno2ptr.end() && i->first <= seqno;) { seqno2ptr_t::iterator j = i; ++i; BufferHeader* bh = ptr2BH (j->second); seqno2ptr.erase (j); bh->seqno_g = SEQNO_ILL; // should never be reused if (gu_likely(BH_is_released(bh))) { switch (bh->store) { case BUFFER_IN_MEM: mem.discard (bh); break; case BUFFER_IN_RB: rb.discard (bh); break; case BUFFER_IN_PAGE: break; } } } } void* GCache::malloc (ssize_t size) { size += sizeof(BufferHeader); gu::Lock lock(mtx); void* ptr; mallocs++; ptr = mem.malloc(size); if (0 == ptr) ptr = rb.malloc(size); if (0 == ptr) ptr = ps.malloc(size); #ifndef NDEBUG if (0 != ptr) buf_tracker.insert (ptr); #endif return ptr; } void GCache::free (const void* ptr) { if (gu_likely(0 != ptr)) { BufferHeader* const bh(ptr2BH(ptr)); gu::Lock lock(mtx); free_common (bh); } else { log_warn << "Attempt to free a null pointer"; assert(0); } } // this will crash if ptr == 0 void* GCache::realloc (void* ptr, ssize_t size) { size += sizeof(BufferHeader); void* new_ptr = 0; BufferHeader* bh = ptr2BH(ptr); if (gu_unlikely(bh->seqno_g > 0)) // sanity check { log_fatal << "Internal program error: changing size of an ordered" << " buffer, seqno: " << bh->seqno_g << ". Aborting."; abort(); } gu::Lock lock(mtx); reallocs++; MemOps* store(0); switch (bh->store) { case BUFFER_IN_MEM: store = &mem; break; case BUFFER_IN_RB: store = &rb; break; case BUFFER_IN_PAGE: store = &ps; break; default: log_fatal << "Memory corruption: unrecognized store: " << bh->store; abort(); } new_ptr = store->realloc (ptr, size); if (0 == new_ptr) { new_ptr = malloc (size); if (0 != new_ptr) { memcpy (new_ptr, ptr, bh->size - sizeof(BufferHeader)); store->free (ptr); } } #ifndef NDEBUG if (ptr != new_ptr && 0 != new_ptr) { std::set::iterator it = buf_tracker.find(ptr); if (it != buf_tracker.end()) buf_tracker.erase(it); it = buf_tracker.find(new_ptr); } #endif return new_ptr; } } percona-xtradb-cluster-galera/gcache/src/GCache_seqno.cpp0000644000000000000000000001344112247075736023701 0ustar rootroot00000000000000/* * Copyright (C) 2009-2011 Codership Oy */ #include #include #include #include "SeqnoNone.hpp" #include "gcache_bh.hpp" #include "GCache.hpp" namespace gcache { /*! * Reinitialize seqno sequence (after SST or such) * Clears seqno->ptr map // and sets seqno_min to seqno. */ #if OLD void GCache::seqno_reset (/*int64_t seqno*/) { gu::Lock lock(mtx); if (!seqno2ptr.empty()) { int64_t old_min = seqno2ptr.begin()->first; int64_t old_max = seqno2ptr.rbegin()->first; log_info << "Discarding old history seqnos from cache: " << old_min << '-' << old_max; discard_seqno (old_max); // forget all previous seqnos } // seqno_min = seqno; } #else void GCache::seqno_reset () { gu::Lock lock(mtx); if (seqno2ptr.empty()) return; /* order is significant here */ rb.seqno_reset(); mem.seqno_reset(); seqno2ptr.clear(); } #endif /*! * Assign sequence number to buffer pointed to by ptr */ void GCache::seqno_assign (const void* const ptr, int64_t const seqno_g, int64_t const seqno_d, bool const free) { gu::Lock lock(mtx); BufferHeader* bh = ptr2BH(ptr); assert (SEQNO_NONE == bh->seqno_g); assert (SEQNO_ILL == bh->seqno_d); assert (!BH_is_released(bh)); if (gu_likely(seqno_g > seqno_max)) { seqno2ptr.insert (seqno2ptr.end(), seqno2ptr_pair_t(seqno_g, ptr)); seqno_max = seqno_g; } else { // this should never happen. seqnos should be assinged in TO. const std::pair& res( seqno2ptr.insert (seqno2ptr_pair_t(seqno_g, ptr))); if (false == res.second) { gu_throw_fatal <<"Attempt to reuse the same seqno: " << seqno_g <<". New ptr = " << ptr << ", previous ptr = " << res.first->second; } } bh->seqno_g = seqno_g; bh->seqno_d = seqno_d; if (free) free_common(bh); } #if DEPRECATED /*! * Get the smallest seqno present in the cache. * Locks seqno from removal. */ int64_t GCache::seqno_get_min () { gu::Lock lock(mtx); // This is a protection against concurrent history locking. // I don't envision the need for concurrent history access, so I don't // implement anything fancier. while (seqno_locked != SEQNO_NONE) lock.wait(cond); if (!seqno2ptr.empty()) { seqno_locked = seqno2ptr.begin()->first; return seqno_locked; } return SEQNO_NONE; } #endif /*! * Move lock to a given seqno. Throw gu::NotFound if seqno is not in cache. * @throws NotFound */ void GCache::seqno_lock (int64_t const seqno_g) { gu::Lock lock(mtx); if (seqno2ptr.find(seqno_g) == seqno2ptr.end()) throw gu::NotFound(); if (seqno_locked != SEQNO_NONE) { cond.signal(); } seqno_locked = seqno_g; } /*! * Get pointer to buffer identified by seqno. * Moves lock to the given seqno. * @throws NotFound */ const void* GCache::seqno_get_ptr (int64_t const seqno_g, int64_t& seqno_d, ssize_t& size) { const void* ptr(0); { gu::Lock lock(mtx); seqno2ptr_iter_t p = seqno2ptr.find(seqno_g); if (p != seqno2ptr.end()) { if (seqno_locked != SEQNO_NONE) { cond.signal(); } seqno_locked = seqno_g; ptr = p->second; } else { throw gu::NotFound(); } } assert (ptr); const BufferHeader* const bh (ptr2BH(ptr)); // this can result in IO seqno_d = bh->seqno_d; size = bh->size - sizeof(BufferHeader); return ptr; } ssize_t GCache::seqno_get_buffers (std::vector& v, int64_t const start) { ssize_t const max(v.size()); assert (max > 0); ssize_t found(0); { gu::Lock lock(mtx); seqno2ptr_iter_t p = seqno2ptr.find(start); if (p != seqno2ptr.end()) { if (seqno_locked != SEQNO_NONE) { cond.signal(); } seqno_locked = start; do { assert (p->first == (start + found)); assert (p->second); v[found].set_ptr(p->second); } while (++found < max && ++p != seqno2ptr.end() && p->first == (start + found)); /* the latter condition ensures seqno continuty, #643 */ } } // the following may cause IO for (ssize_t i(0); i < found; ++i) { const BufferHeader* const bh (ptr2BH(v[i].ptr())); assert (bh->seqno_g == (start + i)); v[i].set_other (bh->size - sizeof(BufferHeader), bh->seqno_g, bh->seqno_d); } return found; } /*! * Releases any history locks present. */ void GCache::seqno_release () { gu::Lock lock(mtx); seqno_locked = SEQNO_NONE; cond.signal(); } } percona-xtradb-cluster-galera/gcache/src/Makefile.am0000644000000000000000000000141412247075736022707 0ustar rootroot00000000000000# Copyright (C) 2009 Codership Oy # GCACHE_SRCS = \ FileDescriptor.cpp \ MMap.cpp \ GCache.cpp \ GCache_header.cpp \ GCache_memops.cpp \ GCache_seqno.cpp # Describe libGCACHE library target #include_HEADERS = GCache.hpp lib_LTLIBRARIES = libgcache.la libgcache_la_SOURCES = $(GCACHE_SRCS) $(GCACHE_INCS) INTERFACE = 1 REVISION = 0 AGE = 0 libgcache_la_LDFLAGS = \ @AM_LDFLAGS@ -version-info $(INTERFACE):$(REVISION):$(AGE) # Desclibe GCACHE_test binary target noinst_PROGRAMS = test test_SOURCES = test.cpp test_LDADD = $(lib_LTLIBRARIES) test_LDFLAGS = @AM_LDFLAGS@ -static .PHONY: lib test tags lib: $(lib_LTLIBRARIES) #test: GCACHE_test tags: TAGS #SUBDIRS = unit_tests # percona-xtradb-cluster-galera/gcache/src/SConscript0000644000000000000000000000127012247075736022665 0ustar rootroot00000000000000 Import('env') gcache_env = env.Clone() gcache_sources = Split (''' gcache_fd.cpp gcache_mmap.cpp GCache_seqno.cpp gcache_params.cpp gcache_page.cpp gcache_page_store.cpp gcache_rb_store.cpp gcache_mem_store.cpp GCache_memops.cpp GCache.cpp ''') gcache_env.StaticLibrary('gcache', gcache_sources) test_env = gcache_env.Clone() test_env.Prepend(LIBS=File('#/galerautils/src/libgalerautils.a')) test_env.Prepend(LIBS=File('#/galerautils/src/libgalerautils++.a')) test_env.Prepend(LIBS=File('libgcache.a')) test_env.Program(source='test.cpp') env.Append(LIBGALERA_OBJS = gcache_env.SharedObject(gcache_sources)) percona-xtradb-cluster-galera/gcache/src/SeqnoNone.hpp0000644000000000000000000000046712247075736023300 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #ifndef __GCACHE_SEQNO_NONE__ #define __GCACHE_SEQNO_NONE__ //#include #include namespace gcache { static int64_t const SEQNO_NONE = 0; static int64_t const SEQNO_ILL = -1; } #endif /* __GCACHE_SEQNO_NONE__ */ percona-xtradb-cluster-galera/gcache/src/gcache.h0000644000000000000000000000145012247075736022236 0ustar rootroot00000000000000/* * Copyright (C) 2010 Codership Oy */ /*! * @file C-interface to GCache. */ #ifndef _gcache_h_ #define _gcache_h_ #include #ifdef __cplusplus extern "C" { #endif #include "gu_config.h" typedef struct _gcache gcache_t; gcache_t* gcache_create (gu_config_t* conf, const char* data_dir); void gcache_destroy (gcache_t* gc); void* gcache_malloc (gcache_t* gc, size_t size); void gcache_free (gcache_t* gc, const void* ptr); void* gcache_realloc (gcache_t* gc, void* ptr, size_t size); #if 0 /* REMOVE */ void gcache_seqno_init (gcache_t* gc, int64_t seqno); void gcache_seqno_assign (gcache_t* gc, const void* ptr, int64_t seqno); void gcache_seqno_release(gcache_t* gc); #endif #ifdef __cplusplus } #endif #endif /* _gcache_h_ */ percona-xtradb-cluster-galera/gcache/src/gcache_bh.hpp0000644000000000000000000000266012247075736023253 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy * */ #ifndef __GCACHE_BUFHEAD__ #define __GCACHE_BUFHEAD__ #include #include #include "SeqnoNone.hpp" #include "gcache_memops.hpp" namespace gcache { static uint32_t const BUFFER_RELEASED = 1 << 0; enum StorageType { BUFFER_IN_MEM, BUFFER_IN_RB, BUFFER_IN_PAGE }; struct BufferHeader { int64_t seqno_g; int64_t seqno_d; ssize_t size; /*! total buffer size, including header */ MemOps* ctx; uint32_t flags; int32_t store; }__attribute__((__packed__)); #define BH_cast(ptr) reinterpret_cast(ptr) static inline BufferHeader* ptr2BH (const void* ptr) { return (static_cast(const_cast(ptr)) - 1); } static inline void BH_clear (BufferHeader* const bh) { memset (bh, 0, sizeof(BufferHeader)); } static inline void BH_release (BufferHeader* const bh) { bh->flags |= BUFFER_RELEASED; } static inline bool BH_is_released (const BufferHeader* const bh) { return (bh->flags & BUFFER_RELEASED); } #if DEPRECATED static inline void BH_cancel (BufferHeader* bh) { bh->flags |= BUFFER_CANCELED; } static inline bool BH_is_canceled (BufferHeader* bh) { return (bh->flags & BUFFER_CANCELED); } #endif } #endif /* __GCACHE_BUFHEAD__ */ percona-xtradb-cluster-galera/gcache/src/gcache_fd.cpp0000644000000000000000000001307712247075736023252 0ustar rootroot00000000000000/* * Copyright (C) 2009-2010 Codership Oy * */ #include "gcache_fd.hpp" #include #if !defined(_XOPEN_SOURCE) && !defined(__APPLE__) #define _XOPEN_SOURCE 600 #endif #include #include #include #include #include #ifndef O_CLOEXEC // CentOS < 6.0 does not have it #define O_CLOEXEC 0 #endif #ifndef O_NOATIME #define O_NOATIME 0 #endif namespace gcache { static const int OPEN_FLAGS = O_RDWR | O_NOATIME | O_CLOEXEC; static const int CREATE_FLAGS = OPEN_FLAGS | O_CREAT /*| O_TRUNC*/; FileDescriptor::FileDescriptor (const std::string& fname, bool sync_) : value (open (fname.c_str(), OPEN_FLAGS, S_IRUSR | S_IWUSR)), name (fname), size (lseek (value, 0, SEEK_END)), sync (sync_) { constructor_common(); } FileDescriptor::FileDescriptor (const std::string& fname, size_t length, bool allocate, bool sync_) : value (open (fname.c_str(), CREATE_FLAGS, S_IRUSR | S_IWUSR)), name (fname), size (length), sync (sync_) { constructor_common(); off_t const current_size(lseek (value, 0, SEEK_END)); if (current_size < size) { if (allocate) { // reserve space that hasn't been reserved prealloc (current_size); } else { write_byte (size - 1); // reserve size } } else if (current_size > size) { log_info << "Truncating '" << name << "' to " << size << " bytes."; if (ftruncate(value, size)) { gu_throw_error(errno) << "Failed to truncate '" << name << "' to " << size << " bytes."; } } else { log_info << "Reusing existing '" << name << "'."; } } void FileDescriptor::constructor_common() { if (value < 0) { gu_throw_error(errno) << "Failed to open file '" + name + '\''; } #if !defined(__APPLE__) /* Darwin does not have posix_fadvise */ /* benefits are questionable int err(posix_fadvise (value, 0, size, POSIX_FADV_SEQUENTIAL)); if (err != 0) { log_warn << "Failed to set POSIX_FADV_SEQUENTIAL on " << name << ": " << err << " (" << strerror(err) << ")"; } */ #endif log_debug << "Opened file '" << name << "'"; log_debug << "File descriptor: " << value; } FileDescriptor::~FileDescriptor () { if (sync && fsync(value) != 0) { int const err (errno); log_error << "Failed to flush file '" << name << "': " << gu::to_string(err) << " (" << strerror(err) << '\''; } if (close(value) != 0) { int const err (errno); log_error << "Failed to close file '" << name << "': " << gu::to_string(err) << " (" << strerror(err) << '\''; } else { log_debug << "Closed file '" << name << "'"; } } void FileDescriptor::flush () const { log_debug << "Flushing file '" << name << "'"; if (fsync (value) < 0) { gu_throw_error(errno) << "fsync() failed on '" + name + '\''; } log_debug << "Flushed file '" << name << "'"; } bool FileDescriptor::write_byte (off_t offset) { unsigned char const byte (0); if (lseek (value, offset, SEEK_SET) != offset) gu_throw_error(errno) << "lseek() failed on '" << name << '\''; if (write (value, &byte, sizeof(byte)) != sizeof(byte)) gu_throw_error(errno) << "write() failed on '" << name << '\''; return true; } /*! prealloc() fallback */ void FileDescriptor::write_file (off_t const start) { off_t const page_size (sysconf (_SC_PAGE_SIZE)); // last byte of the start page off_t offset = (start / page_size + 1) * page_size - 1; // off_t const diff (size - offset); // log_info << "Preallocating " << diff << '/' << size << " bytes in '" // << name << "'..."; while (offset < size && write_byte (offset)) { offset += page_size; } if (offset > size && write_byte (size - 1) && fsync (value) == 0) { // log_info << "Preallocating " << diff << '/' << size // << " bytes in '" << name << "' done."; return; } gu_throw_error (errno) << "File preallocation failed"; } void FileDescriptor::prealloc(off_t const start) { off_t const diff (size - start); log_info << "Preallocating " << diff << '/' << size << " bytes in '" << name << "'..."; #if defined(__APPLE__) if (0 != fcntl (value, F_SETSIZE, size) && 0 != ftruncate (value, size)) #else if (0 != posix_fallocate (value, start, diff)) #endif { if (EINVAL == errno && start >= 0 && diff > 0) { // FS does not support the operation, try physical write write_file (start); } else { gu_throw_error (errno) << "File preallocation failed"; } } } } percona-xtradb-cluster-galera/gcache/src/gcache_fd.hpp0000644000000000000000000000253512247075736023254 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy * */ #ifndef __GCACHE_FILEDES__ #define __GCACHE_FILEDES__ #include #include namespace gcache { class FileDescriptor { public: /* open existing file */ FileDescriptor (const std::string& fname, bool sync = true); /* (re)create file */ FileDescriptor (const std::string& fname, size_t length, bool allocate = true, bool sync = true); virtual ~FileDescriptor (); int get() const { return value; }; const std::string& get_name() const { return name; }; off_t get_size() const { return size; }; void flush() const; private: const int value; const std::string name; const off_t size; const bool sync; // sync on close bool write_byte (off_t offset); void write_file (off_t start = 0); void prealloc (off_t start = 0); void constructor_common(); FileDescriptor (const FileDescriptor&); FileDescriptor& operator = (const FileDescriptor); }; } #endif /* __GCACHE_FILEDES__ */ percona-xtradb-cluster-galera/gcache/src/gcache_mem_store.cpp0000644000000000000000000000236212247075736024646 0ustar rootroot00000000000000/* * Copyright (C) 2010-2011 Codership Oy */ #include "gcache_mem_store.hpp" namespace gcache { bool MemStore::have_free_space (ssize_t size) { while ((size_ + size > max_size_) && !seqno2ptr_.empty()) { /* try to free some released bufs */ seqno2ptr_iter_t const i (seqno2ptr_.begin()); BufferHeader* const bh (ptr2BH (i->second)); if (BH_is_released(bh)) /* discard buffer */ { seqno2ptr_.erase(i); bh->seqno_g = SEQNO_NONE; switch (bh->store) { case BUFFER_IN_RB: bh->ctx->discard(bh); break; case BUFFER_IN_MEM: discard(bh); break; } } } return (size_ + size <= max_size_); } void MemStore::seqno_reset() { for (std::set::iterator buf(allocd_.begin()); buf != allocd_.end();) { std::set::iterator tmp(buf); ++buf; BufferHeader* const bh(ptr2BH(*tmp)); if (bh->seqno_g != SEQNO_NONE) { assert (BH_is_released(bh)); size_ -= bh->size; ::free (bh); allocd_.erase (tmp); } } } } /* namespace gcache */ percona-xtradb-cluster-galera/gcache/src/gcache_mem_store.hpp0000644000000000000000000000705012247075736024652 0ustar rootroot00000000000000/* * Copyright (C) 2010-2011 Codership Oy */ /*! @file mem store class */ #ifndef _gcache_mem_store_hpp_ #define _gcache_mem_store_hpp_ #include "gcache_memops.hpp" #include "gcache_fd.hpp" #include "gcache_mmap.hpp" #include "gcache_bh.hpp" #include #include namespace gcache { class MemStore : public MemOps { typedef std::map seqno2ptr_t; typedef seqno2ptr_t::iterator seqno2ptr_iter_t; public: MemStore (ssize_t max_size, seqno2ptr_t& seqno2ptr) : max_size_ (max_size), size_ (0), allocd_ (), seqno2ptr_(seqno2ptr) {} void reset () { for (std::set::iterator buf(allocd_.begin()); buf != allocd_.end(); ++buf) { ::free (*buf); } allocd_.clear(); size_ = 0; } ~MemStore () { reset(); } void* malloc (ssize_t size) { if (size > max_size_ || have_free_space(size) == false) return 0; assert (size_ + size <= max_size_); BufferHeader* bh (BH_cast (::malloc (size))); if (gu_likely(0 != bh)) { allocd_.insert(bh); bh->size = size; bh->seqno_g = SEQNO_NONE; bh->seqno_d = SEQNO_ILL; bh->flags = 0; bh->store = BUFFER_IN_MEM; bh->ctx = this; size_ += size; return (bh + 1); } return 0; } void free (const void* ptr) { if (gu_likely (0 != ptr)) { BufferHeader* const bh(ptr2BH(ptr)); assert(bh->size > 0); assert(bh->size <= size_); assert(bh->store == BUFFER_IN_MEM); assert(bh->ctx == this); BH_release (bh); if (SEQNO_NONE == bh->seqno_g) discard (bh); } } void* realloc (void* ptr, ssize_t size) { BufferHeader* bh(0); ssize_t old_size(0); if (ptr) { bh = ptr2BH(ptr); assert (SEQNO_NONE == bh->seqno_g); old_size = bh->size; } ssize_t const diff_size(size - old_size); if (size > max_size_ || have_free_space(diff_size) == false) return 0; assert (size_ + diff_size <= max_size_); void* tmp = ::realloc (bh, size); if (tmp) { allocd_.erase(bh); allocd_.insert(tmp); bh = BH_cast(tmp); assert (bh->size == old_size); bh->size = size; size_ += diff_size; return (bh + 1); } return 0; } void discard (BufferHeader* bh) { assert (BH_is_released(bh)); size_ -= bh->size; ::free (bh); allocd_.erase(bh); } void set_max_size (ssize_t size) { max_size_ = size; } void seqno_reset(); // for unit tests only ssize_t _allocd () const { return size_; } private: bool have_free_space (ssize_t size); ssize_t max_size_; ssize_t size_; std::set allocd_; seqno2ptr_t& seqno2ptr_; }; } #endif /* _gcache_mem_store_hpp_ */ percona-xtradb-cluster-galera/gcache/src/gcache_memops.hpp0000644000000000000000000000130612247075736024156 0ustar rootroot00000000000000/* * Copyright (C) 2010 Codership Oy */ /*! @file memory operations interface */ #ifndef _gcache_memops_hpp_ #define _gcache_memops_hpp_ #include namespace gcache { struct BufferHeader; class MemOps { public: MemOps() {} virtual ~MemOps() {} virtual void* malloc (ssize_t size) = 0; virtual void free (const void* ptr) = 0; virtual void* realloc (void* ptr, ssize_t size) = 0; virtual void discard (BufferHeader* bh) = 0; virtual void reset () = 0; }; } #endif /* _gcache_memops_hpp_ */ percona-xtradb-cluster-galera/gcache/src/gcache_mmap.cpp0000644000000000000000000000454512247075736023613 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy * */ #include "gcache_mmap.hpp" #include #include #include // to avoid -Wold-style-cast extern "C" { static const void* const GCACHE_MAP_FAILED = MAP_FAILED; } namespace gcache { MMap::MMap (const FileDescriptor& fd) : size (fd.get_size()), ptr (mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_NORESERVE, fd.get(), 0)), mapped (ptr != GCACHE_MAP_FAILED) { if (!mapped) { gu_throw_error(errno) << "mmap() on '" << fd.get_name() << "' failed"; } #if !defined(__sun__) && !defined(__APPLE__) && !defined(__FreeBSD__) /* Solaris, Darwin, and FreeBSD do not have MADV_DONTFORK */ if (posix_madvise (ptr, size, MADV_DONTFORK)) { int const err(errno); log_warn << "Failed to set MADV_DONTFORK on " << fd.get_name() << ": " << err << " (" << strerror(err) << ")"; } #endif /* benefits are questionable if (posix_madvise (ptr, size, MADV_SEQUENTIAL)) { int const err(errno); log_warn << "Failed to set MADV_SEQUENTIAL on " << fd.get_name() << ": " << err << " (" << strerror(err) << ")"; } */ log_debug << "Memory mapped: " << ptr << " (" << size << " bytes)"; } void MMap::sync () const { log_info << "Flushing memory map to disk..."; if (msync (ptr, size, MS_SYNC) < 0) { gu_throw_error(errno) << "msync(" << ptr << ", " << size << ") failed"; } } void MMap::unmap () { if (munmap (ptr, size) < 0) { gu_throw_error(errno) << "munmap(" << ptr << ", " << size << ") failed"; } mapped = false; log_debug << "Memory unmapped: " << ptr << " (" << size <<" bytes)"; } void MMap::dont_need() const { if (posix_madvise(reinterpret_cast(ptr), size, MADV_DONTNEED)) { log_warn << "Failed to set MADV_DONTNEED on " << ptr << ": " << errno << " (" << strerror(errno) << ')'; } } MMap::~MMap () { if (mapped) unmap(); } } percona-xtradb-cluster-galera/gcache/src/gcache_mmap.hpp0000644000000000000000000000112112247075736023603 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy * */ #ifndef __GCACHE_MMAP__ #define __GCACHE_MMAP__ #include "gcache_fd.hpp" namespace gcache { class MMap { public: size_t const size; void* const ptr; MMap (const FileDescriptor& fd); virtual ~MMap (); void sync() const; void unmap(); void dont_need() const; private: bool mapped; // This class is definitely non-copyable MMap (const MMap&); MMap& operator = (const MMap); }; } #endif /* __GCACHE_MMAP__ */ percona-xtradb-cluster-galera/gcache/src/gcache_page.cpp0000644000000000000000000000637012247075736023573 0ustar rootroot00000000000000/* * Copyright (C) 2010 Codership Oy */ /*! @file page file class implementation */ #include "gcache_page.hpp" // for posix_fadvise() #if !defined(_XOPEN_SOURCE) #define _XOPEN_SOURCE 600 #endif #include static ssize_t check_size (ssize_t size) { if (size < 0) gu_throw_error(EINVAL) << "Negative page size: " << size; return size; } void gcache::Page::reset () { if (gu_unlikely (used_ > 0)) { log_fatal << "Attempt to reset a page '" << name() << "' used by " << used_ << " buffers. Aborting."; abort(); } space_ = mmap_.size; next_ = static_cast(mmap_.ptr); } void gcache::Page::drop_fs_cache() const { mmap_.dont_need(); #if !defined(__APPLE__) int const err (posix_fadvise (fd_.get(), 0, fd_.get_size(), POSIX_FADV_DONTNEED)); if (err != 0) { log_warn << "Failed to set POSIX_FADV_DONTNEED on " << fd_.get_name() << ": " << err << " (" << strerror(err) << ")"; } #endif } gcache::Page::Page (const std::string& name, ssize_t size) : fd_ (name, check_size(size), false, false), mmap_ (fd_), next_ (static_cast(mmap_.ptr)), space_(mmap_.size), used_ (0) { log_info << "Created page " << name << " of size " << space_ << " bytes"; BH_clear (reinterpret_cast(next_)); } void* gcache::Page::malloc (ssize_t size) { if (size <= space_) { BufferHeader* bh(BH_cast(next_)); bh->size = size; bh->seqno_g = SEQNO_NONE; bh->seqno_d = SEQNO_ILL; bh->ctx = this; bh->flags = 0; bh->store = BUFFER_IN_PAGE; space_ -= size; next_ += size; used_++; #ifndef NDEBUG if (space_ >= static_cast(sizeof(BufferHeader))) { BH_clear (BH_cast(next_)); assert (reinterpret_cast(bh + 1) < next_); } assert (next_ <= static_cast(mmap_.ptr) + mmap_.size); #endif return (bh + 1); } else { log_debug << "Failed to allocate " << size << " bytes, space left: " << space_ << " bytes, total allocated: " << next_ - static_cast(mmap_.ptr); return 0; } } void* gcache::Page::realloc (void* ptr, ssize_t size) { BufferHeader* bh(ptr2BH(ptr)); if (bh == BH_cast(next_ - bh->size)) // last buffer, can shrink and expand { ssize_t const diff_size (size - bh->size); if (gu_likely (diff_size < space_)) { bh->size += diff_size; space_ -= diff_size; next_ += diff_size; BH_clear (BH_cast(next_)); return ptr; } else return 0; // not enough space in this page } else { if (gu_likely(size > bh->size)) { void* const ret (malloc (size)); if (ret) { memcpy (ret, ptr, bh->size - sizeof(BufferHeader)); used_--; } return ret; } else { // do nothing, we can't shrink the buffer, it is locked return ptr; } } } percona-xtradb-cluster-galera/gcache/src/gcache_page.hpp0000644000000000000000000000263512247075736023600 0ustar rootroot00000000000000/* * Copyright (C) 2010 Codership Oy */ /*! @file page file class */ #ifndef _gcache_page_hpp_ #define _gcache_page_hpp_ #include "gcache_memops.hpp" #include "gcache_fd.hpp" #include "gcache_mmap.hpp" #include "gcache_bh.hpp" #include namespace gcache { class Page : public MemOps { public: Page (const std::string& name, ssize_t size); ~Page () {} void* malloc (ssize_t size); void free (const void* ptr) { assert (ptr > mmap_.ptr); assert (ptr <= (static_cast(mmap_.ptr) + mmap_.size)); assert (used_ > 0); used_--; BH_release (ptr2BH(ptr)); } void* realloc (void* ptr, ssize_t size); void discard (BufferHeader* ptr) {} ssize_t used () const { return used_; } ssize_t size () const /* total page size */ { return mmap_.size - sizeof(BufferHeader); } const std::string& name() const { return fd_.get_name(); } void reset (); /* Drop filesystem cache on the file */ void drop_fs_cache() const; private: FileDescriptor fd_; MMap mmap_; uint8_t* next_; ssize_t space_; ssize_t used_; Page(const gcache::Page&); Page& operator=(const gcache::Page&); }; } #endif /* _gcache_page_hpp_ */ percona-xtradb-cluster-galera/gcache/src/gcache_page_store.cpp0000644000000000000000000001335212247075736025005 0ustar rootroot00000000000000/* * Copyright (C) 2010 Codership Oy */ /*! @file page store implementation */ #include "gcache_page_store.hpp" #include "gcache_bh.hpp" #include #include #include static const std::string base_name ("gcache.page."); static std::string make_base_name (const std::string& dir_name) { if (dir_name.empty()) { return base_name; } else { if (dir_name[dir_name.length() - 1] == '/') { return (dir_name + base_name); } else { return (dir_name + '/' + base_name); } } } static std::string make_page_name (const std::string& base_name, ssize_t count) { std::ostringstream os; os << base_name << std::setfill ('0') << std::setw (6) << count; return os.str(); } static void* remove_file (void* __restrict__ arg) { char* const file_name (static_cast(arg)); if (NULL != file_name) { if (remove (file_name)) { int err = errno; log_error << "Failed to remove page file '" << file_name << "': " << gu::to_string(err) << " (" << strerror(err) << ")"; } else { log_info << "Deleted page " << file_name; } free (file_name); } else { log_error << "Null file name in " << __FUNCTION__; } pthread_exit(NULL); } bool gcache::PageStore::delete_page () { Page* const page = pages_.front(); if (page->used() > 0) return false; pages_.pop_front(); char* const file_name(strdup(page->name().c_str())); total_size_ -= page->size(); if (current_ == page) current_ = 0; delete page; #ifdef GCACHE_DETACH_THREAD pthread_t delete_thr_; #else if (delete_thr_ != pthread_t(-1)) pthread_join (delete_thr_, NULL); #endif /* GCACHE_DETACH_THERAD */ int err = pthread_create (&delete_thr_, &delete_page_attr_, remove_file, file_name); if (0 != err) { delete_thr_ = pthread_t(-1); gu_throw_error(err) << "Failed to create page file deletion thread"; } return true; } /* Deleting pages only from the beginning kinda means that some free pages * can be locked in the middle for a while. Leaving it like that for simplicity * for now. */ inline void gcache::PageStore::cleanup () { while (total_size_ > keep_size_ && pages_.size() > keep_page_ && delete_page()) {} } void gcache::PageStore::reset () { while (pages_.size() > 0 && delete_page()) {}; } inline void gcache::PageStore::new_page (ssize_t size) { Page* const page = new Page (make_page_name (base_name_, count_), size); pages_.push_back (page); total_size_ += size; current_ = page; count_++; } gcache::PageStore::PageStore (const std::string& dir_name, ssize_t keep_size, ssize_t page_size, bool keep_page) : base_name_ (make_base_name(dir_name)), keep_size_ (keep_size), page_size_ (page_size), keep_page_ (keep_page), count_ (0), pages_ (), current_ (0), total_size_(0), delete_page_attr_() #ifndef GCACHE_DETACH_THREAD , delete_thr_(pthread_t(-1)) #endif /* GCACHE_DETACH_THREAD */ { int err = pthread_attr_init (&delete_page_attr_); if (0 != err) { gu_throw_error(err) << "Failed to initialize page file deletion " << "thread attributes"; } #ifdef GCACHE_DETACH_THREAD err = pthread_attr_setdetachstate (&delete_page_attr_, PTHREAD_CREATE_DETACHED); if (0 != err) { pthread_attr_destroy (&delete_page_attr_); gu_throw_error(err) << "Failed to set DETACHED attribute to " << "page file deletion thread"; } #endif /* GCACHE_DETACH_THREAD */ } gcache::PageStore::~PageStore () { try { while (pages_.size() && delete_page()) {}; #ifndef GCACHE_DETACH_THREAD if (delete_thr_ != pthread_t(-1)) pthread_join (delete_thr_, NULL); #endif /* GCACHE_DETACH_THREAD */ } catch (gu::Exception& e) { log_error << e.what() << " in ~PageStore()"; // abort() ? } if (pages_.size() > 0) { log_error << "Could not delete " << pages_.size() << " page files: some buffers are still \"mmapped\"."; } pthread_attr_destroy (&delete_page_attr_); } inline void* gcache::PageStore::malloc_new (ssize_t size) { void* ret = 0; try { new_page (page_size_ > size ? page_size_ : size); ret = current_->malloc (size); cleanup(); } catch (gu::Exception& e) { log_error << "Cannot create new cache page (out of disk space?): " << e.what(); // abort(); } return ret; } void* gcache::PageStore::malloc (ssize_t size) { if (gu_likely (0 != current_)) { register void* ret = current_->malloc (size); if (gu_likely(0 != ret)) return ret; current_->drop_fs_cache(); } return malloc_new (size); } void gcache::PageStore::free (const void* ptr) { Page* page = static_cast((ptr2BH(ptr))->ctx); free_page_ptr(page, ptr); } void* gcache::PageStore::realloc (void* ptr, ssize_t size) { void* ret = 0; Page* page = static_cast((ptr2BH(ptr))->ctx); ret = page->realloc (ptr, size); if (0 != ret) return ret; ret = malloc_new (size); if (gu_likely(0 != ret)) { ssize_t ptr_size = (ptr2BH(ptr))->size - sizeof(BufferHeader); memcpy (ret, ptr, size > ptr_size ? ptr_size : size); free_page_ptr (page, ptr); } return ret; } percona-xtradb-cluster-galera/gcache/src/gcache_page_store.hpp0000644000000000000000000000405312247075736025010 0ustar rootroot00000000000000/* * Copyright (C) 2010 Codership Oy */ /*! @file page store class */ #ifndef _gcache_page_store_hpp_ #define _gcache_page_store_hpp_ #include "gcache_memops.hpp" #include "gcache_page.hpp" #include #include namespace gcache { class PageStore : public MemOps { public: PageStore (const std::string& dir_name, ssize_t keep_size, ssize_t page_size, bool keep_page); ~PageStore (); void* malloc (ssize_t size); void free (const void* ptr); void* realloc (void* ptr, ssize_t size); void discard (BufferHeader* bh) {}; void reset(); ssize_t count() const { return count_; } // for unit tests void set_page_size (ssize_t size) { page_size_ = size; } void set_keep_size (ssize_t size) { keep_size_ = size; } private: std::string const base_name_; /* /.../.../gcache.page. */ ssize_t keep_size_; /* how much pages to keep after freeing*/ ssize_t page_size_; /* min size of the individual page */ bool const keep_page_; /* whether to keep the last page */ ssize_t count_; std::deque pages_; Page* current_; ssize_t total_size_; pthread_attr_t delete_page_attr_; #ifndef GCACHE_DETACH_THREAD pthread_t delete_thr_; #endif /* GCACHE_DETACH_THREAD */ void new_page (ssize_t size); // returns true if a page could be deleted bool delete_page (); // cleans up extra pages. void cleanup (); void* malloc_new (ssize_t size) ; void free_page_ptr (Page* page, const void* ptr) { page->free(ptr); if (0 == page->used()) cleanup(); } PageStore(const gcache::PageStore&); PageStore& operator=(const gcache::PageStore&); }; } #endif /* _gcache_page_store_hpp_ */ percona-xtradb-cluster-galera/gcache/src/gcache_params.cpp0000644000000000000000000001070712247075736024141 0ustar rootroot00000000000000/* * Copyright (C) 2009-2011 Codership Oy */ #include "GCache.hpp" static const std::string GCACHE_PARAMS_DIR ("gcache.dir"); static const std::string GCACHE_PARAMS_RB_NAME ("gcache.name"); static const std::string GCACHE_DEFAULT_BASENAME ("galera.cache"); static const std::string GCACHE_PARAMS_MEM_SIZE ("gcache.mem_size"); static const ssize_t GCACHE_DEFAULT_MEM_SIZE (0); static const std::string GCACHE_PARAMS_RB_SIZE ("gcache.size"); static const ssize_t GCACHE_DEFAULT_RB_SIZE (128 << 20); // 128Mb static const std::string GCACHE_PARAMS_PAGE_SIZE ("gcache.page_size"); static const ssize_t GCACHE_DEFAULT_PAGE_SIZE (GCACHE_DEFAULT_RB_SIZE); static const std::string GCACHE_PARAMS_KEEP_PAGES_SIZE("gcache.keep_pages_size"); static const ssize_t GCACHE_DEFAULT_KEEP_PAGES_SIZE(0); static const std::string& name_value (gu::Config& cfg, const std::string& data_dir) { std::string dir(""); if (cfg.has(GCACHE_PARAMS_DIR)) { dir = cfg.get(GCACHE_PARAMS_DIR); } else { if (!data_dir.empty()) dir = data_dir; cfg.set (GCACHE_PARAMS_DIR, dir); } try { return cfg.get (GCACHE_PARAMS_RB_NAME); } catch (gu::NotFound&) { if (dir.empty()) { cfg.set (GCACHE_PARAMS_RB_NAME, GCACHE_DEFAULT_BASENAME); } else { cfg.set (GCACHE_PARAMS_RB_NAME, dir + '/' + GCACHE_DEFAULT_BASENAME); } } return cfg.get (GCACHE_PARAMS_RB_NAME); } static ssize_t size_value (gu::Config& cfg, const std::string& key, ssize_t def) { try { return cfg.get (key); } catch (gu::NotFound&) { cfg.set (key, def); } return cfg.get (key); } gcache::GCache::Params::Params (gu::Config& cfg, const std::string& data_dir) : rb_name (name_value (cfg, data_dir)), dir_name (cfg.get(GCACHE_PARAMS_DIR)), mem_size (size_value (cfg, GCACHE_PARAMS_MEM_SIZE, GCACHE_DEFAULT_MEM_SIZE)), rb_size (size_value (cfg, GCACHE_PARAMS_RB_SIZE, GCACHE_DEFAULT_RB_SIZE)), page_size (size_value (cfg, GCACHE_PARAMS_PAGE_SIZE, GCACHE_DEFAULT_PAGE_SIZE)), keep_pages_size (size_value (cfg, GCACHE_PARAMS_KEEP_PAGES_SIZE, GCACHE_DEFAULT_KEEP_PAGES_SIZE)) {} void gcache::GCache::param_set (const std::string& key, const std::string& val) { if (key == GCACHE_PARAMS_RB_NAME) { gu_throw_error(EPERM) << "Can't change ring buffer name in runtime."; } else if (key == GCACHE_PARAMS_DIR) { gu_throw_error(EPERM) << "Can't change data dir in runtime."; } else if (key == GCACHE_PARAMS_MEM_SIZE) { ssize_t tmp_size = gu::Config::from_config(val); if (tmp_size < 0) gu_throw_error(EINVAL) << "Negative memory buffer size"; gu::Lock lock(mtx); /* locking here serves two purposes: ensures atomic setting of config * and params.ram_size and syncs with malloc() method */ config.set(key, tmp_size); params.mem_size = tmp_size; mem.set_max_size (params.mem_size); } else if (key == GCACHE_PARAMS_RB_SIZE) { gu_throw_error(EPERM) << "Can't change ring buffer size in runtime."; } else if (key == GCACHE_PARAMS_PAGE_SIZE) { ssize_t tmp_size = gu::Config::from_config(val); if (tmp_size < 0) gu_throw_error(EINVAL) << "Negative page buffer size"; gu::Lock lock(mtx); /* locking here serves two purposes: ensures atomic setting of config * and params.ram_size and syncs with malloc() method */ config.set(key, tmp_size); params.page_size = tmp_size; ps.set_page_size (params.page_size); } else if (key == GCACHE_PARAMS_KEEP_PAGES_SIZE) { ssize_t tmp_size = gu::Config::from_config(val); if (tmp_size < 0) gu_throw_error(EINVAL) << "Negative keep pages size"; gu::Lock lock(mtx); /* locking here serves two purposes: ensures atomic setting of config * and params.ram_size and syncs with malloc() method */ config.set(key, tmp_size); params.keep_pages_size = tmp_size; ps.set_keep_size (params.keep_pages_size); } else { throw gu::NotFound(); } } percona-xtradb-cluster-galera/gcache/src/gcache_rb_store.cpp0000644000000000000000000002622712247075736024501 0ustar rootroot00000000000000/* * Copyright (C) 2010 Codership Oy */ #include "gcache_rb_store.hpp" #include #include namespace gcache { static size_t check_size (ssize_t s) { if (s < 0) gu_throw_error(EINVAL) << "Negative cache file size: " << s; return s + RingBuffer::pad_size() + sizeof(BufferHeader); } void RingBuffer::reset() { first_ = start_; next_ = start_; BH_clear (reinterpret_cast(next_)); size_free_ = size_cache_; size_used_ = 0; size_trail_= 0; // mallocs_ = 0; // reallocs_ = 0; } void RingBuffer::constructor_common() {} RingBuffer::RingBuffer (const std::string& name, ssize_t size, std::map & seqno2ptr) : fd_ (name, check_size(size)), mmap_ (fd_), open_ (true), preamble_ (static_cast(mmap_.ptr)), header_ (reinterpret_cast(preamble_ + PREAMBLE_LEN)), start_ (reinterpret_cast(header_ + HEADER_LEN)), end_ (reinterpret_cast(preamble_ + mmap_.size)), first_ (start_), next_ (first_), size_cache_(end_ - start_ - sizeof(BufferHeader)), size_free_ (size_cache_), size_used_ (0), size_trail_(0), // mallocs_ (0), // reallocs_ (0), seqno2ptr_ (seqno2ptr) { constructor_common (); BH_clear (reinterpret_cast(next_)); } RingBuffer::~RingBuffer () { open_ = false; mmap_.sync(); mmap_.unmap(); } inline void RingBuffer::discard_seqno (int64_t seqno) { for (seqno2ptr_t::iterator i = seqno2ptr_.begin(); i != seqno2ptr_.end() && i->first <= seqno;) { seqno2ptr_t::iterator j = i; ++i; BufferHeader* bh = ptr2BH (j->second); seqno2ptr_.erase (j); // this buffer will never ever be accessed by seqno again anyways bh->seqno_g = SEQNO_NONE; if (gu_likely (BH_is_released(bh))) { switch (bh->store) { case BUFFER_IN_MEM: bh->ctx->discard(bh); break; case BUFFER_IN_RB: discard (bh); break; } } } } // returns pointer to buffer data area or 0 if no space found BufferHeader* RingBuffer::get_new_buffer (ssize_t const size) { uint8_t* ret = next_; ssize_t const size_next (size + sizeof(BufferHeader)); if (ret >= first_) { assert (0 == size_trail_); // try to find space at the end register ssize_t const end_size(end_ - ret); if (end_size >= size_next) { goto found_space; } else { // no space at the end, go from the start size_trail_ = end_size; ret = start_; } } while ((first_ - ret) < size_next) { // try to discard first buffer to get more space BufferHeader* bh = BH_cast(first_); // this will be automatically true also when (first_ == next_) if (!BH_is_released(bh)) { // can't free any more space, so no buffer, and check trailing if (next_ > first_) size_trail_ = 0; return 0; } if (bh->seqno_g != SEQNO_NONE) discard_seqno (bh->seqno_g); first_ += bh->size; if (0 == (BH_cast(first_))->size /*&& first_ != next_*/) { // empty header and not next: check if we fit at the end // and roll over if not first_ = start_; size_trail_ = 0; // we're now contiguous: first_ <= next_ if ((end_ - ret) >= size_next) { goto found_space; } else { ret = start_; } } } #ifndef NDEBUG if ((first_ - ret) < size_next) { log_fatal << "Assertion ((first - ret) >= size_next) failed: " << std::endl << "first offt = " << (first_ - start_) << std::endl << "next offt = " << (next_ - start_) << std::endl << "end offt = " << (end_ - start_) << std::endl << "ret offt = " << (ret - start_) << std::endl << "size_next = " << size_next << std::endl; abort(); } #endif found_space: size_used_ += size; assert (size_used_ <= size_cache_); size_free_ -= size; assert (size_free_ >= 0); next_ = ret + size; assert (next_ + sizeof(BufferHeader) <= end_); BH_clear (BH_cast(next_)); BufferHeader* bh = BH_cast(ret); bh->size = size; bh->seqno_g = SEQNO_NONE; bh->seqno_d = SEQNO_ILL; bh->flags = 0; bh->store = BUFFER_IN_RB; bh->ctx = this; return bh; } void* RingBuffer::malloc (ssize_t size) { // We can reliably allocate continuous buffer which is 1/2 // of a total cache space. So compare to half the space if (size <= (size_cache_ / 2) && size <= (size_cache_ - size_used_)) { BufferHeader* const bh (get_new_buffer (size)); // mallocs_++; if (gu_likely (0 != bh)) return (bh + 1); } return 0; // "out of memory" } void RingBuffer::free (const void* ptr) { if (gu_likely(NULL != ptr)) { BufferHeader* bh = ptr2BH(ptr); size_used_ -= bh->size; assert(size_used_ >= 0); // space is unused but not free // space counted as free only when it is erased from the map BH_release (bh); if (SEQNO_NONE == bh->seqno_g) discard (bh); } } void* RingBuffer::realloc (void* ptr, ssize_t size) { // We can reliably allocate continuous buffer which is twice as small // as total cache area. So compare to half the space if (size > (size_cache_ / 2)) return 0; BufferHeader* bh = ptr2BH(ptr); // reallocs_++; // first check if we can grow this buffer by allocating // adjacent buffer { uint8_t* adj_ptr = reinterpret_cast(bh) + bh->size; size_t adj_size = size - bh->size; if (adj_ptr == next_) { void* const adj_buf (get_new_buffer (adj_size)); if (adj_ptr == adj_buf) { bh->size = size; return ptr; } else // adjacent buffer allocation failed, return it back { next_ = adj_ptr; size_used_ -= adj_size; size_free_ += adj_size; } } } // find non-adjacent buffer void* ptr_new = malloc (size); if (ptr_new != 0) { memcpy (ptr_new, ptr, bh->size - sizeof(BufferHeader)); free (ptr); } return ptr_new; } void RingBuffer::seqno_reset() { if (size_cache_ == size_free_) return; /* find the last seqno'd RB buffer */ BufferHeader* bh(0); for (seqno2ptr_t::reverse_iterator r(seqno2ptr_.rbegin()); r != seqno2ptr_.rend(); ++r) { BufferHeader* const b(ptr2BH(r->second)); if (BUFFER_IN_RB == b->store) { assert(BH_is_released(b)); bh = b; break; } } if (!bh) return; /* This should be called in isolation, when all seqno'd buffers are * freed, and the only unreleased buffers should come only from new * configuration. Find the first unreleased buffer. There should be * no seqno'd buffers after it. */ while (BH_is_released(bh)) // next_ is never released - no endless loop { first_ = reinterpret_cast(bh) + bh->size; bh = BH_cast(first_); if (gu_unlikely (0 == bh->size && first_ != next_)) { first_ = start_; bh = BH_cast(first_); } } if (first_ == next_) { log_info << "GCache DEBUG: RingBuffer::seqno_reset(): full reset"; /* empty RB, reset it completely */ reset(); return; } assert ((BH_cast(first_))->size > 0); assert ((BH_cast(first_))->seqno_g == SEQNO_NONE); assert (!BH_is_released(BH_cast(first_))); /* Find how much space remains */ ssize_t const old(size_free_); if (first_ < next_) { /* start_ first_ next_ end_ * | |###########| | */ size_used_ = next_ - first_; size_free_ = size_cache_ - size_used_; size_trail_= 0; } else { /* start_ next_ first_ end_ * |#######| |#####| | * ^size_trail_ */ size_free_ = first_ - next_ + size_trail_; size_used_ = size_cache_ - size_free_; } log_info << "GCache DEBUG: RingBuffer::seqno_reset(): discarded " << (size_free_ - old) << " bytes"; /* There is a small but non-0 probability that some seqno'd buffers * are locked withing yet unreleased aborted local actions. * Seek all the way to next_ and invalidate seqnos */ long total(0); long locked(0); bh = BH_cast(first_ + (BH_cast(first_))->size); while (bh != BH_cast(next_)) { if (gu_likely (bh->size > 0)) { total++; if (bh->seqno_g != SEQNO_NONE) { assert (BH_is_released(bh)); bh->seqno_g = SEQNO_NONE; locked++; } bh = BH_cast(reinterpret_cast(bh) + bh->size); } else { assert (BH_cast(next_) < bh); bh = BH_cast(start_); } } log_info << "GCache DEBUG: RingBuffer::seqno_reset(): found " << locked << '/' << total << " locked buffers"; } std::ostream& operator<< (std::ostream& os, const RingBuffer& rb) { os << "\nstart_ : " << reinterpret_cast(rb.start_) << "\nend_ : " << reinterpret_cast(rb.end_) << "\nfirst : " << rb.first_ - rb.start_ << "\nnext : " << rb.next_ - rb.start_ << "\nsize : " << rb.size_cache_ << "\nfree : " << rb.size_free_ << "\nused : " << rb.size_used_; return os; } } percona-xtradb-cluster-galera/gcache/src/gcache_rb_store.hpp0000644000000000000000000000475512247075736024510 0ustar rootroot00000000000000/* * Copyright (C) 2010 Codership Oy */ /*! @file ring buffer storage class */ #ifndef _gcache_rb_store_hpp_ #define _gcache_rb_store_hpp_ #include "gcache_memops.hpp" #include "gcache_fd.hpp" #include "gcache_mmap.hpp" #include "gcache_bh.hpp" #include #include #include namespace gcache { class RingBuffer : public MemOps { public: RingBuffer (const std::string& name, ssize_t size, std::map& seqno2ptr); ~RingBuffer (); void* malloc (ssize_t size); void free (const void* ptr); void* realloc (void* ptr, ssize_t size); void discard (BufferHeader* bh) { size_free_ += bh->size; } ssize_t size () const { return size_cache_; } ssize_t rb_size () const { return fd_.get_size(); } const std::string& rb_name() const { return fd_.get_name(); } void reset(); void seqno_reset(); void discard_seqno (int64_t seqno); static ssize_t pad_size() { RingBuffer* rb(0); // cppcheck-suppress nullPounter return (PREAMBLE_LEN * sizeof(*(rb->preamble_)) + HEADER_LEN * sizeof(*(rb->header_))); } private: static ssize_t const PREAMBLE_LEN = 1024; static ssize_t const HEADER_LEN = 32; FileDescriptor fd_; MMap mmap_; bool open_; char* const preamble_; // ASCII text preamble int64_t* const header_; // cache binary header uint8_t* const start_; // start of cache area uint8_t* const end_; // first byte after cache area uint8_t* first_; // pointer to the first (oldest) buffer uint8_t* next_; // pointer to the next free space ssize_t const size_cache_; ssize_t size_free_; ssize_t size_used_; ssize_t size_trail_; typedef std::map seqno2ptr_t; seqno2ptr_t& seqno2ptr_; BufferHeader* get_new_buffer (ssize_t size); void constructor_common(); RingBuffer(const gcache::RingBuffer&); RingBuffer& operator=(const gcache::RingBuffer&); friend std::ostream& operator<< (std::ostream&, const RingBuffer&); }; std::ostream& operator<< (std::ostream&, const RingBuffer&); } #endif /* _gcache_rb_store_hpp_ */ percona-xtradb-cluster-galera/gcache/src/pasture/0000755000000000000000000000000012247075736022336 5ustar rootroot00000000000000percona-xtradb-cluster-galera/gcache/src/test.cpp0000644000000000000000000000162412247075736022341 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy * */ #include #include "GCache.hpp" using namespace gcache; int main (int argc, char* argv[]) { int ret = 0; std::string fname = "test.cache"; gu_conf_self_tstamp_on (); gu_conf_debug_on (); log_info << "Start"; log_debug << "DEBUG output enabled"; if (argc > 1) fname.assign(argv[1]); // take supplied file name if any gu::Config conf("gcache.name = test.cache; gcache.size = 16K"); GCache* cache = new GCache (conf, ""); log_info << ""; log_info << "...do something..."; log_info << ""; delete cache; log_info << "Exit: " << ret; try { throw gu::Exception ("My test exception", EINVAL); } catch (gu::Exception& e) { log_info << "Exception caught: " << e.what() << ", errno: " << e.get_errno(); } return ret; } percona-xtradb-cluster-galera/gcache/src/pasture/Cond.hpp0000644000000000000000000000142512247075736023734 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #ifndef __GCACHE_COND__ #define __GCACHE_COND__ #include #include #include namespace gcache { class Cond { friend class Lock; protected: pthread_cond_t cond; long ref_count; public: Cond () throw() : ref_count(0) { pthread_cond_init (&cond, NULL); }; ~Cond () { while (EBUSY == pthread_cond_destroy(&cond)) { usleep (100); }; }; inline void signal () { if (ref_count > 0) pthread_cond_signal (&cond); } inline void broadcast () { if (ref_count > 0) pthread_cond_broadcast (&cond); } }; } #endif percona-xtradb-cluster-galera/gcache/src/pasture/Exception.cpp0000644000000000000000000000101512247075736024775 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy * */ #include #include "Exception.hpp" namespace gcache { Exception::Exception (const char* msg_str, int errno) throw() : _errno(errno) { strncpy (msg, msg_str, EXCEPTION_MSG_SIZE); msg[EXCEPTION_MSG_SIZE - 1] = '\0'; } ///* Exception::Exception (const char* msg_str) throw() : _errno(0) { strncpy (msg, msg_str, EXCEPTION_MSG_SIZE); msg[EXCEPTION_MSG_SIZE - 1] = '\0'; } //*/ } percona-xtradb-cluster-galera/gcache/src/pasture/Exception.hpp0000644000000000000000000000121512247075736025004 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy * */ #ifndef __GCACHE_EXCEPTION__ #define __GCACHE_EXCEPTION__ #include namespace gcache { class Exception: public std::exception { private: #define EXCEPTION_MSG_SIZE 256 char msg[EXCEPTION_MSG_SIZE]; const int _errno; public: Exception (const char* msg_str, int) throw(); Exception (const char* msg_str) throw(); virtual ~Exception () throw() {}; virtual const char* what () const throw() { return msg; }; int get_errno () const throw() { return _errno; }; }; } #endif // __GCACHE_EXCEPTION__ percona-xtradb-cluster-galera/gcache/src/pasture/Lock.hpp0000644000000000000000000000200212247075736023731 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy * */ #ifndef __GCACHE_LOCK__ #define __GCACHE_LOCK__ #include #include #include "Exception.hpp" #include #include "Mutex.hpp" #include "Cond.hpp" namespace gcache { class Lock { private: pthread_mutex_t* value; public: Lock (Mutex& mtx) { value = &mtx.value; int err = pthread_mutex_lock (value); if (err) { std::string msg = "Mutex lock failed: "; msg = msg + strerror(err); throw Exception(msg.c_str(), err); } }; virtual ~Lock () { pthread_mutex_unlock (value); log_debug << "Unlocked mutex " << value; }; inline void wait (Cond& cond) { cond.ref_count++; pthread_cond_wait (&(cond.cond), value); cond.ref_count--; }; }; } #endif /* __GCACHE_LOCK__ */ percona-xtradb-cluster-galera/gcache/src/pasture/Logger.cpp0000644000000000000000000000412112247075736024257 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy * * This code is based on an excellent article at Dr.Dobb's: * http://www.ddj.com/cpp/201804215?pgno=1 */ #include #include #include #include #include "Logger.hpp" namespace gcache { void Logger::enable_tstamp (bool yes) { do_timestamp = yes; } void Logger::enable_debug (bool yes) { if (yes) { max_level = LOG_DEBUG; } else { max_level = LOG_INFO; } } void Logger::default_logger (int lvl, const char* msg) { fputs (msg, stderr); fflush (stderr); } void Logger::set_logger (LogCallback cb) { if (0 == cb) { logger = default_logger; } else { logger = cb; } } static const char* level_str[LOG_MAX] = { "FATAL: ", "ERROR: ", " WARN: ", " INFO: ", "DEBUG: " }; void Logger::prepare_default() { if (do_timestamp) { using namespace std; struct tm date; struct timeval time; gettimeofday (&time, NULL); localtime_r (&time.tv_sec, &date); // save original format flags // ios_base::fmtflags original_flags = os.flags(); os << date.tm_year + 1900 << '-' << setw(2) << setfill('0') << date.tm_mon + 1 << '-' << setw(2) << setfill('0') << date.tm_mday << ' ' << setw(2) << setfill('0') << date.tm_hour << ':' << setw(2) << setfill('0') << date.tm_min << ':' << setw(2) << setfill('0') << date.tm_sec << '.' << setw(3) << setfill('0') << ((int)time.tv_usec / 1000) << ' '; // restore original format flags // os.flags(original_flags); } os << level_str[level]; } bool Logger::do_timestamp = false; LogLevel Logger::max_level = LOG_INFO; LogCallback Logger::logger = default_logger; } percona-xtradb-cluster-galera/gcache/src/pasture/Logger.hpp0000644000000000000000000000557512247075736024302 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy * * This code is based on an excellent article at Dr.Dobb's: * http://www.ddj.com/cpp/201804215?pgno=1 */ #ifndef __GCACHE_LOGGER__ #define __GCACHE_LOGGER__ #include //#include namespace gcache { // some portability stuff #ifdef WSREP_H enum LogLevel { LOG_FATAL = WSREP_LOG_FATAL, LOG_ERROR = WSREP_LOG_ERROR, LOG_WARN = WSREP_LOG_WARN, LOG_INFO = WSREP_LOG_INFO, LOG_DEBUG = WSREP_LOG_DEBUG, LOG_MAX }; typedef wsrep_log_cb_t LogCallback; #else enum LogLevel { LOG_FATAL, LOG_ERROR, LOG_WARN, LOG_INFO, LOG_DEBUG, LOG_MAX }; typedef void (*LogCallback) (int, const char*); #endif class Logger { public: Logger() {}; virtual inline ~Logger(); // this function returns a stream for further logging. // std::ostringstream& get(TLogLevel level = logINFO); inline std::ostringstream& get(const LogLevel lvl, const char* file, const char* func, const int line); public: static void enable_tstamp (bool); static void enable_debug (bool); static void set_logger (LogCallback); static inline bool no_log (LogLevel lvl) { return (lvl > max_level); }; protected: std::ostringstream os; private: Logger(const Logger&); Logger& operator =(const Logger&); private: static LogLevel max_level; static bool do_timestamp; static LogCallback logger; static void default_logger (int, const char*); void prepare_default (); LogLevel level; }; Logger::~Logger() { os << std::endl; logger (level, os.str().c_str()); } std::ostringstream& Logger::get(const LogLevel lvl, const char* file, const char* func, const int line) { // fancy original stuff // os << std::string(level > logDEBUG ? 0 : level - logDEBUG, '\t'); level = lvl; if (logger == default_logger) { // put in timestamp and log level line prepare_default(); } return os; } } #define LOG(level) \ if (Logger::no_log(level)) ; \ else Logger().get(level, __FILE__, __PRETTY_FUNCTION__, __LINE__) #define log_fatal LOG(LOG_FATAL) #define log_error LOG(LOG_ERROR) #define log_warn LOG(LOG_WARN) #define log_info LOG(LOG_INFO) #define log_debug LOG(LOG_DEBUG) #endif // __GCACHE_LOGGER__ percona-xtradb-cluster-galera/gcache/src/pasture/Mutex.hpp0000644000000000000000000000151712247075736024155 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy * */ #ifndef __GCACHE_MUTEX__ #define __GCACHE_MUTEX__ #include #include #include "Exception.hpp" #include namespace gcache { class Mutex { friend class Lock; protected: pthread_mutex_t value; public: Mutex () { pthread_mutex_init (&value, NULL); }; virtual ~Mutex () { int err = pthread_mutex_destroy (&value); if (gu_unlikely(err != 0)) { log_fatal << "pthread_mutex_destroy() failed: " << err << " (" << strerror(err) << "). Aborting."; ::abort(); } log_debug << "Destroyed mutex " << &value; }; }; } #endif /* __GCACHE_MUTEX__ */ percona-xtradb-cluster-galera/gcache/src/pasture/gcache_rb_header.cpp0000644000000000000000000001352512247075736026255 0ustar rootroot00000000000000/* * Copyright (C) 2010 Codership Oy */ #include "gcache_rb_header.hpp" #include #include #include #include namespace gcache { enum records { HEADER_LEN = 0, HEADER_VERSION, FILE_OPEN, FILE_SIZE, DATA_OFFSET, FIRST_OFFSET, NEXT_OFFSET, SEQNO_MIN, SEQNO_MAX }; void RingBuffer::header_read () { std::ostringstream error; error << "Can't load gcache data file: "; if (version != static_cast(header[HEADER_VERSION])) { error << "unsupported version: " << header[HEADER_VERSION]; throw gu::Exception (error.str().c_str(), ECANCELED); } if (mmap.size != static_cast(header[FILE_SIZE])) { error << "file size does not match, declared: " << header[FILE_SIZE] << ", real: " << mmap.size; throw gu::Exception (error.str().c_str(), ECANCELED); } ssize_t data_offset = start - static_cast(mmap.ptr); if (data_offset != header[DATA_OFFSET]) { error << "data offset " << header[DATA_OFFSET] << " does not match derived: " << data_offset; throw gu::Exception (error.str().c_str(), ECANCELED); } if (true == header[FILE_OPEN]) { log_warn << "Gcache data file was not gracefully closed, " << "discarding data."; reset_cache (); return; } if (size_cache <= header[FIRST_OFFSET]) { log_warn << "Bogus first buffer offset, discarding data."; reset_cache (); return; } if (size_cache <= header[NEXT_OFFSET]) { log_warn << "Bogus next buffer offset, discarding data."; reset_cache (); return; } seqno_min = header[SEQNO_MIN]; seqno_max = header[SEQNO_MAX]; if ((seqno_min == SEQNO_NONE && seqno_max != SEQNO_NONE) || (seqno_min != SEQNO_NONE && seqno_max == SEQNO_NONE)) { log_warn << "Inconsistent seqno's: " << seqno_min << ", " << seqno_max << ", discarding data."; reset_cache (); return; } if (seqno_min > seqno_max) { log_warn << "Minimum seqno > maximum seqno (" << seqno_min << " > " << seqno_max << "), discarding data."; reset_cache (); return; } first = start + header[FIRST_OFFSET]; next = start + header[NEXT_OFFSET]; /* Validate all buffers and populate seqno map */ log_info << "Validating cached buffers..."; uint8_t* buf = first; BufferHeader* bh = reinterpret_cast(buf); while (bh->size > 0) { if (bh->seqno != SEQNO_NONE) { seqno2ptr.insert (std::pair(bh->seqno, bh + 1)); } if (!BH_is_released (bh)) { log_warn << "Unreleased buffer found. Releasing."; BH_release (bh); } buf += bh->size; if (buf > (end - sizeof(BufferHeader))) break; bh = reinterpret_cast(buf); if (0 == bh->size && buf != next) { // buffer list continues from the beginning buf = start; bh = reinterpret_cast(buf); } } if (buf != next) { log_warn << "Cache metadata corrupted: failed to validate " << "allocated buffers. Discarding data."; reset_cache(); return; } log_info << "Validating cached buffers done."; if (seqno_min != SEQNO_NONE) { log_info << "Checking for gaps in sequence numbers..."; for (int64_t seqno = seqno_min; seqno <= seqno_max; seqno++) { if (seqno2ptr.find(seqno) == seqno2ptr.end()) { log_warn << "Discontinuity in sequence numbers: " << seqno << " is missing. Discarding data."; reset_cache(); return; } } log_info << "Checking for gaps in sequence numbers..."; } } void RingBuffer::header_write () { header[HEADER_LEN] = header_len; header[HEADER_VERSION] = version; header[FILE_OPEN] = open; header[FILE_SIZE] = mmap.size; header[DATA_OFFSET] = start - static_cast(mmap.ptr); header[FIRST_OFFSET] = first - start; header[NEXT_OFFSET] = next - start; header[SEQNO_MIN] = seqno_min; header[SEQNO_MAX] = seqno_max; } void RingBuffer::preamble_write () { std::ostringstream pstream; pstream << "* GCache data file *" << std::endl << "--------------------" << std::endl << "Version : " << header[HEADER_VERSION] << std::endl << "Size : " << header[FILE_SIZE] << "bytes" << std::endl << "Closed : " << (header[FILE_OPEN]?"no":"yes") << std::endl << "Data offset : " << header[DATA_OFFSET] << std::endl << "First buffer : " << header[FIRST_OFFSET] << std::endl << "Next buffer : " << header[NEXT_OFFSET] << std::endl << "Min. seqno : " << header[SEQNO_MIN] << std::endl << "Max. seqno : " << header[SEQNO_MAX] << std::endl << "Ordered buffers : " << (header[SEQNO_MAX] - header[SEQNO_MIN]) << std::endl << "--------------------" << std::endl; strncpy (preamble, pstream.str().c_str(), PREAMBLE_LEN - 1); preamble[PREAMBLE_LEN - 1] = '\0'; } } percona-xtradb-cluster-galera/gcache/tests/SConscript0000644000000000000000000000112612247075736023240 0ustar rootroot00000000000000 Import('check_env') env = check_env.Clone() env.Prepend(LIBS=File('#/galerautils/src/libgalerautils.a')) env.Prepend(LIBS=File('#/galerautils/src/libgalerautils++.a')) env.Prepend(LIBS=File('#/gcache/src/libgcache.a')) gcache_tests = env.Program(target = 'gcache_tests', source = Glob('*.cpp')) # source = Split(''' # gcache_tests.cpp # ''')) stamp="gcache_tests.passed" env.Test(stamp, gcache_tests) env.Alias("test", stamp) Clean(gcache_tests, ['#/gcache_tests.log', '#/gcache.page.000000', '#/rb_test']) percona-xtradb-cluster-galera/gcache/tests/gcache_mem_test.cpp0000644000000000000000000000346312247075736025047 0ustar rootroot00000000000000/* * Copyright (C) 2011 Codership Oy * * $Id$ */ #include "gcache_mem_store.hpp" #include "gcache_bh.hpp" #include "gcache_mem_test.hpp" using namespace gcache; START_TEST(test1) { ssize_t const bh_size (sizeof(gcache::BufferHeader)); ssize_t const mem_size (3 + 2*bh_size); std::map s2p; MemStore ms(mem_size, s2p); void* buf1 = ms.malloc (1 + bh_size); fail_if (NULL == buf1); BufferHeader* bh1(ptr2BH(buf1)); fail_if (bh1->seqno_g != SEQNO_NONE); fail_if (BH_is_released(bh1)); void* buf2 = ms.malloc (1 + bh_size); fail_if (NULL == buf2); fail_if (buf1 == buf2); void* buf3 = ms.malloc (1 + bh_size); fail_if (NULL != buf3); buf1 = ms.realloc (buf1, 2 + bh_size); fail_if (NULL == buf1); bh1 = ptr2BH(buf1); fail_if (bh1->seqno_g != SEQNO_NONE); fail_if (BH_is_released(bh1)); BufferHeader* bh2(ptr2BH(buf2)); fail_if (bh2->seqno_g != SEQNO_NONE); fail_if (BH_is_released(bh2)); bh2->seqno_g = 1; /* freeing seqno'd buffer should only release it, but not discard */ ms.free (buf2); fail_if (!BH_is_released(bh2)); buf3 = ms.malloc (1 + bh_size); fail_if (NULL != buf3); /* discarding a buffer should finally free some space for another */ ms.discard(bh2); buf3 = ms.malloc (1 + bh_size); fail_if (NULL == buf3); /* freeing unseqno'd buffer should free space immeditely */ ms.free (buf1); void* buf4 = ms.malloc (2 + bh_size); fail_if (NULL == buf4); ms.free (buf3); ms.free (buf4); fail_if (ms._allocd()); } END_TEST Suite* gcache_mem_suite() { Suite* s = suite_create("gcache::MemStore"); TCase* tc; tc = tcase_create("test"); tcase_add_test(tc, test1); suite_add_tcase(s, tc); return s; } percona-xtradb-cluster-galera/gcache/tests/gcache_mem_test.hpp0000644000000000000000000000036312247075736025050 0ustar rootroot00000000000000/* * Copyright (C) 2011 Codership Oy * * $Id$ */ #ifndef __gcache_mem_test_hpp__ #define __gcache_mem_test_hpp__ extern "C" { #include } extern Suite* gcache_mem_suite(); #endif // __gcache_mem_test_hpp__ percona-xtradb-cluster-galera/gcache/tests/gcache_page_test.cpp0000644000000000000000000000421212247075736025176 0ustar rootroot00000000000000/* * Copyright (C) 2010 Codership Oy * * $Id$ */ #include "gcache_page_store.hpp" #include "gcache_bh.hpp" #include "gcache_page_test.hpp" START_TEST(test1) { const char* const dir_name = ""; ssize_t const bh_size = sizeof(gcache::BufferHeader); ssize_t const keep_size = 1; ssize_t const page_size = 2 + bh_size; gcache::PageStore ps (dir_name, keep_size, page_size, false); mark_point(); void* buf = ps.malloc (3 + bh_size); fail_if (0 == buf); void* tmp = ps.realloc (buf, 2 + bh_size); fail_if (buf != tmp); tmp = ps.realloc (buf, 4 + bh_size); // here new page should be allocated fail_if (0 == tmp); fail_if (buf == tmp); ps.free (tmp); } END_TEST START_TEST(test2) { const char* const dir_name = ""; ssize_t const bh_size = sizeof(gcache::BufferHeader); ssize_t const keep_size = 1; ssize_t page_size = (1 << 20) + bh_size; gcache::PageStore ps (dir_name, keep_size, page_size, false); mark_point(); uint8_t* buf = static_cast(ps.malloc (page_size)); fail_if (0 == buf); while (--page_size) { buf[page_size] = page_size; } mark_point(); ps.free (buf); } END_TEST START_TEST(test3) // check that all page size is efficiently used { const char* const dir_name = ""; ssize_t const keep_size = 1; ssize_t const page_size = 1024; gcache::PageStore ps (dir_name, keep_size, page_size, false); mark_point(); ssize_t ptr_size = (page_size / 2); void* ptr1 = ps.malloc (ptr_size); fail_if (0 == ptr1); void* ptr2 = ps.malloc (ptr_size); fail_if (0 == ptr2); fail_if (ps.count() != 1, "ps.count() = %zd, expected 1", ps.count()); // check that ptr2 is adjacent to ptr1 void* tmp = static_cast(ptr1) + ptr_size; fail_if (tmp != ptr2, "tmp = %p, ptr2 = %p", tmp, ptr2); } END_TEST Suite* gcache_page_suite() { Suite* s = suite_create("gcache::PageStore"); TCase* tc; tc = tcase_create("test"); tcase_add_test(tc, test1); tcase_add_test(tc, test2); tcase_add_test(tc, test3); suite_add_tcase(s, tc); return s; } percona-xtradb-cluster-galera/gcache/tests/gcache_page_test.hpp0000644000000000000000000000036712247075736025212 0ustar rootroot00000000000000/* * Copyright (C) 2010 Codership Oy * * $Id$ */ #ifndef __gcache_page_test_hpp__ #define __gcache_page_test_hpp__ extern "C" { #include } extern Suite* gcache_page_suite(); #endif // __gcache_page_test_hpp__ percona-xtradb-cluster-galera/gcache/tests/gcache_rb_test.cpp0000644000000000000000000000406112247075736024667 0ustar rootroot00000000000000/* * Copyright (C) 2011 Codership Oy * * $Id$ */ #include "gcache_rb_store.hpp" #include "gcache_bh.hpp" #include "gcache_rb_test.hpp" using namespace gcache; START_TEST(test1) { std::string const rb_name = "rb_test"; ssize_t const bh_size = sizeof(gcache::BufferHeader); ssize_t const rb_size (4 + 2*bh_size); std::map s2p; RingBuffer rb(rb_name, rb_size, s2p); fail_if (rb.size() != rb_size, "Expected %zd, got %zd", rb_size, rb.size()); void* buf1 = rb.malloc (3 + bh_size); fail_if (NULL != buf1); // > 1/2 size buf1 = rb.malloc (1 + bh_size); fail_if (NULL == buf1); BufferHeader* bh1(ptr2BH(buf1)); fail_if (bh1->seqno_g != SEQNO_NONE); fail_if (BH_is_released(bh1)); void* buf2 = rb.malloc (2 + bh_size); fail_if (NULL == buf2); fail_if (BH_is_released(bh1)); BufferHeader* bh2(ptr2BH(buf2)); fail_if (bh2->seqno_g != SEQNO_NONE); fail_if (BH_is_released(bh2)); void* tmp = rb.realloc (buf1, 2 + bh_size); fail_if (NULL != tmp); rb.free (buf2); fail_if (!BH_is_released(bh2)); tmp = rb.realloc (buf1, 2 + bh_size); fail_if (NULL != tmp); rb.free (buf1); fail_if (!BH_is_released(bh1)); buf1 = rb.malloc (1 + bh_size); fail_if (NULL == buf1); tmp = rb.realloc (buf1, 2 + bh_size); fail_if (NULL == tmp); fail_if (tmp != buf1); buf2 = rb.malloc (1 + bh_size); fail_if (NULL == buf2); tmp = rb.realloc (buf2, 2 + bh_size); fail_if (NULL == tmp); fail_if (tmp != buf2); tmp = rb.malloc (1 + bh_size); fail_if (NULL != tmp); rb.free(buf1); fail_if (!BH_is_released(ptr2BH(buf1))); rb.free(buf2); fail_if (!BH_is_released(ptr2BH(buf2))); tmp = rb.malloc (2 + bh_size); fail_if (NULL == tmp); mark_point(); } END_TEST Suite* gcache_rb_suite() { Suite* ts = suite_create("gcache::RbStore"); TCase* tc = tcase_create("test"); tcase_set_timeout(tc, 60); tcase_add_test(tc, test1); suite_add_tcase(ts, tc); return ts; } percona-xtradb-cluster-galera/gcache/tests/gcache_rb_test.hpp0000644000000000000000000000035712247075736024700 0ustar rootroot00000000000000/* * Copyright (C) 2011 Codership Oy * * $Id$ */ #ifndef __gcache_rb_test_hpp__ #define __gcache_rb_test_hpp__ extern "C" { #include } extern Suite* gcache_rb_suite(); #endif // __gcache_rb_test_hpp__ percona-xtradb-cluster-galera/gcache/tests/gcache_tests.cpp0000644000000000000000000000165712247075736024377 0ustar rootroot00000000000000// Copyright (C) 2010 Codership Oy #include #include #include extern "C" { #include } #include "gcache_tests.hpp" int main(int argc, char* argv[]) { bool no_fork = (argc >= 2 && std::string(argv[1]) == "nofork"); FILE* log_file = 0; if (!no_fork) { log_file = fopen (LOG_FILE, "w"); if (!log_file) return EXIT_FAILURE; gu_conf_set_log_file (log_file); } gu_conf_debug_on(); int failed = 0; for (int i = 0; suites[i] != 0; ++i) { SRunner* sr = srunner_create(suites[i]()); if (no_fork) srunner_set_fork_status(sr, CK_NOFORK); srunner_run_all(sr, CK_NORMAL); failed += srunner_ntests_failed(sr); srunner_free(sr); } if (log_file != 0) fclose(log_file); printf ("Total tests failed: %d\n", failed); return failed == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } percona-xtradb-cluster-galera/gcache/tests/gcache_tests.hpp0000644000000000000000000000104312247075736024371 0ustar rootroot00000000000000// Copyright (C) 2010-2011 Codership Oy // $Id$ /*! * @file: package specific part of the main test file. */ #ifndef __gcache_tests_hpp__ #define __gcache_tests_hpp__ #define LOG_FILE "gcache_tests.log" #include "gcache_mem_test.hpp" #include "gcache_rb_test.hpp" #include "gcache_page_test.hpp" extern "C" { #include } typedef Suite *(*suite_creator_t)(void); static suite_creator_t suites[] = { gcache_mem_suite, gcache_rb_suite, gcache_page_suite, 0 }; #endif /* __gcache_tests_hpp__ */ percona-xtradb-cluster-galera/gcomm/SConscript0000644000000000000000000000014112247075736021762 0ustar rootroot00000000000000# SCons build script for building gcomm SConscript(Split('''src/SConscript test/SConscript''')) percona-xtradb-cluster-galera/gcomm/doc/0000755000000000000000000000000012247075736020521 5ustar rootroot00000000000000percona-xtradb-cluster-galera/gcomm/src/0000755000000000000000000000000012247075736020543 5ustar rootroot00000000000000percona-xtradb-cluster-galera/gcomm/test/0000755000000000000000000000000012247075736020733 5ustar rootroot00000000000000percona-xtradb-cluster-galera/gcomm/doc/Doxyfile0000644000000000000000000014370512247075736022241 0ustar rootroot00000000000000# Doxyfile 1.4.6 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = GComm # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = 0.2.3 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = ./ # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, # Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, # Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, # Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, # Swedish, and Ukrainian. OUTPUT_LANGUAGE = English # This tag can be used to specify the encoding used in the generated output. # The encoding is not always determined by the language that is chosen, # but also whether or not the output is meant for Windows or non-Windows users. # In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES # forces the Windows encoding (this is the default for the Windows binary), # whereas setting the tag to NO uses a Unix-style encoding (the default for # all platforms other than Windows). USE_WINDOWS_ENCODING = NO # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like the Qt-style comments (thus requiring an # explicit @brief command for a brief description. JAVADOC_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the DETAILS_AT_TOP tag is set to YES then Doxygen # will output the detailed description near the top, like JavaDoc. # If set to NO, the detailed description appears after the member # documentation. DETAILS_AT_TOP = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for Java. # For instance, namespaces will be presented as packages, qualified scopes # will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to # include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from the # version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = ../src ../src/gcomm # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py FILE_PATTERNS = *.c *.h *.hpp # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES (the default) # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES (the default) # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compressed HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # If the GENERATE_TREEVIEW tag is set to YES, a side panel will be # generated containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = YES # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will # generate a call dependency graph for every global function or class method. # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected # functions only using the \callgraph command. CALL_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width # (in pixels) of the graphs generated by dot. If a graph becomes larger than # this value, doxygen will try to truncate the graph, so that it fits within # the specified constraint. Beware that most browsers cannot cope with very # large images. MAX_DOT_GRAPH_WIDTH = 1024 # The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height # (in pixels) of the graphs generated by dot. If a graph becomes larger than # this value, doxygen will try to truncate the graph, so that it fits within # the specified constraint. Beware that most browsers cannot cope with very # large images. MAX_DOT_GRAPH_HEIGHT = 1024 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that a graph may be further truncated if the graph's # image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH # and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), # the graph is not depth-constrained. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, which results in a white background. # Warning: Depending on the platform used, enabling this option may lead to # badly anti-aliased labels on the edges of a graph (i.e. they become hard to # read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO percona-xtradb-cluster-galera/gcomm/src/SConscript0000644000000000000000000000151612247075736022560 0ustar rootroot00000000000000# Import('env') libgcomm_sources = [ 'conf.cpp', 'defaults.cpp', 'evs_consensus.cpp', 'evs_input_map2.cpp', 'evs_message2.cpp', 'evs_node.cpp', 'evs_proto.cpp', 'gmcast.cpp', 'gmcast_proto.cpp', 'histogram.cpp', 'pc.cpp', 'pc_proto.cpp', 'protonet.cpp', 'protostack.cpp', 'transport.cpp', 'uuid.cpp', 'view.cpp', 'socket.cpp'] libgcomm_env = env.Clone() libgcomm_env.Append(CXXFLAGS = ' -fno-strict-aliasing') if '-DHAVE_ASIO_HPP' in libgcomm_env['CPPFLAGS']: # ASIO sources need to be built with relaxed C++ flags libgcomm_sources.extend([ 'asio_tcp.cpp', 'asio_udp.cpp', 'asio_protonet.cpp']) libgcomm_env.StaticLibrary('gcomm', libgcomm_sources) env.Append(LIBGALERA_OBJS = libgcomm_env.SharedObject(libgcomm_sources)) percona-xtradb-cluster-galera/gcomm/src/asio_addr.hpp0000644000000000000000000000251312247075736023202 0ustar rootroot00000000000000/* * Copyright (C) 2010 Codership Oy */ #ifndef GCOMM_ASIO_ADDR_HPP #define GCOMM_ASIO_ADDR_HPP #include "gu_exception.hpp" #include "asio_protonet.hpp" #include #include namespace gcomm { static inline std::string escape_addr(const asio::ip::address& addr) { if (addr.is_v4()) { return addr.to_v4().to_string(); } else { return "[" + addr.to_v6().to_string() + "]"; } } static inline std::string unescape_addr(const std::string& addr) { std::string ret(addr); size_t pos(ret.find('[')); if (pos != std::string::npos) ret.erase(pos, 1); pos = ret.find(']'); if (pos != std::string::npos) ret.erase(pos, 1); return ret; } static inline std::string anyaddr(const asio::ip::address& addr) { if (addr.is_v4() == true) { return addr.to_v4().any().to_string(); } else { return addr.to_v6().any().to_string(); } gu_throw_fatal; } } template void set_fd_options(S& socket) { long flags(FD_CLOEXEC); if (fcntl(socket.native(), F_SETFD, flags) == -1) { gu_throw_error(errno) << "failed to set FD_CLOEXEC"; } } #endif // GCOMM_ASIO_ADDR_HPP percona-xtradb-cluster-galera/gcomm/src/asio_protonet.cpp0000644000000000000000000001673012247075736024143 0ustar rootroot00000000000000/* * Copyright (C) 2010 Codership Oy */ #include "asio_tcp.hpp" #include "asio_udp.hpp" #include "asio_addr.hpp" #include "asio_protonet.hpp" #include "socket.hpp" #include "gcomm/util.hpp" #include "gcomm/conf.hpp" #include "gu_logger.hpp" #include #include #include #include #ifdef HAVE_ASIO_SSL_HPP namespace { static std::string get_file(const gu::Config& conf, const std::string& fname) { try { return conf.get(fname); } catch (gu::NotFound& e) { log_error << "could not find '" << fname << "' from configuration"; throw; } } static void set_cipher_list(SSL_CTX* ssl_ctx, gu::Config& conf) { std::string cipher_list( conf.get(gcomm::Conf::SocketSslCipherList, "AES128-SHA")); if (SSL_CTX_set_cipher_list(ssl_ctx, cipher_list.c_str()) == 0) { gu_throw_error(EINVAL) << "could not set cipher list, check that " << "the list is valid: "<< cipher_list; } conf.set(gcomm::Conf::SocketSslCipherList, cipher_list); } static void set_compression(gu::Config& conf) { bool compression( conf.get(gcomm::Conf::SocketSslCompression, true));; if (compression == false) { log_info << "disabling SSL compression"; sk_SSL_COMP_zero(SSL_COMP_get_compression_methods()); } conf.set(gcomm::Conf::SocketSslCompression, compression); } } std::string gcomm::AsioProtonet::get_ssl_password() const { std::string file(get_file(conf_, Conf::SocketSslPasswordFile)); std::ifstream ifs(file.c_str(), std::ios_base::in); if (ifs.good() == false) { gu_throw_error(errno) << "could not open password file '" << file << "'"; } std::string ret; std::getline(ifs, ret); return ret; } #endif // HAVE_ASIO_SSL_HPP gcomm::AsioProtonet::AsioProtonet(gu::Config& conf, int version) : gcomm::Protonet(conf, "asio", version), mutex_(), poll_until_(gu::datetime::Date::max()), io_service_(), timer_(io_service_), #ifdef HAVE_ASIO_SSL_HPP ssl_context_(io_service_, asio::ssl::context::sslv23), #endif // HAVE_ASIO_SSL_HPP mtu_(1 << 15), checksum_(true) { #ifdef HAVE_ASIO_SSL_HPP // use ssl if either private key or cert file is specified bool use_ssl(conf_.has(Conf::SocketSslPrivateKeyFile) == true || conf_.has(Conf::SocketSslCertificateFile) == true); try { // overrides use_ssl is given explicitly use_ssl = conf_.get(Conf::SocketUseSsl); } catch (gu::NotFound& nf) { } if (use_ssl == true) { conf_.set(Conf::SocketUseSsl, true); log_info << "initializing ssl context"; set_compression(conf_); set_cipher_list(ssl_context_.impl(), conf_); ssl_context_.set_verify_mode(asio::ssl::context::verify_peer); ssl_context_.set_password_callback( boost::bind(&gcomm::AsioProtonet::get_ssl_password, this)); // private key file (required) const std::string private_key_file( get_file(conf_, Conf::SocketSslPrivateKeyFile)); try { ssl_context_.use_private_key_file( private_key_file, asio::ssl::context::pem); } catch (gu::NotFound& e) { log_error << "could not load private key file '" << private_key_file << "'"; throw; } catch (std::exception& e) { log_error << "could not use private key file '" << private_key_file << "': " << e.what(); throw; } // certificate file (required) const std::string certificate_file( get_file(conf_, Conf::SocketSslCertificateFile)); try { ssl_context_.use_certificate_file(certificate_file, asio::ssl::context::pem); } catch (std::exception& e) { log_error << "could not load certificate file'" << certificate_file << "': " << e.what(); throw; } // verify file (optional, defaults to certificate_file) const std::string verify_file( conf_.get(Conf::SocketSslVerifyFile, certificate_file)); try { ssl_context_.load_verify_file(verify_file); } catch (std::exception& e) { log_error << "could not load verify file '" << verify_file << "': " << e.what(); throw; } conf_.set(Conf::SocketSslVerifyFile, verify_file); } #endif // HAVE_ASIO_SSL_HPP } gcomm::AsioProtonet::~AsioProtonet() { } void gcomm::AsioProtonet::enter() { mutex_.lock(); } void gcomm::AsioProtonet::leave() { mutex_.unlock(); } gcomm::SocketPtr gcomm::AsioProtonet::socket(const gu::URI& uri) { if (uri.get_scheme() == "tcp" || uri.get_scheme() == "ssl") { return boost::shared_ptr(new AsioTcpSocket(*this, uri)); } else if (uri.get_scheme() == "udp") { return boost::shared_ptr(new AsioUdpSocket(*this, uri)); } else { gu_throw_fatal << "scheme '" << uri.get_scheme() << "' not implemented"; } } gcomm::Acceptor* gcomm::AsioProtonet::acceptor(const gu::URI& uri) { return new AsioTcpAcceptor(*this, uri); } gu::datetime::Period handle_timers_helper(gcomm::Protonet& pnet, const gu::datetime::Period& period) { const gu::datetime::Date now(gu::datetime::Date::now()); const gu::datetime::Date stop(now + period); const gu::datetime::Date next_time(pnet.handle_timers()); const gu::datetime::Period sleep_p(std::min(stop - now, next_time - now)); return (sleep_p < 0 ? 0 : sleep_p); } void gcomm::AsioProtonet::event_loop(const gu::datetime::Period& period) { io_service_.reset(); poll_until_ = gu::datetime::Date::now() + period; const gu::datetime::Period p(handle_timers_helper(*this, period)); timer_.expires_from_now(boost::posix_time::nanosec(p.get_nsecs())); timer_.async_wait(boost::bind(&AsioProtonet::handle_wait, this, asio::placeholders::error)); io_service_.run(); } void gcomm::AsioProtonet::dispatch(const SocketId& id, const Datagram& dg, const ProtoUpMeta& um) { for (std::deque::iterator i = protos_.begin(); i != protos_.end(); ++i) { (*i)->dispatch(id, dg, um); } } void gcomm::AsioProtonet::interrupt() { io_service_.stop(); } void gcomm::AsioProtonet::handle_wait(const asio::error_code& ec) { gu::datetime::Date now(gu::datetime::Date::now()); const gu::datetime::Period p(handle_timers_helper(*this, poll_until_ - now)); using std::rel_ops::operator>=; if (ec == asio::error_code() && poll_until_ >= now) { timer_.expires_from_now(boost::posix_time::nanosec(p.get_nsecs())); timer_.async_wait(boost::bind(&AsioProtonet::handle_wait, this, asio::placeholders::error)); } else { io_service_.stop(); } } percona-xtradb-cluster-galera/gcomm/src/asio_protonet.hpp0000644000000000000000000000317612247075736024150 0ustar rootroot00000000000000/* * Copyright (C) 2010 Codership Oy */ #ifndef GCOMM_ASIO_PROTONET_HPP #define GCOMM_ASIO_PROTONET_HPP #include "gcomm/protonet.hpp" #include "socket.hpp" #include "gu_monitor.hpp" #pragma GCC diagnostic ignored "-Weffc++" #pragma GCC diagnostic ignored "-Wold-style-cast" #include "asio.hpp" #ifdef HAVE_ASIO_SSL_HPP #include "asio/ssl.hpp" #endif // HAVE_ASIO_SSL_HPP #include #include #include namespace gcomm { class AsioProtonet; } class gcomm::AsioProtonet : public gcomm::Protonet { public: AsioProtonet(gu::Config& conf, int version = 0); ~AsioProtonet(); void event_loop(const gu::datetime::Period& p); void dispatch(const SocketId&, const Datagram&, const ProtoUpMeta&); void interrupt(); SocketPtr socket(const gu::URI&); gcomm::Acceptor* acceptor(const gu::URI&); void enter(); void leave(); size_t mtu() const { return mtu_; } #ifdef HAVE_ASIO_SSL_HPP std::string get_ssl_password() const; #endif // HAVE_ASIO_SSL_HPP private: friend class AsioTcpSocket; friend class AsioTcpAcceptor; friend class AsioUdpSocket; AsioProtonet(const AsioProtonet&); void handle_wait(const asio::error_code& ec); gu::RecursiveMutex mutex_; gu::datetime::Date poll_until_; asio::io_service io_service_; asio::deadline_timer timer_; #ifdef HAVE_ASIO_SSL_HPP asio::ssl::context ssl_context_; #endif // HAVE_ASIO_SSL_HPP size_t mtu_; bool checksum_; }; #endif // GCOMM_ASIO_PROTONET_HPP percona-xtradb-cluster-galera/gcomm/src/asio_tcp.cpp0000644000000000000000000006213012247075736023052 0ustar rootroot00000000000000/* * Copyright (C) 2012 Codership Oy */ #include "asio_tcp.hpp" #include "asio_addr.hpp" #include "gcomm/util.hpp" #include "gcomm/common.hpp" #define FAILED_HANDLER(_e) failed_handler(_e, __FUNCTION__, __LINE__) gcomm::AsioTcpSocket::AsioTcpSocket(AsioProtonet& net, const gu::URI& uri) : Socket (uri), net_ (net), socket_ (net.io_service_), #ifdef HAVE_ASIO_SSL_HPP ssl_socket_ (0), #endif /* HAVE_ASIO_SSL_HPP */ send_q_ (), recv_buf_ (net_.mtu() + NetHeader::serial_size_), recv_offset_ (0), state_ (S_CLOSED), local_addr_ (), remote_addr_ () { log_debug << "ctor for " << id(); } gcomm::AsioTcpSocket::~AsioTcpSocket() { log_debug << "dtor for " << id(); close_socket(); #ifdef HAVE_ASIO_SSL_HPP delete ssl_socket_; ssl_socket_ = 0; #endif /* HAVE_ASIO_SSL_HPP */ } void gcomm::AsioTcpSocket::failed_handler(const asio::error_code& ec, const std::string& func, int line) { log_debug << "failed handler from " << func << ":" << line << " socket " << id() << " " << socket_.native() << " error " << ec << " " << socket_.is_open() << " state " << state(); try { log_debug << "local endpoint " << local_addr() << " remote endpoint " << remote_addr(); } catch (...) { } const State prev_state(state()); if (state() != S_CLOSED) { state_ = S_FAILED; } if (prev_state != S_FAILED && prev_state != S_CLOSED) { net_.dispatch(id(), Datagram(), ProtoUpMeta(ec.value())); } } #ifdef HAVE_ASIO_SSL_HPP namespace { static const char* get_cipher(SSL* ssl) { return SSL_get_cipher_name(ssl); } static const char* get_compression(SSL* ssl) { return SSL_COMP_get_name(SSL_get_current_compression(ssl)); } static std::string extra_error_info(const asio::error_code& ec) { std::ostringstream os; if (ec.category() == asio::error::get_ssl_category()) { char errstr[120] = {0, }; ERR_error_string_n(ec.value(), errstr, sizeof(errstr)); os << ec.value() << ": '" << errstr << "'"; } return os.str(); } } void gcomm::AsioTcpSocket::handshake_handler(const asio::error_code& ec) { if (ec) { log_error << "handshake with remote endpoint " << remote_addr() << " failed: " << ec << ": '" << ec.message() << "' ( " << extra_error_info(ec) << ")"; FAILED_HANDLER(ec); return; } if (ssl_socket_ == 0) { log_error << "handshake handler called for non-SSL socket " << id() << " " << remote_addr() << " <-> " << local_addr(); FAILED_HANDLER(asio::error_code(EPROTO, asio::error::system_category)); return; } log_info << "SSL handshake successful, " << "remote endpoint " << remote_addr() << " local endpoint " << local_addr() << " cipher: " << get_cipher(ssl_socket_->impl()->ssl) << " compression: " << get_compression(ssl_socket_->impl()->ssl); state_ = S_CONNECTED; net_.dispatch(id(), Datagram(), ProtoUpMeta(ec.value())); async_receive(); } #endif /* HAVE_ASIO_SSL_HPP */ void gcomm::AsioTcpSocket::connect_handler(const asio::error_code& ec) { Critical crit(net_); try { if (ec) { FAILED_HANDLER(ec); return; } else { assign_local_addr(); assign_remote_addr(); #ifdef HAVE_ASIO_SSL_HPP if (ssl_socket_ != 0) { ssl_socket_->lowest_layer().set_option( asio::ip::tcp::no_delay(true)); set_fd_options(ssl_socket_->lowest_layer()); log_debug << "socket " << id() << " connected, remote endpoint " << remote_addr() << " local endpoint " << local_addr(); ssl_socket_->async_handshake( asio::ssl::stream::client, boost::bind(&AsioTcpSocket::handshake_handler, shared_from_this(), asio::placeholders::error) ); } else { #endif /* HAVE_ASIO_SSL_HPP */ socket_.set_option(asio::ip::tcp::no_delay(true)); set_fd_options(socket_); log_debug << "socket " << id() << " connected, remote endpoint " << remote_addr() << " local endpoint " << local_addr(); state_ = S_CONNECTED; net_.dispatch(id(), Datagram(), ProtoUpMeta(ec.value())); async_receive(); #ifdef HAVE_ASIO_SSL_HPP } #endif /* HAVE_ASIO_SSL_HPP */ } } catch (asio::system_error& e) { FAILED_HANDLER(e.code()); } } void gcomm::AsioTcpSocket::connect(const gu::URI& uri) { try { Critical crit(net_); asio::ip::tcp::resolver resolver(net_.io_service_); // Give query flags explicitly to avoid having AI_ADDRCONFIG in // underlying getaddrinfo() hint flags. asio::ip::tcp::resolver::query query(unescape_addr(uri.get_host()), uri.get_port(), asio::ip::tcp::resolver::query::flags(0)); asio::ip::tcp::resolver::iterator i(resolver.resolve(query)); #ifdef HAVE_ASIO_SSL_HPP if (uri.get_scheme() == SSL_SCHEME) { ssl_socket_ = new asio::ssl::stream( net_.io_service_, net_.ssl_context_ ); ssl_socket_->lowest_layer().async_connect( *i, boost::bind(&AsioTcpSocket::connect_handler, shared_from_this(), asio::placeholders::error) ); } else { #endif /* HAVE_ASIO_SSL_HPP */ socket_.async_connect(*i, boost::bind(&AsioTcpSocket::connect_handler, shared_from_this(), asio::placeholders::error)); #ifdef HAVE_ASIO_SSL_HPP } #endif /* HAVE_ASIO_SSL_HPP */ state_ = S_CONNECTING; } catch (asio::system_error& e) { gu_throw_error(e.code().value()) << "error while connecting to remote host " << uri.to_string() << "', asio error '" << e.what() << "'"; } } void gcomm::AsioTcpSocket::close() { Critical crit(net_); if (state() == S_CLOSED || state() == S_CLOSING) return; log_debug << "closing " << id() << " state " << state() << " send_q size " << send_q_.size(); if (send_q_.empty() == true || state() != S_CONNECTED) { close_socket(); state_ = S_CLOSED; } else { state_ = S_CLOSING; } } void gcomm::AsioTcpSocket::write_handler(const asio::error_code& ec, size_t bytes_transferred) { Critical crit(net_); if (state() != S_CONNECTED && state() != S_CLOSING) { log_debug << "write handler for " << id() << " state " << state(); #ifdef HAVE_ASIO_SSL_HPP if (ec.category() == asio::error::get_ssl_category()) { log_warn << "write_handler(): " << ec.message() << " (" << extra_error_info(ec) << ")"; } #endif return; } if (!ec) { gcomm_assert(send_q_.empty() == false); gcomm_assert(send_q_.front().len() >= bytes_transferred); while (send_q_.empty() == false && bytes_transferred >= send_q_.front().len()) { const Datagram& dg(send_q_.front()); bytes_transferred -= dg.len(); send_q_.pop_front(); } gcomm_assert(bytes_transferred == 0); if (send_q_.empty() == false) { const Datagram& dg(send_q_.front()); boost::array cbs; cbs[0] = asio::const_buffer(dg.header() + dg.header_offset(), dg.header_len()); cbs[1] = asio::const_buffer(&dg.payload()[0], dg.payload().size()); write_one(cbs); } else if (state_ == S_CLOSING) { log_debug << "deferred close of " << id(); close_socket(); state_ = S_CLOSED; } } else if (state_ == S_CLOSING) { log_debug << "deferred close of " << id() << " error " << ec; close_socket(); state_ = S_CLOSED; } else { FAILED_HANDLER(ec); } } int gcomm::AsioTcpSocket::send(const Datagram& dg) { Critical crit(net_); if (state() != S_CONNECTED) { return ENOTCONN; } NetHeader hdr(static_cast(dg.len()), net_.version_); if (net_.checksum_ == true) { hdr.set_crc32(crc32(dg)); } send_q_.push_back(dg); // makes copy of dg Datagram& priv_dg(send_q_.back()); priv_dg.set_header_offset(priv_dg.header_offset() - NetHeader::serial_size_); serialize(hdr, priv_dg.header(), priv_dg.header_size(), priv_dg.header_offset()); if (send_q_.size() == 1) { boost::array cbs; cbs[0] = asio::const_buffer(priv_dg.header() + priv_dg.header_offset(), priv_dg.header_len()); cbs[1] = asio::const_buffer(&priv_dg.payload()[0], priv_dg.payload().size()); write_one(cbs); } return 0; } void gcomm::AsioTcpSocket::read_handler(const asio::error_code& ec, const size_t bytes_transferred) { Critical crit(net_); if (ec) { #ifdef HAVE_ASIO_SSL_HPP if (ec.category() == asio::error::get_ssl_category()) { log_warn << "read_handler(): " << ec.message() << " (" << extra_error_info(ec) << ")"; } #endif FAILED_HANDLER(ec); return; } if (state() == S_CLOSING) { // keep on reading data in case of deferred shutdown too boost::array mbs; mbs[0] = asio::mutable_buffer(&recv_buf_[0], recv_buf_.size()); read_one(mbs); return; } else if (state() != S_CONNECTED) { log_debug << "read handler for " << id() << " state " << state(); return; } recv_offset_ += bytes_transferred; while (recv_offset_ >= NetHeader::serial_size_) { NetHeader hdr; try { unserialize(&recv_buf_[0], recv_buf_.size(), 0, hdr); } catch (gu::Exception& e) { FAILED_HANDLER(asio::error_code(e.get_errno(), asio::error::system_category)); return; } if (recv_offset_ >= hdr.len() + NetHeader::serial_size_) { Datagram dg( gu::SharedBuffer( new gu::Buffer(&recv_buf_[0] + NetHeader::serial_size_, &recv_buf_[0] + NetHeader::serial_size_ + hdr.len()))); if (net_.checksum_ == true) { #ifdef TEST_NET_CHECKSUM_ERROR long rnd(rand()); if (rnd % 10000 == 0) { hdr.set_crc32(static_cast(rnd)); } #endif /* TEST_NET_CHECKSUM_ERROR */ if ((hdr.has_crc32() == true && crc32(dg) != hdr.crc32()) || (hdr.has_crc32() == false && hdr.crc32() != 0)) { log_warn << "checksum failed, hdr: len=" << hdr.len() << " has_crc32=" << hdr.has_crc32() << " crc32=" << hdr.crc32(); FAILED_HANDLER(asio::error_code(EPROTO, asio::error::system_category)); return; } } ProtoUpMeta um; net_.dispatch(id(), dg, um); recv_offset_ -= NetHeader::serial_size_ + hdr.len(); if (recv_offset_ > 0) { memmove(&recv_buf_[0], &recv_buf_[0] + NetHeader::serial_size_ + hdr.len(), recv_offset_); } } else { break; } } boost::array mbs; mbs[0] = asio::mutable_buffer(&recv_buf_[0] + recv_offset_, recv_buf_.size() - recv_offset_); read_one(mbs); } size_t gcomm::AsioTcpSocket::read_completion_condition( const asio::error_code& ec, const size_t bytes_transferred) { Critical crit(net_); if (ec) { #ifdef HAVE_ASIO_SSL_HPP if (ec.category() == asio::error::get_ssl_category()) { log_warn << "read_completion_condition(): " << ec.message() << " (" << extra_error_info(ec) << ")"; } #endif FAILED_HANDLER(ec); return 0; } if (state() == S_CLOSING) { log_debug << "read completion condition for " << id() << " state " << state(); return 0; } else if (state_ != S_CONNECTED) { log_debug << "read completion condition for " << id() << " state " << state(); return 0; } if (recv_offset_ + bytes_transferred >= NetHeader::serial_size_) { NetHeader hdr; try { unserialize(&recv_buf_[0], NetHeader::serial_size_, 0, hdr); } catch (gu::Exception& e) { log_warn << "unserialize error " << e.what(); FAILED_HANDLER(asio::error_code(e.get_errno(), asio::error::system_category)); return 0; } if (recv_offset_ + bytes_transferred >= NetHeader::serial_size_ + hdr.len()) { return 0; } } return (recv_buf_.size() - recv_offset_); } void gcomm::AsioTcpSocket::async_receive() { Critical crit(net_); gcomm_assert(state() == S_CONNECTED); boost::array mbs; mbs[0] = asio::mutable_buffer(&recv_buf_[0], recv_buf_.size()); read_one(mbs); } size_t gcomm::AsioTcpSocket::mtu() const { return net_.mtu(); } std::string gcomm::AsioTcpSocket::local_addr() const { return local_addr_; } std::string gcomm::AsioTcpSocket::remote_addr() const { return remote_addr_; } void gcomm::AsioTcpSocket::read_one(boost::array& mbs) { #ifdef HAVE_ASIO_SSL_HPP if (ssl_socket_ != 0) { async_read(*ssl_socket_, mbs, boost::bind(&AsioTcpSocket::read_completion_condition, shared_from_this(), asio::placeholders::error, asio::placeholders::bytes_transferred), boost::bind(&AsioTcpSocket::read_handler, shared_from_this(), asio::placeholders::error, asio::placeholders::bytes_transferred)); } else { #endif /* HAVE_ASIO_SSL_HPP */ async_read(socket_, mbs, boost::bind(&AsioTcpSocket::read_completion_condition, shared_from_this(), asio::placeholders::error, asio::placeholders::bytes_transferred), boost::bind(&AsioTcpSocket::read_handler, shared_from_this(), asio::placeholders::error, asio::placeholders::bytes_transferred)); #ifdef HAVE_ASIO_SSL_HPP } #endif /* HAVE_ASIO_SSL_HPP */ } void gcomm::AsioTcpSocket::write_one( const boost::array& cbs) { #ifdef HAVE_ASIO_SSL_HPP if (ssl_socket_ != 0) { async_write(*ssl_socket_, cbs, boost::bind(&AsioTcpSocket::write_handler, shared_from_this(), asio::placeholders::error, asio::placeholders::bytes_transferred)); } else { #endif /* HAVE_ASIO_SSL_HPP */ async_write(socket_, cbs, boost::bind(&AsioTcpSocket::write_handler, shared_from_this(), asio::placeholders::error, asio::placeholders::bytes_transferred)); #ifdef HAVE_ASIO_SSL_HPP } #endif /* HAVE_ASIO_SSL_HPP */ } void gcomm::AsioTcpSocket::close_socket() { try { #ifdef HAVE_ASIO_SSL_HPP if (ssl_socket_ != 0) { // close underlying transport before calling shutdown() // to avoid blocking ssl_socket_->lowest_layer().close(); ssl_socket_->shutdown(); } else { #endif /* HAVE_ASIO_SSL_HPP */ socket_.close(); #ifdef HAVE_ASIO_SSL_HPP } #endif /* HAVE_ASIO_SSL_HPP */ } catch (...) { } } void gcomm::AsioTcpSocket::assign_local_addr() { #ifdef HAVE_ASIO_SSL_HPP if (ssl_socket_ != 0) { local_addr_ = gcomm::uri_string( gcomm::SSL_SCHEME, gcomm::escape_addr( ssl_socket_->lowest_layer().local_endpoint().address()), gu::to_string( ssl_socket_->lowest_layer().local_endpoint().port()) ); } else { #endif /* HAVE_ASIO_SSL_HPP */ local_addr_ = gcomm::uri_string( gcomm::TCP_SCHEME, gcomm::escape_addr(socket_.local_endpoint().address()), gu::to_string(socket_.local_endpoint().port()) ); #ifdef HAVE_ASIO_SSL_HPP } #endif /* HAVE_ASIO_SSL_HPP */ } void gcomm::AsioTcpSocket::assign_remote_addr() { #ifdef HAVE_ASIO_SSL_HPP if (ssl_socket_ != 0) { remote_addr_ = gcomm::uri_string( gcomm::SSL_SCHEME, gcomm::escape_addr( ssl_socket_->lowest_layer().remote_endpoint().address()), gu::to_string( ssl_socket_->lowest_layer().remote_endpoint().port()) ); } else { #endif /* HAVE_ASIO_SSL_HPP */ remote_addr_ = uri_string( gcomm::TCP_SCHEME, gcomm::escape_addr(socket_.remote_endpoint().address()), gu::to_string(socket_.remote_endpoint().port()) ); #ifdef HAVE_ASIO_SSL_HPP } #endif /* HAVE_ASIO_SSL_HPP */ } gcomm::AsioTcpAcceptor::AsioTcpAcceptor(AsioProtonet& net, const gu::URI& uri) : Acceptor (uri), net_ (net), acceptor_ (net_.io_service_), accepted_socket_() { } gcomm::AsioTcpAcceptor::~AsioTcpAcceptor() { close(); } void gcomm::AsioTcpAcceptor::accept_handler( SocketPtr socket, const asio::error_code& error) { if (!error) { AsioTcpSocket* s(static_cast(socket.get())); try { s->assign_local_addr(); s->assign_remote_addr(); #ifdef HAVE_ASIO_SSL_HPP if (s->ssl_socket_ != 0) { s->ssl_socket_->lowest_layer().set_option( asio::ip::tcp::no_delay(true)); set_fd_options(s->ssl_socket_->lowest_layer()); log_debug << "socket " << s->id() << " connected, remote endpoint " << s->remote_addr() << " local endpoint " << s->local_addr(); s->ssl_socket_->async_handshake( asio::ssl::stream::server, boost::bind(&AsioTcpSocket::handshake_handler, s->shared_from_this(), asio::placeholders::error)); s->state_ = Socket::S_CONNECTING; } else { #endif /* HAVE_ASIO_SSL_HP */ s->socket_.set_option(asio::ip::tcp::no_delay(true)); set_fd_options(s->socket_); s->state_ = Socket::S_CONNECTED; #ifdef HAVE_ASIO_SSL_HPP } #endif /* HAVE_ASIO_SSL_HPP */ accepted_socket_ = socket; log_debug << "accepted socket " << socket->id(); net_.dispatch(id(), Datagram(), ProtoUpMeta(error.value())); } catch (asio::system_error& e) { // socket object should be freed automatically when it // goes out of scope log_debug << "accept failed: " << e.what(); } AsioTcpSocket* new_socket(new AsioTcpSocket(net_, uri_)); #ifdef HAVE_ASIO_SSL_HPP if (uri_.get_scheme() == SSL_SCHEME) { new_socket->ssl_socket_ = new asio::ssl::stream( net_.io_service_, net_.ssl_context_); acceptor_.async_accept(new_socket->ssl_socket_->lowest_layer(), boost::bind(&AsioTcpAcceptor::accept_handler, this, SocketPtr(new_socket), asio::placeholders::error)); } else { #endif /* HAVE_ASIO_SSL_HPP */ acceptor_.async_accept(new_socket->socket_, boost::bind(&AsioTcpAcceptor::accept_handler, this, SocketPtr(new_socket), asio::placeholders::error)); #ifdef HAVE_ASIO_SSL_HPP } #endif /* HAVE_ASIO_SSL_HPP */ } else { log_warn << "accept handler: " << error; } } void gcomm::AsioTcpAcceptor::listen(const gu::URI& uri) { try { asio::ip::tcp::resolver resolver(net_.io_service_); // Give query flags explicitly to avoid having AI_ADDRCONFIG in // underlying getaddrinfo() hint flags. asio::ip::tcp::resolver::query query(unescape_addr(uri.get_host()), uri.get_port(), asio::ip::tcp::resolver::query::flags(0)); asio::ip::tcp::resolver::iterator i(resolver.resolve(query)); acceptor_.open(i->endpoint().protocol()); acceptor_.set_option(asio::ip::tcp::socket::reuse_address(true)); set_fd_options(acceptor_); acceptor_.bind(*i); acceptor_.listen(); AsioTcpSocket* new_socket(new AsioTcpSocket(net_, uri)); #ifdef HAVE_ASIO_SSL_HPP if (uri_.get_scheme() == SSL_SCHEME) { new_socket->ssl_socket_ = new asio::ssl::stream( net_.io_service_, net_.ssl_context_); acceptor_.async_accept(new_socket->ssl_socket_->lowest_layer(), boost::bind(&AsioTcpAcceptor::accept_handler, this, SocketPtr(new_socket), asio::placeholders::error)); } else { #endif /* HAVE_ASIO_SSL_HPP */ acceptor_.async_accept(new_socket->socket_, boost::bind(&AsioTcpAcceptor::accept_handler, this, SocketPtr(new_socket), asio::placeholders::error)); #ifdef HAVE_ASIO_SSL_HPP } #endif /* HAVE_ASIO_SSL_HPP */ } catch (asio::system_error& e) { log_error << e.what(); gu_throw_error(e.code().value()) << "error while trying to listen '" << uri.to_string() << "', asio error '" << e.what() << "'"; } } std::string gcomm::AsioTcpAcceptor::listen_addr() const { try { return uri_string( uri_.get_scheme(), escape_addr(acceptor_.local_endpoint().address()), gu::to_string(acceptor_.local_endpoint().port()) ); } catch (asio::system_error& e) { gu_throw_error(e.code().value()) << "failed to read listen addr " << "', asio error '" << e.what() << "'"; } } void gcomm::AsioTcpAcceptor::close() { try { acceptor_.close(); } catch (...) { } } gcomm::SocketPtr gcomm::AsioTcpAcceptor::accept() { if (accepted_socket_->state() == Socket::S_CONNECTED) { accepted_socket_->async_receive(); } return accepted_socket_; } percona-xtradb-cluster-galera/gcomm/src/asio_tcp.hpp0000644000000000000000000000626212247075736023063 0ustar rootroot00000000000000/* * Copyright (C) 2010 Codership Oy */ #ifndef GCOMM_ASIO_TCP_HPP #define GCOMM_ASIO_TCP_HPP #include "socket.hpp" #include "asio_protonet.hpp" #include #include #include #include #include namespace gcomm { class AsioTcpSocket; class AsioTcpAcceptor; } // TCP Socket implementation class gcomm::AsioTcpSocket : public gcomm::Socket, public boost::enable_shared_from_this { public: AsioTcpSocket(AsioProtonet& net, const gu::URI& uri); ~AsioTcpSocket(); void failed_handler(const asio::error_code& ec, const std::string& func, int line); #ifdef HAVE_ASIO_SSL_HPP void handshake_handler(const asio::error_code& ec); #endif // HAVE_ASIO_SSL_HPP void connect_handler(const asio::error_code& ec); void connect(const gu::URI& uri); void close(); void write_handler(const asio::error_code& ec, size_t bytes_transferred); int send(const Datagram& dg); size_t read_completion_condition( const asio::error_code& ec, const size_t bytes_transferred); void read_handler(const asio::error_code& ec, const size_t bytes_transferred); void async_receive(); size_t mtu() const; std::string local_addr() const; std::string remote_addr() const; State state() const { return state_; } SocketId id() const { return &socket_; } private: friend class gcomm::AsioTcpAcceptor; AsioTcpSocket(const AsioTcpSocket&); void operator=(const AsioTcpSocket&); void read_one(boost::array& mbs); void write_one(const boost::array& cbs); void close_socket(); // call to assign local/remote addresses at the point where it // is known that underlying socket is live void assign_local_addr(); void assign_remote_addr(); AsioProtonet& net_; asio::ip::tcp::socket socket_; #ifdef HAVE_ASIO_SSL_HPP asio::ssl::stream* ssl_socket_; #endif // HAVE_ASIO_SSL_HPP std::deque send_q_; std::vector recv_buf_; size_t recv_offset_; State state_; // Querying addresses from failed socket does not work, // so need to maintain copy for diagnostics logging std::string local_addr_; std::string remote_addr_; }; class gcomm::AsioTcpAcceptor : public gcomm::Acceptor { public: AsioTcpAcceptor(AsioProtonet& net, const gu::URI& uri); ~AsioTcpAcceptor(); void accept_handler( SocketPtr socket, const asio::error_code& error); void listen(const gu::URI& uri); std::string listen_addr() const; void close(); SocketPtr accept(); State state() const { gu_throw_fatal << "TODO:"; } SocketId id() const { return &acceptor_; } private: AsioProtonet& net_; asio::ip::tcp::acceptor acceptor_; SocketPtr accepted_socket_; }; #endif // GCOMM_ASIO_TCP_HPP percona-xtradb-cluster-galera/gcomm/src/asio_udp.cpp0000644000000000000000000001612112247075736023053 0ustar rootroot00000000000000/* * Copyright (C) 2010-2012 Codership Oy */ #include "asio_udp.hpp" #include "asio_addr.hpp" #include "gcomm/util.hpp" #include "gcomm/common.hpp" #include #include static bool is_multicast(const asio::ip::udp::endpoint& ep) { if (ep.address().is_v4() == true) { return ep.address().to_v4().is_multicast(); } else if (ep.address().is_v6() == true) { return ep.address().to_v6().is_multicast(); } gu_throw_fatal; } static void join_group(asio::ip::udp::socket& socket, const asio::ip::udp::endpoint& ep, const asio::ip::address& local_if) { gcomm_assert(is_multicast(ep) == true); if (ep.address().is_v4() == true) { socket.set_option(asio::ip::multicast::join_group(ep.address().to_v4(), local_if.to_v4())); socket.set_option(asio::ip::multicast::outbound_interface(local_if.to_v4())); } else { gu_throw_fatal << "mcast interface not implemented"; socket.set_option(asio::ip::multicast::join_group(ep.address().to_v6())); } } static void leave_group(asio::ip::udp::socket& socket, asio::ip::udp::endpoint& ep) { // gcomm_assert(is_multicast(ep) == true); // socket.set_option(asio::ip::multicast::leave_group(ep.address().to_v4())); } gcomm::AsioUdpSocket::AsioUdpSocket(AsioProtonet& net, const gu::URI& uri) : Socket(uri), net_(net), state_(S_CLOSED), socket_(net_.io_service_), target_ep_(), source_ep_(), recv_buf_((1 << 15) + NetHeader::serial_size_) { } gcomm::AsioUdpSocket::~AsioUdpSocket() { close(); } void gcomm::AsioUdpSocket::connect(const gu::URI& uri) { gcomm_assert(state() == S_CLOSED); Critical crit(net_); asio::ip::udp::resolver resolver(net_.io_service_); asio::ip::udp::resolver::query query(unescape_addr(uri.get_host()), uri.get_port()); asio::ip::udp::resolver::iterator conn_i(resolver.resolve(query)); target_ep_ = conn_i->endpoint(); socket_.open(conn_i->endpoint().protocol()); socket_.set_option(asio::ip::udp::socket::reuse_address(true)); socket_.set_option(asio::ip::udp::socket::linger(true, 1)); set_fd_options(socket_); asio::ip::udp::socket::non_blocking_io cmd(true); socket_.io_control(cmd); const std::string if_addr( unescape_addr( uri.get_option("socket.if_addr", anyaddr(conn_i->endpoint().address())))); asio::ip::address local_if(asio::ip::address::from_string(if_addr)); if (is_multicast(conn_i->endpoint()) == true) { join_group(socket_, conn_i->endpoint(), local_if); socket_.set_option( asio::ip::multicast::enable_loopback( gu::from_string(uri.get_option("socket.if_loop", "false")))); socket_.set_option( asio::ip::multicast::hops( gu::from_string(uri.get_option("socket.mcast_ttl", "1")))); socket_.bind(*conn_i); } else { socket_.bind( asio::ip::udp::endpoint( local_if, gu::from_string(uri.get_port()))); } async_receive(); state_ = S_CONNECTED; } void gcomm::AsioUdpSocket::close() { Critical crit(net_); if (state() != S_CLOSED) { if (is_multicast(target_ep_) == true) { leave_group(socket_, target_ep_); } socket_.close(); } state_ = S_CLOSED; } int gcomm::AsioUdpSocket::send(const Datagram& dg) { Critical crit(net_); boost::array cbs; NetHeader hdr(dg.len(), net_.version_); if (net_.checksum_ == true) { hdr.set_crc32(crc32(dg)); } gu::byte_t buf[NetHeader::serial_size_]; serialize(hdr, buf, sizeof(buf), 0); cbs[0] = asio::const_buffer(buf, sizeof(buf)); cbs[1] = asio::const_buffer(dg.header() + dg.header_offset(), dg.header_len()); cbs[2] = asio::const_buffer(&dg.payload()[0], dg.payload().size()); try { socket_.send_to(cbs, target_ep_); } catch (asio::system_error& err) { log_warn << "Error: " << err.what(); return err.code().value(); } return 0; } void gcomm::AsioUdpSocket::read_handler(const asio::error_code& ec, size_t bytes_transferred) { if (ec) { // return; } if (bytes_transferred >= NetHeader::serial_size_) { Critical crit(net_); NetHeader hdr; try { unserialize(&recv_buf_[0], NetHeader::serial_size_, 0, hdr); } catch (gu::Exception& e) { log_warn << "hdr unserialize failed: " << e.get_errno(); return; } if (NetHeader::serial_size_ + hdr.len() != bytes_transferred) { log_warn << "len " << hdr.len() << " does not match to bytes transferred" << bytes_transferred; } else { Datagram dg( gu::SharedBuffer( new gu::Buffer(&recv_buf_[0] + NetHeader::serial_size_, &recv_buf_[0] + NetHeader::serial_size_ + hdr.len()))); if (net_.checksum_ == true && ((hdr.has_crc32() == true && crc32(dg) != hdr.crc32()) || (hdr.has_crc32() == false && hdr.crc32() != 0))) { log_warn << "checksum failed, hdr: len=" << hdr.len() << " has_crc32=" << hdr.has_crc32() << " crc32=" << hdr.crc32(); } else { net_.dispatch(id(), dg, ProtoUpMeta()); } } } else { log_warn << "short read of " << bytes_transferred; } async_receive(); } void gcomm::AsioUdpSocket::async_receive() { Critical crit(net_); boost::array mbs; mbs[0] = asio::mutable_buffer(&recv_buf_[0], recv_buf_.size()); socket_.async_receive_from(mbs, source_ep_, boost::bind(&AsioUdpSocket::read_handler, shared_from_this(), asio::placeholders::error, asio::placeholders::bytes_transferred)); } size_t gcomm::AsioUdpSocket::mtu() const { return (1 << 15); } std::string gcomm::AsioUdpSocket::local_addr() const { return uri_string(UDP_SCHEME, escape_addr(socket_.local_endpoint().address()), gu::to_string(socket_.local_endpoint().port())); } std::string gcomm::AsioUdpSocket::remote_addr() const { return uri_string(UDP_SCHEME, escape_addr(socket_.remote_endpoint().address()), gu::to_string(socket_.remote_endpoint().port())); } percona-xtradb-cluster-galera/gcomm/src/asio_udp.hpp0000644000000000000000000000216212247075736023060 0ustar rootroot00000000000000/* * Copyright (C) 2010 Codership Oy */ #ifndef GCOMM_ASIO_UDP_HPP #define GCOMM_ASIO_UDP_HPP #include "socket.hpp" #include "asio_protonet.hpp" #include #include namespace gcomm { class AsioUdpSocket; class AsioProtonet; } class gcomm::AsioUdpSocket : public gcomm::Socket, public boost::enable_shared_from_this { public: AsioUdpSocket(AsioProtonet& net, const gu::URI& uri); ~AsioUdpSocket(); void connect(const gu::URI& uri); void close(); int send(const Datagram& dg); void read_handler(const asio::error_code&, size_t); void async_receive(); size_t mtu() const; std::string local_addr() const; std::string remote_addr() const; State state() const { return state_; } SocketId id() const { return &socket_; } private: AsioProtonet& net_; State state_; asio::ip::udp::socket socket_; asio::ip::udp::endpoint target_ep_; asio::ip::udp::endpoint source_ep_; std::vector recv_buf_; }; #endif // GCOMM_ASIO_UDP_HPP percona-xtradb-cluster-galera/gcomm/src/conf.cpp0000644000000000000000000001072112247075736022175 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #include "gcomm/conf.hpp" static std::string const Delim = "."; // Protonet std::string const gcomm::Conf::ProtonetBackend("protonet.backend"); std::string const gcomm::Conf::ProtonetVersion("protonet.version"); // TCP std::string const gcomm::Conf::TcpNonBlocking = "socket" + Delim + "non_blocking"; std::string const gcomm::Conf::SocketUseSsl = "socket" + Delim + "ssl"; std::string const gcomm::Conf::SocketSslVerifyFile = "socket" + Delim + "ssl_ca"; std::string const gcomm::Conf::SocketSslCertificateFile = "socket" + Delim + "ssl_cert"; std::string const gcomm::Conf::SocketSslPrivateKeyFile = "socket" + Delim + "ssl_key"; std::string const gcomm::Conf::SocketSslPasswordFile = "socket" + Delim + "ssl_password_file"; std::string const gcomm::Conf::SocketSslCipherList = "socket" + Delim + "ssl_cipher"; std::string const gcomm::Conf::SocketSslCompression = "socket" + Delim + "ssl_compression"; // GMCast std::string const gcomm::Conf::GMCastScheme = "gmcast"; std::string const gcomm::Conf::GMCastVersion = GMCastScheme + Delim + "version"; std::string const gcomm::Conf::GMCastGroup = GMCastScheme + Delim + "group"; std::string const gcomm::Conf::GMCastListenAddr = GMCastScheme + Delim + "listen_addr"; std::string const gcomm::Conf::GMCastMCastAddr = GMCastScheme + Delim + "mcast_addr"; std::string const gcomm::Conf::GMCastMCastPort = GMCastScheme + Delim + "mcast_port"; std::string const gcomm::Conf::GMCastMCastTTL = GMCastScheme + Delim + "mcast_ttl"; std::string const gcomm::Conf::GMCastTimeWait = GMCastScheme + Delim + "time_wait"; std::string const gcomm::Conf::GMCastPeerTimeout = GMCastScheme + Delim + "peer_timeout"; std::string const gcomm::Conf::GMCastMaxInitialReconnectAttempts = GMCastScheme + Delim + "mira"; std::string const gcomm::Conf::GMCastPeerAddr = GMCastScheme + Delim + "peer_addr"; std::string const gcomm::Conf::GMCastIsolate = GMCastScheme + Delim + "isolate"; // EVS std::string const gcomm::Conf::EvsScheme = "evs"; std::string const gcomm::Conf::EvsVersion = EvsScheme + Delim + "version"; std::string const gcomm::Conf::EvsViewForgetTimeout = EvsScheme + Delim + "view_forget_timeout"; std::string const gcomm::Conf::EvsInactiveTimeout = EvsScheme + Delim + "inactive_timeout"; std::string const gcomm::Conf::EvsSuspectTimeout = EvsScheme + Delim + "suspect_timeout"; std::string const gcomm::Conf::EvsInactiveCheckPeriod = EvsScheme + Delim + "inactive_check_period"; std::string const gcomm::Conf::EvsInstallTimeout = EvsScheme + Delim + "install_timeout"; std::string const gcomm::Conf::EvsKeepalivePeriod = EvsScheme + Delim + "keepalive_period"; std::string const gcomm::Conf::EvsJoinRetransPeriod = EvsScheme + Delim + "join_retrans_period"; std::string const gcomm::Conf::EvsStatsReportPeriod = EvsScheme + Delim + "stats_report_period"; std::string const gcomm::Conf::EvsDebugLogMask = EvsScheme + Delim + "debug_log_mask"; std::string const gcomm::Conf::EvsInfoLogMask = EvsScheme + Delim + "info_log_mask"; std::string const gcomm::Conf::EvsSendWindow = EvsScheme + Delim + "send_window"; std::string const gcomm::Conf::EvsUserSendWindow = EvsScheme + Delim + "user_send_window"; std::string const gcomm::Conf::EvsUseAggregate = EvsScheme + Delim + "use_aggregate"; std::string const gcomm::Conf::EvsCausalKeepalivePeriod = EvsScheme + Delim + "causal_keepalive_period"; std::string const gcomm::Conf::EvsMaxInstallTimeouts = EvsScheme + Delim + "max_install_timeouts"; // PC std::string const gcomm::Conf::PcScheme = "pc"; std::string const gcomm::Conf::PcVersion = PcScheme + Delim + "version"; std::string const gcomm::Conf::PcIgnoreSb = PcScheme + Delim + "ignore_sb"; std::string const gcomm::Conf::PcIgnoreQuorum = PcScheme + Delim + "ignore_quorum"; std::string const gcomm::Conf::PcChecksum = PcScheme + Delim + "checksum"; std::string const gcomm::Conf::PcLinger = PcScheme + Delim + "linger"; std::string const gcomm::Conf::PcAnnounceTimeout = PcScheme + Delim + "announce_timeout"; std::string const gcomm::Conf::PcNpvo = PcScheme + Delim + "npvo"; std::string const gcomm::Conf::PcBootstrap = PcScheme + Delim + "bootstrap"; std::string const gcomm::Conf::PcWaitPrim = PcScheme + Delim + "wait_prim"; std::string const gcomm::Conf::PcWaitPrimTimeout = PcScheme + Delim + "wait_prim_timeout"; std::string const gcomm::Conf::PcWeight = PcScheme + Delim + "weight"; percona-xtradb-cluster-galera/gcomm/src/defaults.cpp0000644000000000000000000000361212247075736023060 0ustar rootroot00000000000000/* * Copyright (C) 2012 Codership Oy */ #include "defaults.hpp" #include "gcomm/common.hpp" namespace gcomm { std::string const Defaults::GMCastTcpPort = BASE_PORT_DEFAULT; std::string const Defaults::EvsViewForgetTimeout = "PT5M"; std::string const Defaults::EvsViewForgetTimeoutMin = "PT1S"; std::string const Defaults::EvsInactiveCheckPeriod = "PT0.5S"; std::string const Defaults::EvsSuspectTimeout = "PT5S"; std::string const Defaults::EvsSuspectTimeoutMin = "PT0.1S"; std::string const Defaults::EvsInactiveTimeout = "PT15S"; std::string const Defaults::EvsInactiveTimeoutMin = "PT0.1S"; std::string const Defaults::EvsRetransPeriod = "PT1S"; std::string const Defaults::EvsRetransPeriodMin = "PT0.1S"; std::string const Defaults::EvsJoinRetransPeriod = "PT1S"; std::string const Defaults::EvsStatsReportPeriod = "PT1M"; std::string const Defaults::EvsStatsReportPeriodMin = "PT1S"; std::string const Defaults::EvsSendWindow = "4"; std::string const Defaults::EvsSendWindowMin = "1"; std::string const Defaults::EvsUserSendWindow = "2"; std::string const Defaults::EvsUserSendWindowMin = "1"; std::string const Defaults::EvsMaxInstallTimeouts = "1"; std::string const Defaults::PcAnnounceTimeout = "PT3S"; std::string const Defaults::PcChecksum = "false"; std::string const Defaults::PcIgnoreQuorum = "false"; std::string const Defaults::PcIgnoreSb = PcIgnoreQuorum; std::string const Defaults::PcNpvo = "false"; std::string const Defaults::PcVersion = "0"; std::string const Defaults::PcWaitPrim = "true"; std::string const Defaults::PcWaitPrimTimeout = "P30S"; std::string const Defaults::PcWeight = "1"; } percona-xtradb-cluster-galera/gcomm/src/defaults.hpp0000644000000000000000000000355512247075736023073 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #ifndef GCOMM_DEFAULTS_HPP #define GCOMM_DEFAULTS_HPP #include namespace gcomm { struct Defaults { static std::string const GMCastTcpPort ; static std::string const EvsViewForgetTimeout ; static std::string const EvsViewForgetTimeoutMin ; static std::string const EvsInactiveCheckPeriod ; static std::string const EvsSuspectTimeout ; static std::string const EvsSuspectTimeoutMin ; static std::string const EvsInactiveTimeout ; static std::string const EvsInactiveTimeoutMin ; static std::string const EvsInstallTimeout ; static std::string const EvsRetransPeriod ; static std::string const EvsRetransPeriodMin ; static std::string const EvsJoinRetransPeriod ; static std::string const EvsStatsReportPeriod ; static std::string const EvsStatsReportPeriodMin ; static std::string const EvsSendWindow ; static std::string const EvsSendWindowMin ; static std::string const EvsUserSendWindow ; static std::string const EvsUserSendWindowMin ; static std::string const EvsMaxInstallTimeouts ; static std::string const PcAnnounceTimeout ; static std::string const PcChecksum ; static std::string const PcIgnoreQuorum ; static std::string const PcIgnoreSb ; static std::string const PcNpvo ; static std::string const PcVersion ; static std::string const PcWaitPrim ; static std::string const PcWaitPrimTimeout ; static std::string const PcWeight ; }; } #endif // GCOMM_DEFAULTS_HPP percona-xtradb-cluster-galera/gcomm/src/evs_consensus.cpp0000644000000000000000000003763012247075736024155 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #include "evs_consensus.hpp" #include "evs_message2.hpp" #include "evs_input_map2.hpp" #include "evs_node.hpp" #include "gcomm/view.hpp" #include "gu_logger.hpp" #include // Disable debug logging until debug mask is available here #define evs_log_debug(i) if (true) {} else log_debug << uuid() << " " // // Helpers // class LeaveSeqCmpOp { public: bool operator()(const gcomm::evs::MessageNodeList::value_type& a, const gcomm::evs::MessageNodeList::value_type& b) const { using gcomm::evs::MessageNode; using gcomm::evs::MessageNodeList; const MessageNode& aval(MessageNodeList::value(a)); const MessageNode& bval(MessageNodeList::value(b)); gcomm_assert(aval.leaving() != false && bval.leaving() != false); const gcomm::evs::seqno_t asec(aval.leave_seq()); const gcomm::evs::seqno_t bsec(bval.leave_seq()); gcomm_assert(asec != -1 && bsec != -1); return (asec < bsec); } }; class RangeLuCmp { public: bool operator()(const gcomm::evs::MessageNodeList::value_type& a, const gcomm::evs::MessageNodeList::value_type& b) const { return (gcomm::evs::MessageNodeList::value(a).im_range().lu() < gcomm::evs::MessageNodeList::value(b).im_range().lu()); } }; class SafeSeqCmp { public: bool operator()(const gcomm::evs::MessageNodeList::value_type& a, const gcomm::evs::MessageNodeList::value_type& b) const { return (gcomm::evs::MessageNodeList::value(a).safe_seq() < gcomm::evs::MessageNodeList::value(b).safe_seq()); } }; // // // bool gcomm::evs::Consensus::equal(const Message& m1, const Message& m2) const { gcomm_assert(m1.type() == Message::T_JOIN || m1.type() == Message::T_INSTALL); gcomm_assert(m2.type() == Message::T_JOIN || m2.type() == Message::T_INSTALL); // Seq and aru seq are comparable only if coming from same view if (m1.source_view_id() == m2.source_view_id()) { if (m1.seq() != m2.seq()) { evs_log_debug(D_CONSENSUS) << "seq not equal " << m1.seq() << " " << m2.seq(); return false; } if (m1.aru_seq() != m2.aru_seq()) { evs_log_debug(D_CONSENSUS) << "aruseq not equal " << m1.aru_seq() << " " << m2.aru_seq(); return false; } } MessageNodeList nl1, nl2; // When comparing messages from same source whole node list is comparable, // otherwise only operational part of it. if (m1.source() == m2.source()) { for_each(m1.node_list().begin(), m1.node_list().end(), SelectNodesOp(nl1, m1.source_view_id(), true, true)); for_each(m2.node_list().begin(), m2.node_list().end(), SelectNodesOp(nl2, m2.source_view_id(), true, true)); } else { for_each(m1.node_list().begin(), m1.node_list().end(), SelectNodesOp(nl1, ViewId(), true, false)); for_each(m2.node_list().begin(), m2.node_list().end(), SelectNodesOp(nl2, ViewId(), true, false)); } evs_log_debug(D_CONSENSUS) << "nl1: " << nl1 << " nl2: " << nl2; return (nl1 == nl2); } gcomm::evs::seqno_t gcomm::evs::Consensus::highest_reachable_safe_seq() const { std::list seq_list; for (NodeMap::const_iterator i = known_.begin(); i != known_.end(); ++i) { const Node& node(NodeMap::value(i)); const JoinMessage* jm(node.join_message()); const LeaveMessage* lm(node.leave_message()); if ((jm == 0 && current_view_.is_member(NodeMap::key(i)) == true) || (jm != 0 && jm->source_view_id() == current_view_.id()) || (lm != 0 && lm->source_view_id() == current_view_.id())) { if (lm != 0) { // #760 - commented out: It does not matter whether // the node that has sent leave message is suspected // or not, leave message seqno is the highest that it // will declare as safe // if (node.is_suspected() == false) // { seq_list.push_back(lm->seq()); // } } else if (node.operational() == false) { seq_list.push_back( std::min( input_map_.safe_seq(node.index()), input_map_.range(node.index()).lu() - 1)); } else { seq_list.push_back(input_map_.range(node.index()).hs()); } } } return *std::min_element(seq_list.begin(), seq_list.end()); } bool gcomm::evs::Consensus::is_consistent_highest_reachable_safe_seq( const Message& msg) const { gcomm_assert(msg.type() == Message::T_JOIN || msg.type() == Message::T_INSTALL); gcomm_assert(msg.source_view_id() == current_view_.id()); const MessageNodeList& node_list(msg.node_list()); // Same view MessageNodeList same_view; for_each(node_list.begin(), node_list.end(), SelectNodesOp(same_view, current_view_.id(), true, false)); MessageNodeList::const_iterator max_hs_i(max_element(same_view.begin(), same_view.end(), RangeHsCmp())); gcomm_assert(max_hs_i != same_view.end()); // Max highest seen const seqno_t max_hs( MessageNodeList::value(max_hs_i).im_range().hs()); seqno_t max_reachable_safe_seq(max_hs); // Leaving nodes MessageNodeList leaving; for_each(node_list.begin(), node_list.end(), SelectNodesOp(leaving, current_view_.id(), false, true)); if (leaving.empty() == false) { const MessageNodeList::const_iterator min_leave_seq_i( std::min_element(leaving.begin(), leaving.end(), LeaveSeqCmpOp())); gcomm_assert(min_leave_seq_i != leaving.end()); const seqno_t min_leave_seq( MessageNodeList::value(min_leave_seq_i).leave_seq()); max_reachable_safe_seq = std::min(max_reachable_safe_seq, min_leave_seq); } // Partitioning nodes MessageNodeList partitioning; for_each(node_list.begin(), node_list.end(), SelectNodesOp(partitioning, current_view_.id(), false, false)); if (partitioning.empty() == false) { MessageNodeList::const_iterator min_part_safe_seq_i( std::min_element(partitioning.begin(), partitioning.end(), SafeSeqCmp())); gcomm_assert(min_part_safe_seq_i != partitioning.end()); const seqno_t min_part_safe_seq( MessageNodeList::value(min_part_safe_seq_i).safe_seq()); max_reachable_safe_seq = std::min(max_reachable_safe_seq, min_part_safe_seq); MessageNodeList::const_iterator min_part_lu_i( std::min_element(partitioning.begin(), partitioning.end(), RangeLuCmp())); gcomm_assert(min_part_lu_i != partitioning.end()); const seqno_t min_part_lu(MessageNodeList::value(min_part_lu_i).im_range().lu() - 1); max_reachable_safe_seq = std::min(max_reachable_safe_seq, min_part_lu); } evs_log_debug(D_CONSENSUS) << " max reachable safe seq " << max_reachable_safe_seq << " highest reachable safe seq " << highest_reachable_safe_seq() << " max_hs " << max_hs << " input map max hs " << input_map_.max_hs() << " input map safe_seq " << input_map_.safe_seq(); return (input_map_.max_hs() == max_hs && highest_reachable_safe_seq() == max_reachable_safe_seq && input_map_.safe_seq() == max_reachable_safe_seq); } bool gcomm::evs::Consensus::is_consistent_input_map(const Message& msg) const { gcomm_assert(msg.type() == Message::T_JOIN || msg.type() == Message::T_INSTALL); gcomm_assert(msg.source_view_id() == current_view_.id()); if (msg.aru_seq() != input_map_.aru_seq()) { evs_log_debug(D_CONSENSUS) << "message aru seq " << msg.aru_seq() << " not consistent with input map aru seq " << input_map_.aru_seq(); return false; } if (msg.seq() != input_map_.safe_seq()) { evs_log_debug(D_CONSENSUS) << "message safe seq " << msg.seq() << " not consistent with input map safe seq " << input_map_.safe_seq(); return false; } Map local_insts, msg_insts; for (NodeMap::const_iterator i = known_.begin(); i != known_.end(); ++i) { const UUID& uuid(NodeMap::key(i)); const Node& node(NodeMap::value(i)); if (current_view_.is_member(uuid) == true) { gu_trace((void)local_insts.insert_unique( std::make_pair(uuid, input_map_.range(node.index())))); } } const MessageNodeList& m_insts(msg.node_list()); for (MessageNodeList::const_iterator i = m_insts.begin(); i != m_insts.end(); ++i) { const UUID& msg_uuid(MessageNodeList::key(i)); const MessageNode& msg_inst(MessageNodeList::value(i)); if (msg_inst.view_id() == current_view_.id()) { gu_trace((void)msg_insts.insert_unique( std::make_pair(msg_uuid, msg_inst.im_range()))); } } evs_log_debug(D_CONSENSUS) << " msg_insts " << msg_insts << " local_insts " << local_insts; return (msg_insts == local_insts); } bool gcomm::evs::Consensus::is_consistent_partitioning(const Message& msg) const { gcomm_assert(msg.type() == Message::T_JOIN || msg.type() == Message::T_INSTALL); gcomm_assert(msg.source_view_id() == current_view_.id()); // Compare instances that were present in the current view but are // not proceeding in the next view. Map local_insts, msg_insts; for (NodeMap::const_iterator i = known_.begin(); i != known_.end(); ++i) { const UUID& uuid(NodeMap::key(i)); const Node& node(NodeMap::value(i)); if (node.operational() == false && node.leave_message() == 0 && current_view_.is_member(uuid) == true) { gu_trace((void)local_insts.insert_unique( std::make_pair(uuid, input_map_.range(node.index())))); } } const MessageNodeList& m_insts = msg.node_list(); for (MessageNodeList::const_iterator i = m_insts.begin(); i != m_insts.end(); ++i) { const UUID& m_uuid(MessageNodeList::key(i)); const MessageNode& m_inst(MessageNodeList::value(i)); if (m_inst.operational() == false && m_inst.leaving() == false && m_inst.view_id() == current_view_.id()) { gu_trace((void)msg_insts.insert_unique( std::make_pair(m_uuid, m_inst.im_range()))); } } evs_log_debug(D_CONSENSUS) << " msg insts:\n" << msg_insts << " local insts:\n" << local_insts; return (msg_insts == local_insts); } bool gcomm::evs::Consensus::is_consistent_leaving(const Message& msg) const { gcomm_assert(msg.type() == Message::T_JOIN || msg.type() == Message::T_INSTALL); gcomm_assert(msg.source_view_id() == current_view_.id()); // Compare instances that were present in the current view but are // not proceeding in the next view. Map local_insts, msg_insts; for (NodeMap::const_iterator i = known_.begin(); i != known_.end(); ++i) { const UUID& uuid(NodeMap::key(i)); const Node& inst(NodeMap::value(i)); const LeaveMessage* lm(inst.leave_message()); if (inst.operational() == false && lm != 0 && lm->source_view_id() == current_view_.id()) { gu_trace((void)local_insts.insert_unique( std::make_pair(uuid, input_map_.range(inst.index())))); } } const MessageNodeList& m_insts = msg.node_list(); for (MessageNodeList::const_iterator i = m_insts.begin(); i != m_insts.end(); ++i) { const UUID& m_uuid(MessageNodeList::key(i)); const MessageNode& m_inst(MessageNodeList::value(i)); if (m_inst.operational() == false && m_inst.leaving() == true && m_inst.view_id() == current_view_.id()) { gu_trace((void)msg_insts.insert_unique( std::make_pair(m_uuid, m_inst.im_range()))); } } evs_log_debug(D_CONSENSUS) << " msg insts " << msg_insts << " local insts " << local_insts; return (local_insts == msg_insts); } bool gcomm::evs::Consensus::is_consistent_same_view(const Message& msg) const { gcomm_assert(msg.type() == Message::T_JOIN || msg.type() == Message::T_INSTALL); gcomm_assert(msg.source_view_id() == current_view_.id()); if (is_consistent_highest_reachable_safe_seq(msg) == false) { evs_log_debug(D_CONSENSUS) << "highest reachable safe seq not consistent"; return false; } if (is_consistent_input_map(msg) == false) { evs_log_debug(D_CONSENSUS) << "input map not consistent with " << msg; return false; } if (is_consistent_partitioning(msg) == false) { evs_log_debug(D_CONSENSUS) << "partitioning not consistent with " << msg; return false; } if (is_consistent_leaving(msg) == false) { evs_log_debug(D_CONSENSUS) << "leaving not consistent with " << msg; return false; } return true; } bool gcomm::evs::Consensus::is_consistent(const Message& msg) const { gcomm_assert(msg.type() == Message::T_JOIN || msg.type() == Message::T_INSTALL); const JoinMessage* my_jm = NodeMap::value(known_.find_checked(uuid())).join_message(); if (my_jm == 0) { return false; } if (msg.source_view_id() == current_view_.id()) { return (is_consistent_same_view(msg) == true && equal(msg, *my_jm) == true); } else { return equal(msg, *my_jm); } } bool gcomm::evs::Consensus::is_consensus() const { const JoinMessage* my_jm = NodeMap::value(known_.find_checked(uuid())).join_message(); if (my_jm == 0) { evs_log_debug(D_CONSENSUS) << "no own join message"; return false; } if (is_consistent_same_view(*my_jm) == false) { evs_log_debug(D_CONSENSUS) << "own join message not consistent"; return false; } for (NodeMap::const_iterator i = known_.begin(); i != known_.end(); ++i) { const Node& inst(NodeMap::value(i)); if (inst.operational() == true) { const JoinMessage* jm = inst.join_message(); if (jm == 0) { evs_log_debug(D_CONSENSUS) << "no join message for " << NodeMap::key(i); return false; } // call is_consistent() instead of equal() to enforce strict // check for messages originating from the same view (#541) if (is_consistent(*jm) == false) { evs_log_debug(D_CONSENSUS) << "join message " << *jm << " not consistent with my join " << *my_jm; return false; } } } return true; } percona-xtradb-cluster-galera/gcomm/src/evs_consensus.hpp0000644000000000000000000000324312247075736024153 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #include "evs_seqno.hpp" namespace gcomm { class UUID; class View; namespace evs { class NodeMap; class InputMap; class Message; class Consensus; } } class gcomm::evs::Consensus { public: Consensus(const UUID& uuid, const NodeMap& known, const InputMap& input_map, const View& current_view) : uuid_ (uuid), known_ (known), input_map_ (input_map), current_view_(current_view) { } /*! * Compare two messages if they are equal in consensus context. */ bool equal(const Message&, const Message&) const; /*! * Compute highest reachable safe seq from local state. * * @return Highest reachable safe seq. */ seqno_t highest_reachable_safe_seq() const; /*! * Check if highest reachable safe seq according to message * consistent with local state. */ bool is_consistent_highest_reachable_safe_seq(const Message&) const; /*! * Check if message aru seq, safe seq and node ranges matches to * local state. */ bool is_consistent_input_map(const Message&) const; bool is_consistent_partitioning(const Message&) const; bool is_consistent_leaving(const Message&) const; bool is_consistent_same_view(const Message&) const; bool is_consistent(const Message&) const; bool is_consensus() const; private: const UUID& uuid() const { return uuid_; } const UUID& uuid_; const NodeMap& known_; const InputMap& input_map_; const View& current_view_; }; percona-xtradb-cluster-galera/gcomm/src/evs_input_map2.cpp0000644000000000000000000002572312247075736024213 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #include "evs_input_map2.hpp" #include "gcomm/util.hpp" #include "gu_exception.hpp" #include "gu_logger.hpp" #include "gu_buffer.hpp" #include #include ////////////////////////////////////////////////////////////////////////// // // Static operators and functions // ////////////////////////////////////////////////////////////////////////// // Compare node index LUs class NodeIndexLUCmpOp { public: bool operator()(const gcomm::evs::InputMapNodeIndex::value_type& a, const gcomm::evs::InputMapNodeIndex::value_type& b) const { return (a.range().lu() < b.range().lu()); } }; class NodeIndexHSCmpOp { public: bool operator()(const gcomm::evs::InputMapNodeIndex::value_type& a, const gcomm::evs::InputMapNodeIndex::value_type& b) const { return (a.range().hs() < b.range().hs()); } }; // Compare node index safe seqs class NodeIndexSafeSeqCmpOp { public: bool operator()(const gcomm::evs::InputMapNodeIndex::value_type& a, const gcomm::evs::InputMapNodeIndex::value_type& b) const { return a.safe_seq() < b.safe_seq(); } }; ////////////////////////////////////////////////////////////////////////// // // Ostream operators // ////////////////////////////////////////////////////////////////////////// std::ostream& gcomm::evs::operator<<(std::ostream& os, const InputMapNode& in) { return (os << "node: {" << "idx=" << in.index() << "," << "range=" << in.range() << "," << "safe_seq=" << in.safe_seq() << "}"); } std::ostream& gcomm::evs::operator<<(std::ostream& os, const InputMapNodeIndex& ni) { copy(ni.begin(), ni.end(), std::ostream_iterator(os, " ")); return os; } std::ostream& gcomm::operator<<(std::ostream& os, const InputMapMsgKey& mk) { return (os << "(" << mk.index() << "," << mk.seq() << ")"); } std::ostream& gcomm::evs::operator<<(std::ostream& os, const InputMapMsg& m) { return (os << m.msg()); } std::ostream& gcomm::evs::operator<<(std::ostream& os, const InputMap& im) { return (os << "evs::input_map: {" << "aru_seq=" << im.aru_seq() << "," << "safe_seq=" << im.safe_seq() << "," << "node_index=" << *im.node_index_ #ifndef NDEBUG << "," << "msg_index=" << *im.msg_index_ << "," << "recovery_index=" << *im.recovery_index_ #endif // !NDEBUG << "}"); } ////////////////////////////////////////////////////////////////////////// // // Constructors/destructors // ////////////////////////////////////////////////////////////////////////// gcomm::evs::InputMap::InputMap() : window_ (-1), safe_seq_ (-1), aru_seq_ (-1), node_index_ (new InputMapNodeIndex()), msg_index_ (new InputMapMsgIndex()), recovery_index_ (new InputMapMsgIndex()), n_msgs_ (O_SAFE + 1), max_droppable_ (16) { } gcomm::evs::InputMap::~InputMap() { clear(); delete node_index_; delete msg_index_; delete recovery_index_; } ////////////////////////////////////////////////////////////////////////// // // Public member functions // ////////////////////////////////////////////////////////////////////////// void gcomm::evs::InputMap::reset(const size_t nodes, const seqno_t window) { gcomm_assert(msg_index_->empty() == true && recovery_index_->empty() == true && accumulate(n_msgs_.begin(), n_msgs_.end(), 0) == 0); node_index_->clear(); window_ = window; log_debug << " size " << node_index_->size(); gu_trace(node_index_->resize(nodes, InputMapNode())); for (size_t i = 0; i < nodes; ++i) { node_index_->at(i).set_index(i); } log_debug << *node_index_ << " size " << node_index_->size(); } gcomm::evs::seqno_t gcomm::evs::InputMap::min_hs() const { seqno_t ret; gcomm_assert(node_index_->empty() == false); ret = min_element(node_index_->begin(), node_index_->end(), NodeIndexHSCmpOp())->range().hs(); return ret; } gcomm::evs::seqno_t gcomm::evs::InputMap::max_hs() const { seqno_t ret; gcomm_assert(node_index_->empty() == false); ret = max_element(node_index_->begin(), node_index_->end(), NodeIndexHSCmpOp())->range().hs(); return ret; } void gcomm::evs::InputMap::set_safe_seq(const size_t uuid, const seqno_t seq) { gcomm_assert(seq != -1); // @note This assertion does not necessarily hold. Some other // instance may well have higher all received up to seqno // than this (due to packet loss). Commented out... and left // for future reference. // gcomm_assert(aru_seq != seqno_t::max() && seq <= aru_seq); // Update node safe seq. Must (at least should) be updated // in monotonically increasing order if node works ok. InputMapNode& node(node_index_->at(uuid)); gcomm_assert(seq >= node.safe_seq()) << "node.safe_seq=" << node.safe_seq() << " seq=" << seq; node.set_safe_seq(seq); // Update global safe seq which must be monotonically increasing. InputMapNodeIndex::const_iterator min = min_element(node_index_->begin(), node_index_->end(), NodeIndexSafeSeqCmpOp()); const seqno_t minval = min->safe_seq(); gcomm_assert(minval >= safe_seq_); safe_seq_ = minval; // Global safe seq must always be smaller than equal to aru seq gcomm_assert(safe_seq_ <= aru_seq_); // Cleanup recovery index cleanup_recovery_index(); } void gcomm::evs::InputMap::clear() { if (msg_index_->empty() == false) { log_warn << "discarding " << msg_index_->size() << " messages from message index"; } msg_index_->clear(); if (recovery_index_->empty() == false) { log_debug << "discarding " << recovery_index_->size() << " messages from recovery index"; } recovery_index_->clear(); node_index_->clear(); aru_seq_ = -1; safe_seq_ = -1; fill(n_msgs_.begin(), n_msgs_.end(), 0); } gcomm::evs::Range gcomm::evs::InputMap::insert(const size_t uuid, const UserMessage& msg, const Datagram& rb) { Range range; // Only insert messages with meaningful seqno gcomm_assert(msg.seq() > -1); // User should check aru_seq before inserting. This check is left // also in optimized builds since violating it may cause duplicate // messages. gcomm_assert(aru_seq_ < msg.seq()) << "aru seq " << aru_seq_ << " msg seq " << msg.seq() << " index size " << msg_index_->size(); gcomm_assert(uuid < node_index_->size()); InputMapNode& node((*node_index_)[uuid]); range = node.range(); // User should check LU before inserting. This check is left // also in optimized builds since violating it may cause duplicate // messages gcomm_assert(range.lu() <= msg.seq()) << "lu " << range.lu() << " > " << msg.seq(); // Check whether this message has already been seen if (msg.seq() < node.range().lu() || (msg.seq() <= node.range().hs() && recovery_index_->find(InputMapMsgKey(node.index(), msg.seq())) != recovery_index_->end())) { return node.range(); } // Loop over message seqno range and insert messages when not // already found for (seqno_t s = msg.seq(); s <= msg.seq() + msg.seq_range(); ++s) { InputMapMsgIndex::iterator msg_i; if (range.hs() < s) { msg_i = msg_index_->end(); } else { msg_i = msg_index_->find(InputMapMsgKey(node.index(), s)); } if (msg_i == msg_index_->end()) { Datagram ins_dg(s == msg.seq() ? Datagram(rb) : Datagram()); gu_trace((void)msg_index_->insert_unique( std::make_pair( InputMapMsgKey(node.index(), s), InputMapMsg( (s == msg.seq() ? msg : UserMessage(msg.version(), msg.source(), msg.source_view_id(), s, msg.aru_seq(), 0, O_DROP)), ins_dg)))); ++n_msgs_[msg.order()]; } // Update highest seen if (range.hs() < s) { range.set_hs(s); } // Update lowest unseen if (range.lu() == s) { seqno_t i(s); do { ++i; } while ( i <= range.hs() && (msg_index_->find(InputMapMsgKey(node.index(), i)) != msg_index_->end() || recovery_index_->find(InputMapMsgKey(node.index(), i)) != recovery_index_->end())); range.set_lu(i); } } node.set_range(range); update_aru(); return range; } void gcomm::evs::InputMap::erase(iterator i) { const UserMessage& msg(InputMapMsgIndex::value(i).msg()); --n_msgs_[msg.order()]; gu_trace(recovery_index_->insert_unique(*i)); gu_trace(msg_index_->erase(i)); } gcomm::evs::InputMap::iterator gcomm::evs::InputMap::find(const size_t uuid, const seqno_t seq) const { iterator ret; const InputMapNode& node(node_index_->at(uuid)); const InputMapMsgKey key(node.index(), seq); gu_trace(ret = msg_index_->find(key)); return ret; } gcomm::evs::InputMap::iterator gcomm::evs::InputMap::recover(const size_t uuid, const seqno_t seq) const { iterator ret; const InputMapNode& node(node_index_->at(uuid)); const InputMapMsgKey key(node.index(), seq); gu_trace(ret = recovery_index_->find_checked(key)); return ret; } ////////////////////////////////////////////////////////////////////////// // // Private member functions // ////////////////////////////////////////////////////////////////////////// inline void gcomm::evs::InputMap::update_aru() { InputMapNodeIndex::const_iterator min = min_element(node_index_->begin(), node_index_->end(), NodeIndexLUCmpOp()); const seqno_t minval = min->range().lu(); /* aru_seq must not decrease */ gcomm_assert(minval - 1 >= aru_seq_); aru_seq_ = minval - 1; } void gcomm::evs::InputMap::cleanup_recovery_index() { gcomm_assert(node_index_->size() > 0); InputMapMsgIndex::iterator i = recovery_index_->lower_bound( InputMapMsgKey(0, safe_seq_ + 1)); recovery_index_->erase(recovery_index_->begin(), i); } percona-xtradb-cluster-galera/gcomm/src/evs_input_map2.hpp0000644000000000000000000002306112247075736024211 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy * * $Id$ */ /*! * @file Input map for EVS messaging. Provides simple interface for * handling messages with different safety guarantees. * * @note When operating with iterators, note that evs::Message * accessed through iterator may have different sequence * number as it position dictates. Use sequence number * found from key part. * * @todo Fix issue in above note if feasible. */ #ifndef EVS_INPUT_MAP2_HPP #define EVS_INPUT_MAP2_HPP #include "evs_message2.hpp" #include "gcomm/map.hpp" #include "gcomm/datagram.hpp" #include namespace gcomm { /* Forward declarations */ class InputMapMsgKey; std::ostream& operator<<(std::ostream&, const InputMapMsgKey&); namespace evs { class InputMapMsg; std::ostream& operator<<(std::ostream&, const InputMapMsg&); class InputMapMsgIndex; class InputMapNode; std::ostream& operator<<(std::ostream&, const InputMapNode&); typedef std::vector InputMapNodeIndex; std::ostream& operator<<(std::ostream&, const InputMapNodeIndex&); class InputMap; } } /* Internal msg representation */ class gcomm::InputMapMsgKey { public: InputMapMsgKey(const size_t index, const evs::seqno_t seq) : index_ (index), seq_ (seq) { } size_t index() const { return index_; } evs::seqno_t seq () const { return seq_; } bool operator<(const InputMapMsgKey& cmp) const { return (seq_ < cmp.seq_ || (seq_ == cmp.seq_ && index_ < cmp.index_)); } private: size_t const index_; evs::seqno_t const seq_; }; /* Internal message representation */ class gcomm::evs::InputMapMsg { public: InputMapMsg(const UserMessage& msg, const Datagram& rb) : msg_(msg), rb_ (rb) { } InputMapMsg(const InputMapMsg& m) : msg_(m.msg_), rb_ (m.rb_) { } ~InputMapMsg() { } const UserMessage& msg () const { return msg_; } const Datagram& rb () const { return rb_; } private: void operator=(const InputMapMsg&); UserMessage const msg_; Datagram rb_; }; #if defined(GALERA_USE_BOOST_POOL_ALLOC) #include class gcomm::evs::InputMapMsgIndex : public Map, boost::fast_pool_allocator< std::pair, boost::default_user_allocator_new_delete, boost::details::pool::null_mutex > > > {}; #else /* GALERA_USE_BOOST_POOL_ALLOC */ class gcomm::evs::InputMapMsgIndex : public Map {}; #endif /* GALERA_USE_BOOST_POOL_ALLOC */ /* Internal node representation */ class gcomm::evs::InputMapNode { public: InputMapNode() : idx_(), range_(0, -1), safe_seq_(-1) { } void set_range (const Range r) { range_ = r; } void set_safe_seq (const seqno_t s) { safe_seq_ = s; } void set_index (const size_t i) { idx_ = i; } Range range () const { return range_; } seqno_t safe_seq () const { return safe_seq_; } size_t index () const { return idx_; } private: size_t idx_; Range range_; seqno_t safe_seq_; }; /*! * Input map for messages. * */ class gcomm::evs::InputMap { public: /* Iterators exposed to user */ typedef InputMapMsgIndex::iterator iterator; typedef InputMapMsgIndex::const_iterator const_iterator; /*! * Default constructor. */ InputMap(); /*! * Default destructor. */ ~InputMap(); /*! * Get current value of aru_seq. * * @return Current value of aru_seq */ seqno_t aru_seq () const { return aru_seq_; } /*! * Get current value of safe_seq. * * @return Current value of safe_seq */ seqno_t safe_seq() const { return safe_seq_; } /*! * Set sequence number safe for node. * * @param uuid Node uuid * @param seq Sequence number to be set safe * * @throws FatalException if node was not found or sequence number * was not in the allowed range */ void set_safe_seq(const size_t uuid, const seqno_t seq); /*! * Get current value of safe_seq for node. * * @param uuid Node uuid * * @return Safe sequence number for node * * @throws FatalException if node was not found */ seqno_t safe_seq(const size_t uuid) const { return node_index_->at(uuid).safe_seq(); } /*! * Get current range parameter for node * * @param uuid Node uuid * * @return Range parameter for node * * @throws FatalException if node was not found */ Range range (const size_t uuid) const { return node_index_->at(uuid).range(); } seqno_t min_hs() const; seqno_t max_hs() const; /*! * Get iterator to the beginning of the input map * * @return Iterator pointing to the first element */ iterator begin() const { return msg_index_->begin(); } /*! * Get iterator next to the last element of the input map * * @return Iterator pointing past the last element */ iterator end () const { return msg_index_->end(); } /*! * Check if message pointed by iterator fulfills O_SAFE condition. * * @return True or false */ bool is_safe (iterator i) const { const seqno_t seq(InputMapMsgIndex::key(i).seq()); return (seq <= safe_seq_); } /*! * Check if message pointed by iterator fulfills O_AGREED condition. * * @return True or false */ bool is_agreed(iterator i) const { const seqno_t seq(InputMapMsgIndex::key(i).seq()); return (seq <= aru_seq_); } /*! * Check if message pointed by iterator fulfills O_FIFO condition. * * @return True or false */ bool is_fifo (iterator i) const { const seqno_t seq(InputMapMsgIndex::key(i).seq()); const InputMapNode& node((*node_index_)[ InputMapMsgIndex::key(i).index()]); return (node.range().lu() > seq); } bool has_deliverables() const { if (msg_index_->empty() == false) { if (n_msgs_[O_FIFO] > 0 && is_fifo(msg_index_->begin())) return true; else if (n_msgs_[O_AGREED] > 0 && is_agreed(msg_index_->begin())) return true; else if (n_msgs_[O_SAFE] > 0 && is_safe(msg_index_->begin())) return true; else if (n_msgs_[O_DROP] > max_droppable_) return true; return false; } else { return false; } } /*! * Insert new message into input map. * * @param uuid Node uuid of the message source * @param msg EVS message * @param rb ReadBuf pointer associated to message * @param offset Offset to the beginning of the payload * * @return Range parameter of the node * * @throws FatalException if node not found or message sequence * number is out of allowed range */ Range insert(const size_t uuid, const UserMessage& msg, const Datagram& dg = Datagram()); /*! * Erase message pointed by iterator. Note that message may still * be recovered through recover() method as long as it does not * fulfill O_SAFE constraint. * * @param i Iterator * * @throws FatalException if iterator is not valid */ void erase(iterator i); /*! * Find message. * * @param uuid Message source node uuid * @param seq Message sequence numeber * * @return Iterator pointing to message or at end() if message was not found * * @throws FatalException if node was not found */ iterator find(const size_t uuid, const seqno_t seq) const; /*! * Recover message. * * @param uuid Message source node uuid * @param seq Message sequence number * * @return Iterator pointing to the message * * @throws FatalException if node or message was not found */ iterator recover(const size_t uuid, const seqno_t seq) const; /*! * */ void reset(const size_t, const seqno_t = 256); /*! * Clear input map state. */ void clear(); private: friend std::ostream& operator<<(std::ostream&, const InputMap&); /* Non-copyable */ InputMap(const InputMap&); void operator=(const InputMap&); /*! * Update aru_seq value to represent current state. */ void update_aru(); /*! * Clean up recovery index. All messages up to safe_seq are removed. */ void cleanup_recovery_index(); seqno_t window_; seqno_t safe_seq_; /*!< Safe seqno */ seqno_t aru_seq_; /*!< All received upto seqno */ InputMapNodeIndex* node_index_; /*!< Index of nodes */ InputMapMsgIndex* msg_index_; /*!< Index of messages */ InputMapMsgIndex* recovery_index_; /*!< Recovery index */ std::vector n_msgs_; size_t max_droppable_; }; #endif // EVS_INPUT_MAP2_HPP percona-xtradb-cluster-galera/gcomm/src/evs_message2.cpp0000644000000000000000000003765612247075736023653 0ustar rootroot00000000000000/* * Copyright (C) 2009-2012 Codership Oy * * $Id$ */ #include "evs_message2.hpp" #include "gu_exception.hpp" #include "gu_logger.hpp" std::ostream& gcomm::evs::operator<<(std::ostream& os, const gcomm::evs::MessageNode& node) { os << "node: {"; os << "operational=" << node.operational() << ","; os << "suspected=" << node.suspected() << ","; os << "leave_seq=" << node.leave_seq() << ","; os << "view_id=" << node.view_id() << ","; os << "safe_seq=" << node.safe_seq() << ","; os << "im_range=" << node.im_range() << ","; os << "}"; return os; } std::ostream& gcomm::evs::operator<<(std::ostream& os, const gcomm::evs::Message& msg) { os << "evs::msg{"; os << "version=" << static_cast(msg.version()) << ","; os << "type=" << msg.type() << ","; os << "user_type=" << static_cast(msg.user_type()) << ","; os << "order=" << msg.order() << ","; os << "seq=" << msg.seq() << ","; os << "seq_range=" << msg.seq_range() << ","; os << "aru_seq=" << msg.aru_seq() << ","; os << "flags=" << static_cast(msg.flags()) << ","; os << "source=" << msg.source() << ","; os << "source_view_id=" << msg.source_view_id() << ","; os << "range_uuid=" << msg.range_uuid() << ","; os << "range=" << msg.range() << ","; os << "fifo_seq=" << msg.fifo_seq() << ","; os << "node_list=(" << msg.node_list() << ")\n"; os << "}"; return os; } size_t gcomm::evs::MessageNode::serialize(gu::byte_t* const buf, size_t const buflen, size_t offset) const { uint8_t b = static_cast((operational_ == true ? F_OPERATIONAL : 0) | (suspected_ == true ? F_SUSPECTED : 0)); gu_trace(offset = gu::serialize1(b, buf, buflen, offset)); uint8_t pad(0); gu_trace(offset = gu::serialize1(pad, buf, buflen, offset)); gu_trace(offset = gu::serialize8(leave_seq_, buf, buflen, offset)); gu_trace(offset = view_id_.serialize(buf, buflen, offset)); gu_trace(offset = gu::serialize8(safe_seq_, buf, buflen, offset)); gu_trace(offset = im_range_.serialize(buf, buflen, offset)); return offset; } size_t gcomm::evs::MessageNode::unserialize(const gu::byte_t* const buf, size_t const buflen, size_t offset) { uint8_t b; gu_trace(offset = gu::unserialize1(buf, buflen, offset, b)); if ((b & ~(F_OPERATIONAL | F_SUSPECTED)) != 0) { log_warn << "unknown flags: " << static_cast(b); } operational_ = b & F_OPERATIONAL; suspected_ = b & F_SUSPECTED; uint8_t pad(0); gu_trace(offset = gu::unserialize1(buf, buflen, offset, pad)); if (pad != 0) { gu_throw_error(EINVAL) << "invalid pad" << pad; } gu_trace(offset = gu::unserialize8(buf, buflen, offset, leave_seq_)); gu_trace(offset = view_id_.unserialize(buf, buflen, offset)); gu_trace(offset = gu::unserialize8(buf, buflen, offset, safe_seq_)); gu_trace(offset = im_range_.unserialize(buf, buflen, offset)); return offset; } size_t gcomm::evs::MessageNode::serial_size() { return 2 + // 4 bytes reserved for flags sizeof(seqno_t) + ViewId::serial_size() + sizeof(seqno_t) + Range::serial_size(); } bool gcomm::evs::Message::operator==(const Message& cmp) const { return (version_ == cmp.version_ && type_ == cmp.type_ && user_type_ == cmp.user_type_ && order_ == cmp.order_ && seq_ == cmp.seq_ && seq_range_ == cmp.seq_range_ && aru_seq_ == cmp.aru_seq_ && fifo_seq_ == cmp.fifo_seq_ && flags_ == cmp.flags_ && source_ == cmp.source_ && source_view_id_ == cmp.source_view_id_ && install_view_id_ == cmp.install_view_id_ && range_uuid_ == cmp.range_uuid_ && range_ == cmp.range_ && node_list_ == cmp.node_list_); } size_t gcomm::evs::Message::serialize(gu::byte_t* const buf, size_t const buflen, size_t offset) const { uint8_t b = static_cast(version_ | (type_ << 2) | (order_ << 5)); gu_trace(offset = gu::serialize1(b, buf, buflen, offset)); gu_trace(offset = gu::serialize1(flags_, buf, buflen, offset)); uint16_t pad(0); gu_trace(offset = gu::serialize2(pad, buf, buflen, offset)); gu_trace(offset = gu::serialize8(fifo_seq_, buf, buflen, offset)); if (flags_ & F_SOURCE) { gu_trace(offset = source_.serialize(buf, buflen, offset)); } gu_trace(offset = source_view_id_.serialize(buf, buflen, offset)); return offset; } size_t gcomm::evs::Message::unserialize(const gu::byte_t* const buf, size_t const buflen, size_t offset) { uint8_t b; gu_trace(offset = gu::unserialize1(buf, buflen, offset, b)); version_ = static_cast(b & 0x3); if (version_ != 0) { gu_throw_error(EPROTONOSUPPORT) << "protocol version not supported: " << version_; } type_ = static_cast((b >> 2) & 0x7); if (type_ <= T_NONE || type_ > T_LEAVE) { gu_throw_error(EINVAL) << "invalid type " << type_; } order_ = static_cast((b >> 5) & 0x7); if (order_ < O_DROP || order_ > O_SAFE) { gu_throw_error(EINVAL) << "invalid safety prefix " << order_; } gu_trace(offset = gu::unserialize1(buf, buflen, offset, flags_)); uint16_t pad; gu_trace(offset = gu::unserialize2(buf, buflen, offset, pad)); if (pad != 0) { gu_throw_error(EINVAL) << "invalid pad" << pad; } gu_trace(offset = gu::unserialize8(buf, buflen, offset, fifo_seq_)); if (flags_ & F_SOURCE) { gu_trace(offset = source_.unserialize(buf, buflen, offset)); } gu_trace(offset = source_view_id_.unserialize(buf, buflen, offset)); return offset; } size_t gcomm::evs::Message::serial_size() const { return (1 + // version | type | order 1 + // flags 2 + // pad sizeof(fifo_seq_) + // fifo_seq ((flags_ & F_SOURCE) ? UUID::serial_size() : 0) + ViewId::serial_size()); // source_view_id } size_t gcomm::evs::UserMessage::serialize(gu::byte_t* const buf, size_t const buflen, size_t offset) const { gu_trace(offset = Message::serialize(buf, buflen, offset)); gu_trace(offset = gu::serialize1(user_type_, buf, buflen, offset)); gcomm_assert(seq_range_ <= seqno_t(0xff)); uint8_t b = static_cast(seq_range_); gu_trace(offset = gu::serialize1(b, buf, buflen, offset)); gu_trace(offset = gu::serialize2(uint16_t(0), buf, buflen, offset)); gu_trace(offset = gu::serialize8(seq_, buf, buflen, offset)); gu_trace(offset = gu::serialize8(aru_seq_, buf, buflen, offset)); return offset; } size_t gcomm::evs::UserMessage::unserialize(const gu::byte_t* const buf, size_t const buflen, size_t offset, bool skip_header) { if (skip_header == false) { gu_trace(offset = Message::unserialize(buf, buflen, offset)); } gu_trace(offset = gu::unserialize1(buf, buflen, offset, user_type_)); uint8_t b; gu_trace(offset = gu::unserialize1(buf, buflen, offset, b)); seq_range_ = b; uint16_t pad; gu_trace(offset = gu::unserialize2(buf, buflen, offset, pad)); if (pad != 0) { log_warn << "invalid pad: " << pad; } gu_trace(offset = gu::unserialize8(buf, buflen, offset, seq_)); gu_trace(offset = gu::unserialize8(buf, buflen, offset, aru_seq_)); return offset; } size_t gcomm::evs::UserMessage::serial_size() const { return Message::serial_size() + // Header 1 + // User type 1 + // Seq range 2 + // Pad/reserved sizeof(seqno_t) + // Seq sizeof(seqno_t); // Aru seq } size_t gcomm::evs::AggregateMessage::serialize(gu::byte_t* const buf, size_t const buflen, size_t offset) const { gu_trace(offset = gu::serialize1(flags_, buf, buflen, offset)); gu_trace(offset = gu::serialize1(user_type_, buf, buflen, offset)); gu_trace(offset = gu::serialize2(len_, buf, buflen, offset)); return offset; } size_t gcomm::evs::AggregateMessage::unserialize(const gu::byte_t* const buf, size_t const buflen, size_t offset) { gu_trace(offset = gu::unserialize1(buf, buflen, offset, flags_)); gu_trace(offset = gu::unserialize1(buf, buflen, offset, user_type_)); gu_trace(offset = gu::unserialize2(buf, buflen, offset, len_)); return offset; } size_t gcomm::evs::AggregateMessage::serial_size() const { return sizeof(flags_) + sizeof(len_) + sizeof(user_type_); } size_t gcomm::evs::DelegateMessage::serialize(gu::byte_t* const buf, size_t const buflen, size_t offset) const { gu_trace(offset = Message::serialize(buf, buflen, offset)); return offset; } size_t gcomm::evs::DelegateMessage::unserialize(const gu::byte_t* const buf, size_t const buflen, size_t offset, bool skip_header) { if (skip_header == false) { gu_trace(offset = Message::unserialize(buf, buflen, offset)); } return offset; } size_t gcomm::evs::DelegateMessage::serial_size() const { return Message::serial_size(); } size_t gcomm::evs::GapMessage::serialize(gu::byte_t* const buf, size_t const buflen, size_t offset) const { gu_trace(offset = Message::serialize(buf, buflen, offset)); gu_trace(offset = gu::serialize8(seq_, buf, buflen, offset)); gu_trace(offset = gu::serialize8(aru_seq_, buf, buflen, offset)); gu_trace(offset = range_uuid_.serialize(buf, buflen, offset)); gu_trace(offset = range_.serialize(buf, buflen, offset)); return offset; } size_t gcomm::evs::GapMessage::unserialize(const gu::byte_t* const buf, size_t const buflen, size_t offset, bool skip_header) { if (skip_header == false) { gu_trace(offset = Message::unserialize(buf, buflen, offset)); } gu_trace(offset = gu::unserialize8(buf, buflen, offset, seq_)); gu_trace(offset = gu::unserialize8(buf, buflen, offset, aru_seq_)); gu_trace(offset = range_uuid_.unserialize(buf, buflen, offset)); gu_trace(offset = range_.unserialize(buf, buflen, offset)); return offset; } size_t gcomm::evs::GapMessage::serial_size() const { return (Message::serial_size() + 2 * sizeof(seqno_t) + UUID::serial_size() + Range::serial_size()); } size_t gcomm::evs::JoinMessage::serialize(gu::byte_t* const buf, size_t const buflen, size_t offset) const { gu_trace(offset = Message::serialize(buf, buflen, offset)); gu_trace(offset = gu::serialize8(seq_, buf, buflen, offset)); gu_trace(offset = gu::serialize8(aru_seq_, buf, buflen, offset)); gu_trace(offset = node_list_.serialize(buf, buflen, offset)); return offset; } size_t gcomm::evs::JoinMessage::unserialize(const gu::byte_t* const buf, size_t const buflen, size_t offset, bool skip_header) { if (skip_header == false) { gu_trace(offset = Message::unserialize(buf, buflen, offset)); } gu_trace(offset = gu::unserialize8(buf, buflen, offset, seq_)); gu_trace(offset = gu::unserialize8(buf, buflen, offset, aru_seq_)); node_list_.clear(); gu_trace(offset = node_list_.unserialize(buf, buflen, offset)); return offset; } size_t gcomm::evs::JoinMessage::serial_size() const { return (Message::serial_size() + 2 * sizeof(seqno_t) + node_list_.serial_size()); } size_t gcomm::evs::InstallMessage::serialize(gu::byte_t* const buf, size_t const buflen, size_t offset) const { gu_trace(offset = Message::serialize(buf, buflen, offset)); gu_trace(offset = gu::serialize8(seq_, buf, buflen, offset)); gu_trace(offset = gu::serialize8(aru_seq_, buf, buflen, offset)); gu_trace(offset = install_view_id_.serialize(buf, buflen, offset)); gu_trace(offset = node_list_.serialize(buf, buflen, offset)); return offset; } size_t gcomm::evs::InstallMessage::unserialize(const gu::byte_t* const buf, size_t const buflen, size_t offset, bool skip_header) { if (skip_header == false) { gu_trace(offset = Message::unserialize(buf, buflen, offset)); } gu_trace(offset = gu::unserialize8(buf, buflen, offset, seq_)); gu_trace(offset = gu::unserialize8(buf, buflen, offset, aru_seq_)); gu_trace(offset = install_view_id_.unserialize(buf, buflen, offset)); node_list_.clear(); gu_trace(offset = node_list_.unserialize(buf, buflen, offset)); return offset; } size_t gcomm::evs::InstallMessage::serial_size() const { return (Message::serial_size() + 2 * sizeof(seqno_t) + ViewId::serial_size() + node_list_.serial_size()); } size_t gcomm::evs::LeaveMessage::serialize(gu::byte_t* const buf, size_t const buflen, size_t offset) const { gu_trace(offset = Message::serialize(buf, buflen, offset)); gu_trace(offset = gu::serialize8(seq_, buf, buflen, offset)); gu_trace(offset = gu::serialize8(aru_seq_, buf, buflen, offset)); return offset; } size_t gcomm::evs::LeaveMessage::unserialize(const gu::byte_t* const buf, size_t const buflen, size_t offset, bool skip_header) { if (skip_header == false) { gu_trace(offset = Message::unserialize(buf, buflen, offset)); } gu_trace(offset = gu::unserialize8(buf, buflen, offset, seq_)); gu_trace(offset = gu::unserialize8(buf, buflen, offset, aru_seq_)); return offset; } size_t gcomm::evs::LeaveMessage::serial_size() const { return (Message::serial_size() + 2 * sizeof(seqno_t)); } percona-xtradb-cluster-galera/gcomm/src/evs_message2.hpp0000644000000000000000000005056412247075736023651 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy * * $Id$ */ #ifndef EVS_MESSAGE2_HPP #define EVS_MESSAGE2_HPP #include "gcomm/order.hpp" #include "gcomm/view.hpp" #include "gcomm/map.hpp" #include "evs_seqno.hpp" #include "gu_datetime.hpp" #include "gu_convert.hpp" namespace gcomm { namespace evs { class MessageNode; std::ostream& operator<<(std::ostream&, const MessageNode&); class MessageNodeList; class Message; std::ostream& operator<<(std::ostream&, const Message&); class UserMessage; class AggregateMessage; std::ostream& operator<<(std::ostream&, const AggregateMessage&); class DelegateMessage; class GapMessage; class JoinMessage; class LeaveMessage; class InstallMessage; class SelectNodesOp; class RangeLuCmp; class RangeHsCmp; } } class gcomm::evs::MessageNode { public: MessageNode(const bool operational = false, const bool suspected = false, const seqno_t leave_seq = -1, const ViewId& view_id = ViewId(V_REG), const seqno_t safe_seq = -1, const Range im_range = Range()) : operational_(operational), suspected_ (suspected ), leave_seq_ (leave_seq ), view_id_ (view_id ), safe_seq_ (safe_seq ), im_range_ (im_range ) { } MessageNode(const MessageNode& mn) : operational_ (mn.operational_), suspected_ (mn.suspected_ ), leave_seq_ (mn.leave_seq_ ), view_id_ (mn.view_id_ ), safe_seq_ (mn.safe_seq_ ), im_range_ (mn.im_range_ ) { } bool operational() const { return operational_ ; } bool suspected() const { return suspected_ ; } bool leaving() const { return (leave_seq_ != -1) ; } seqno_t leave_seq() const { return leave_seq_ ; } const ViewId& view_id() const { return view_id_ ; } seqno_t safe_seq() const { return safe_seq_ ; } Range im_range() const { return im_range_ ; } bool operator==(const MessageNode& cmp) const { return (operational_ == cmp.operational_ && suspected_ == cmp.suspected_ && leave_seq_ == cmp.leave_seq_ && view_id_ == cmp.view_id_ && safe_seq_ == cmp.safe_seq_ && im_range_ == cmp.im_range_); } size_t serialize(gu::byte_t* buf, size_t buflen, size_t offset) const; size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset); static size_t serial_size(); private: enum { F_OPERATIONAL = 1 << 0, F_SUSPECTED = 1 << 1 }; bool operational_; // Is operational bool suspected_; seqno_t leave_seq_; ViewId view_id_; // Current view as seen by source of this message seqno_t safe_seq_; // Safe seq as seen... Range im_range_; // Input map range as seen... }; class gcomm::evs::MessageNodeList : public gcomm::Map { }; /*! * EVS message class */ class gcomm::evs::Message { public: enum Type { T_NONE = 0, T_USER = 1, /*!< User generated message */ T_DELEGATE = 2, /*!< Delegate message */ T_GAP = 3, /*!< Gap message */ T_JOIN = 4, /*!< Join message */ T_INSTALL = 5, /*!< Install message */ T_LEAVE = 6 /*!< Leave message */ }; static const uint8_t F_MSG_MORE = 0x1; /*!< Sender has more messages to send */ static const uint8_t F_RETRANS = 0x2; /*!< Message is resent upon request */ /*! * @brief Message source has been set explicitly via set_source() */ static const uint8_t F_SOURCE = 0x4; static const uint8_t F_AGGREGATE= 0x8; /*!< Message contains aggregated payload */ static const uint8_t F_COMMIT = 0x10; static const uint8_t F_BC = 0x20;/*!< Message was sent in backward compatibility mode */ /*! * Get version of the message * * @return Version number */ uint8_t version() const { return version_; } /*! * Get type of the message * * @return Message type */ Type type() const { return type_; } /*! * Check wheter message is of membership type * * @return True if message is of membership type, otherwise false */ bool is_membership() const { return (type_ == T_JOIN || type_ == T_INSTALL || type_ == T_LEAVE); } /*! * Get user type of the message. This is applicable only for * messages of type T_USER. * * @return User type of the message. */ uint8_t user_type() const { return user_type_; } /*! * Get message order type. * * @return Order type of the message. */ Order order() const { return order_; } /*! * Get sequence number associated to the message. * * @return Const reference to sequence number associated to the message. */ seqno_t seq() const { return seq_; } /*! * Get sequence numer range associated to the message. * * @return Sequence number range associated to the message. */ seqno_t seq_range() const { return seq_range_; } /*! * Get all-received-upto sequence number associated the the message. * * @return All-received-upto sequence number associated to the message. */ seqno_t aru_seq() const { return aru_seq_; } /*! * Get message flags. * * @return Message flags. */ uint8_t flags() const { return flags_; } /*! * Set message source * * @param uuid Source node uuid */ void set_source(const UUID& uuid) { source_ = uuid; flags_ |= F_SOURCE; } /*! * Get message source UUID. * * @return Message source UUID. */ const UUID& source() const { return source_; } /*! * Get message source view id, view where the message was originated * from. * * @return Message source view id. */ const gcomm::ViewId& source_view_id() const { return source_view_id_; } const gcomm::ViewId& install_view_id() const { return install_view_id_; } /*! * Get range UUID associated to the message. * * @return Range UUID associated to the message. */ const UUID& range_uuid() const { return range_uuid_; } /*! * Get range associated to the message. * * @return Range associated to the message. */ Range range() const { return range_; } /*! * Get fifo sequence number associated to the message. This is * applicable only for messages of membership type. * * @return Fifo sequence number associated to the message. */ int64_t fifo_seq() const { return fifo_seq_; } /*! * Get message node list. * * @return Const reference to message node list. */ const MessageNodeList& node_list() const { return node_list_; } /*! * Get timestamp associated to the message. */ gu::datetime::Date tstamp() const { return tstamp_; } size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset); bool operator==(const Message& cmp) const; /*! * Copy constructor. */ Message(const Message& msg) : version_ (msg.version_), type_ (msg.type_), user_type_ (msg.user_type_), order_ (msg.order_), seq_ (msg.seq_), seq_range_ (msg.seq_range_), aru_seq_ (msg.aru_seq_), fifo_seq_ (msg.fifo_seq_), flags_ (msg.flags_), source_ (msg.source_), source_view_id_ (msg.source_view_id_), install_view_id_ (msg.install_view_id_), range_uuid_ (msg.range_uuid_), range_ (msg.range_), tstamp_ (msg.tstamp_), node_list_ (msg.node_list_) { } Message& operator=(const Message& msg) { version_ = msg.version_; type_ = msg.type_; user_type_ = msg.user_type_; order_ = msg.order_; seq_ = msg.seq_; seq_range_ = msg.seq_range_; aru_seq_ = msg.aru_seq_; fifo_seq_ = msg.fifo_seq_; flags_ = msg.flags_; source_ = msg.source_; source_view_id_ = msg.source_view_id_; install_view_id_ = msg.install_view_id_; range_uuid_ = msg.range_uuid_; range_ = msg.range_; tstamp_ = msg.tstamp_; node_list_ = msg.node_list_; return *this; } virtual ~Message() { } /*! Default constructor */ Message(const uint8_t version = 0, const Type type = T_NONE, const UUID& source = UUID::nil(), const ViewId& source_view_id = ViewId(), const ViewId& install_view_id = ViewId(), const uint8_t user_type = 0xff, const Order order = O_DROP, const int64_t fifo_seq = -1, const seqno_t seq = -1, const seqno_t seq_range = -1, const seqno_t aru_seq = -1, const uint8_t flags = 0, const UUID& range_uuid = UUID(), const Range range = Range(), const MessageNodeList& node_list = MessageNodeList()) : version_ (version), type_ (type), user_type_ (user_type), order_ (order), seq_ (seq), seq_range_ (seq_range), aru_seq_ (aru_seq), fifo_seq_ (fifo_seq), flags_ (flags), source_ (source), source_view_id_ (source_view_id), install_view_id_ (install_view_id), range_uuid_ (range_uuid), range_ (range), tstamp_ (gu::datetime::Date::now()), node_list_ (node_list) { } protected: size_t serialize(gu::byte_t* buf, size_t buflen, size_t offset) const; size_t serial_size() const; uint8_t version_; Type type_; uint8_t user_type_; Order order_; seqno_t seq_; seqno_t seq_range_; seqno_t aru_seq_; int64_t fifo_seq_; uint8_t flags_; UUID source_; ViewId source_view_id_; ViewId install_view_id_; UUID range_uuid_; Range range_; gu::datetime::Date tstamp_; MessageNodeList node_list_; }; /*! * User message class. */ class gcomm::evs::UserMessage : public Message { public: UserMessage(const int version = -1, const UUID& source = UUID::nil(), const ViewId& source_view_id = ViewId(), const seqno_t seq = -1, const seqno_t aru_seq = -1, const seqno_t seq_range = 0, const Order order = O_SAFE, const int64_t fifo_seq = -1, const uint8_t user_type = 0xff, const uint8_t flags = 0) : Message(version, Message::T_USER, source, source_view_id, ViewId(), user_type, order, fifo_seq, seq, seq_range, aru_seq, flags, UUID(), Range()) { } void set_aru_seq(const seqno_t as) { aru_seq_ = as; } size_t serialize(gu::byte_t* buf, size_t buflen, size_t offset) const; size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset, bool skip_header = false); size_t serial_size() const; }; class gcomm::evs::AggregateMessage { public: AggregateMessage(const int flags = 0, const size_t len = 0, const uint8_t user_type = 0xff) : flags_ (gu::convert(flags, uint8_t(0))), user_type_(user_type), len_ (gu::convert(len, uint16_t(0))) { } int flags() const { return flags_; } size_t len() const { return len_; } uint8_t user_type() const { return user_type_; } size_t serialize(gu::byte_t* buf, size_t buflen, size_t offset) const; size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset); size_t serial_size() const; bool operator==(const AggregateMessage& cmp) const { return (flags_ == cmp.flags_ && len_ == cmp.len_ && user_type_ == cmp.user_type_); } private: uint8_t flags_; uint8_t user_type_; uint16_t len_; }; inline std::ostream& gcomm::evs::operator<<(std::ostream& os, const AggregateMessage& am) { return (os << "{flags=" << am.flags() << ",len=" << am.len() << "}"); } class gcomm::evs::DelegateMessage : public Message { public: DelegateMessage(const int version = -1, const UUID& source = UUID::nil(), const ViewId& source_view_id = ViewId(), const int64_t fifo_seq = -1) : Message(version, T_DELEGATE, source, source_view_id, ViewId(), 0xff, O_UNRELIABLE, fifo_seq) { } size_t serialize(gu::byte_t* buf, size_t buflen, size_t offset) const; size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset, bool skip_header = false); size_t serial_size() const; }; class gcomm::evs::GapMessage : public Message { public: GapMessage(const int version = -1, const UUID& source = UUID::nil(), const ViewId& source_view_id = ViewId(), const seqno_t seq = -1, const seqno_t aru_seq = -1, const int64_t fifo_seq = -1, const UUID& range_uuid = UUID::nil(), const Range range = Range(), const uint8_t flags = 0) : Message(version, T_GAP, source, source_view_id, ViewId(), 0xff, O_UNRELIABLE, fifo_seq, seq, -1, aru_seq, flags, range_uuid, range) { } size_t serialize(gu::byte_t* buf, size_t buflen, size_t offset) const; size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset, bool skip_header = false); size_t serial_size() const; }; class gcomm::evs::JoinMessage : public Message { public: JoinMessage(const int version = -1, const UUID& source = UUID::nil(), const ViewId& source_view_id = ViewId(), const seqno_t seq = -1, const seqno_t aru_seq = -1, const int64_t fifo_seq = -1, const MessageNodeList& node_list = MessageNodeList()) : Message(version, Message::T_JOIN, source, source_view_id, ViewId(), 0xff, O_UNRELIABLE, fifo_seq, seq, -1, aru_seq, 0, UUID(), Range(), node_list) { } size_t serialize(gu::byte_t* buf, size_t buflen, size_t offset) const; size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset, bool skip_header = false); size_t serial_size() const; }; class gcomm::evs::InstallMessage : public Message { public: InstallMessage(const int version = -1, const UUID& source = UUID::nil(), const ViewId& source_view_id = ViewId(), const ViewId& install_view_id = ViewId(), const seqno_t seq = -1, const seqno_t aru_seq = -1, const int64_t fifo_seq = -1, const MessageNodeList& node_list = MessageNodeList()) : Message(version, Message::T_INSTALL, source, source_view_id, install_view_id, 0xff, O_UNRELIABLE, fifo_seq, seq, -1, aru_seq, 0, UUID(), Range(), node_list) { } size_t serialize(gu::byte_t* buf, size_t buflen, size_t offset) const; size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset, bool skip_header = false); size_t serial_size() const; }; class gcomm::evs::LeaveMessage : public Message { public: LeaveMessage(const int version = -1, const UUID& source = UUID::nil(), const ViewId& source_view_id = ViewId(), const seqno_t seq = -1, const seqno_t aru_seq = -1, const int64_t fifo_seq = -1, const uint8_t flags = 0) : Message(version, T_LEAVE, source, source_view_id, ViewId(), 0xff, O_UNRELIABLE, fifo_seq, seq, -1, aru_seq, flags) { } size_t serialize(gu::byte_t* buf, size_t buflen, size_t offset) const; size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset, bool skip_header = false); size_t serial_size() const; }; class gcomm::evs::SelectNodesOp { public: SelectNodesOp(MessageNodeList& nl, const gcomm::ViewId& view_id, const bool operational, const bool leaving) : nl_ (nl), view_id_ (view_id), operational_ (operational), leaving_ (leaving) { } void operator()(const MessageNodeList::value_type& vt) const { const MessageNode& node(MessageNodeList::value(vt)); if ((view_id_ == ViewId() || node.view_id() == view_id_ ) && ((operational_ == true && leaving_ == true ) || (node.operational() == operational_ && node.leaving() == leaving_ ) ) ) { nl_.insert_unique(vt); } } private: MessageNodeList& nl_; ViewId const view_id_; bool const operational_; bool const leaving_; }; class gcomm::evs::RangeLuCmp { public: bool operator()(const MessageNodeList::value_type& a, const MessageNodeList::value_type& b) const { gcomm_assert(MessageNodeList::value(a).view_id() == MessageNodeList::value(b).view_id()); return (MessageNodeList::value(a).im_range().lu() < MessageNodeList::value(b).im_range().lu()); } }; class gcomm::evs::RangeHsCmp { public: bool operator()(const MessageNodeList::value_type& a, const MessageNodeList::value_type& b) const { gcomm_assert(MessageNodeList::value(a).view_id() == MessageNodeList::value(b).view_id()); return (MessageNodeList::value(a).im_range().hs() < MessageNodeList::value(b).im_range().hs()); } }; #endif // EVS_MESSAGE2_HPP percona-xtradb-cluster-galera/gcomm/src/evs_node.cpp0000644000000000000000000000405012247075736023050 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #include "evs_node.hpp" #include "evs_message2.hpp" #include std::ostream& gcomm::evs::operator<<(std::ostream& os, const gcomm::evs::Node& n) { os << "evs::node{"; os << "operational=" << n.operational() << ","; os << "suspected=" << n.suspected() << ","; os << "installed=" << n.installed() << ","; os << "fifo_seq=" << n.fifo_seq() << ","; if (n.join_message() != 0) { os << "join_message=\n" << *n.join_message() << ",\n"; } if (n.leave_message() != 0) { os << "leave_message=\n" << *n.leave_message() << ",\n"; } os << "}"; return os; } gcomm::evs::Node::Node(const Node& n) : index_ (n.index_), operational_ (n.operational_), suspected_ (n.suspected_), inactive_ (n.inactive_), committed_ (n.committed_), installed_ (n.installed_), join_message_ (n.join_message_ != 0 ? new JoinMessage(*n.join_message_) : 0), leave_message_ (n.leave_message_ != 0 ? new LeaveMessage(*n.leave_message_) : 0), suspect_timeout_ (n.suspect_timeout_), inactive_timeout_(n.inactive_timeout_), tstamp_ (n.tstamp_), fifo_seq_ (n.fifo_seq_) { } gcomm::evs::Node::~Node() { delete join_message_; delete leave_message_; } void gcomm::evs::Node::set_join_message(const JoinMessage* jm) { if (join_message_ != 0) { delete join_message_; } if (jm != 0) { join_message_ = new JoinMessage(*jm); } else { join_message_ = 0; } } void gcomm::evs::Node::set_leave_message(const LeaveMessage* lm) { if (leave_message_ != 0) { delete leave_message_; } if (lm != 0) { leave_message_ = new LeaveMessage(*lm); } else { leave_message_ = 0; } } bool gcomm::evs::Node::is_suspected() const { return suspected_; } bool gcomm::evs::Node::is_inactive() const { return inactive_; } percona-xtradb-cluster-galera/gcomm/src/evs_node.hpp0000644000000000000000000001113312247075736023055 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #ifndef EVS_NODE_HPP #define EVS_NODE_HPP #include "evs_message2.hpp" #include "gcomm/map.hpp" #include "gcomm/uuid.hpp" #include "gu_datetime.hpp" #include "gu_logger.hpp" #include #include namespace gcomm { namespace evs { class Node; class NodeMap; std::ostream& operator<<(std::ostream&, const Node&); class InspectNode; class OperationalSelect; } } class gcomm::evs::Node { public: Node(const gu::datetime::Period& inactive_timeout, const gu::datetime::Period& suspect_timeout) : index_ (std::numeric_limits::max()), operational_ (true), suspected_ (false), inactive_ (false), committed_ (false), installed_ (false), join_message_ (0), leave_message_ (0), suspect_timeout_ (suspect_timeout), inactive_timeout_(inactive_timeout), tstamp_ (gu::datetime::Date::now()), fifo_seq_ (-1) {} Node(const Node& n); ~Node(); void set_index(const size_t idx) { index_ = idx; } size_t index() const { return index_; } void set_operational(const bool op) { gcomm_assert(op == false); operational_ = op; } bool operational() const { return operational_; } void set_suspected(const bool s) { suspected_ = s; } bool suspected() const { return suspected_; } void set_committed(const bool comm) { committed_ = comm; } bool committed() const { return committed_; } void set_installed(const bool inst) { installed_ = inst; } bool installed() const { return installed_; } void set_join_message(const JoinMessage* msg); const JoinMessage* join_message() const { return join_message_; } void set_leave_message(const LeaveMessage* msg); const LeaveMessage* leave_message() const { return leave_message_; } void set_tstamp(const gu::datetime::Date& t) { tstamp_ = t; } const gu::datetime::Date& tstamp() const { return tstamp_; } void set_fifo_seq(const int64_t seq) { fifo_seq_ = seq; } int64_t fifo_seq() const { return fifo_seq_; } bool is_inactive() const; bool is_suspected() const; void set_suspect_timeout(const gu::datetime::Period& p) { suspect_timeout_ = p; } void set_inactive_timeout(const gu::datetime::Period& p) { inactive_timeout_ = p; } private: void operator=(const Node&); friend class InspectNode; // Index for input map size_t index_; // True if instance is considered to be operational (has produced messages) bool operational_; bool suspected_; bool inactive_; // True if it is known that the instance has committed to install message bool committed_; // True if it is known that the instance has installed current view bool installed_; // Last received JOIN message JoinMessage* join_message_; // Last activity timestamp LeaveMessage* leave_message_; gu::datetime::Period suspect_timeout_; // gu::datetime::Period inactive_timeout_; // gu::datetime::Date tstamp_; // int64_t fifo_seq_; }; class gcomm::evs::NodeMap : public Map { }; class gcomm::evs::OperationalSelect { public: OperationalSelect(NodeMap& nm_) : nm(nm_) { } void operator()(const NodeMap::value_type& vt) const { if (NodeMap::value(vt).operational() == true) { nm.insert_unique(vt); } } private: NodeMap& nm; }; class gcomm::evs::InspectNode { public: void operator()(std::pair& p) const { Node& node(p.second); gu::datetime::Date now(gu::datetime::Date::now()); if (node.tstamp() + node.suspect_timeout_ < now) { if (node.suspected_ == false) { log_debug << "declaring node with index " << node.index_ << " suspected, timeout " << node.suspect_timeout_; } node.suspected_ = true; } else { node.suspected_ = false; } if (node.tstamp() + node.inactive_timeout_ < now) { if (node.inactive_ == false) { log_debug << "declaring node with index " << node.index_ << " inactive "; } node.inactive_ = true; } else { node.inactive_ = false; } } }; #endif // EVS_NODE_HPP percona-xtradb-cluster-galera/gcomm/src/evs_proto.cpp0000644000000000000000000036151112247075736023276 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #ifdef PROFILE_EVS_PROTO #define GCOMM_PROFILE 1 #else #undef GCOMM_PROFILE #endif // PROFILE_EVS_PROTO #include "evs_proto.hpp" #include "evs_message2.hpp" #include "evs_input_map2.hpp" #include "gcomm/transport.hpp" #include "gcomm/conf.hpp" #include "gcomm/util.hpp" #include "defaults.hpp" #include #include #include using namespace std::rel_ops; // Convenience macros for debug and info logging #define evs_log_debug(__mask__) \ if ((debug_mask_ & (__mask__)) == 0) { } \ else log_debug << self_string() << ": " #define evs_log_info(__mask__) \ if ((info_mask_ & (__mask__)) == 0) { } \ else log_info << self_string() << ": " // const int gcomm::evs::Proto::max_version_(GCOMM_EVS_MAX_VERSION); gcomm::evs::Proto::Proto(gu::Config& conf, const UUID& my_uuid, const gu::URI& uri, const size_t mtu) : Protolay(conf), timers_(), version_(check_range(Conf::EvsVersion, param(conf, uri, Conf::EvsVersion, "0"), 0, max_version_ + 1)), debug_mask_(param(conf, uri, Conf::EvsDebugLogMask, "0x1", std::hex)), info_mask_(param(conf, uri, Conf::EvsInfoLogMask, "0x0", std::hex)), last_stats_report_(gu::datetime::Date::now()), collect_stats_(true), hs_agreed_("0.0,0.0005,0.001,0.002,0.005,0.01,0.02,0.05,0.1,0.5,1.,5.,10.,30."), hs_safe_("0.0,0.0005,0.001,0.002,0.005,0.01,0.02,0.05,0.1,0.5,1.,5.,10.,30."), hs_local_causal_("0.0,0.0005,0.001,0.002,0.005,0.01,0.02,0.05,0.1,0.5,1.,5.,10.,30."), send_queue_s_(0), n_send_queue_s_(0), sent_msgs_(7, 0), retrans_msgs_(0), recovered_msgs_(0), recvd_msgs_(7, 0), delivered_msgs_(O_LOCAL_CAUSAL + 1), send_user_prof_ ("send_user"), send_gap_prof_ ("send_gap"), send_join_prof_ ("send_join"), send_install_prof_ ("send_install"), send_leave_prof_ ("send_leave"), consistent_prof_ ("consistent"), consensus_prof_ ("consensus"), shift_to_prof_ ("shift_to"), input_map_prof_ ("input_map"), delivery_prof_ ("delivery"), delivering_(false), my_uuid_(my_uuid), known_(), self_i_(), view_forget_timeout_( check_range(Conf::EvsViewForgetTimeout, param( conf, uri, Conf::EvsViewForgetTimeout, Defaults::EvsViewForgetTimeout), gu::from_string( Defaults::EvsViewForgetTimeoutMin), gu::datetime::Period::max())), inactive_timeout_( check_range(Conf::EvsInactiveTimeout, param( conf, uri, Conf::EvsInactiveTimeout, Defaults::EvsInactiveTimeout), gu::from_string( Defaults::EvsInactiveTimeoutMin), gu::datetime::Period::max())), suspect_timeout_( check_range(Conf::EvsSuspectTimeout, param( conf, uri, Conf::EvsSuspectTimeout, Defaults::EvsSuspectTimeout), gu::from_string( Defaults::EvsSuspectTimeoutMin), gu::datetime::Period::max())), inactive_check_period_( check_range(Conf::EvsInactiveCheckPeriod, param( conf, uri, Conf::EvsInactiveCheckPeriod, Defaults::EvsInactiveCheckPeriod), gu::datetime::Period::min(), suspect_timeout_/2 + 1)), retrans_period_( check_range(Conf::EvsKeepalivePeriod, param( conf, uri, Conf::EvsKeepalivePeriod, Defaults::EvsRetransPeriod), gu::from_string( Defaults::EvsRetransPeriodMin), suspect_timeout_/3 + 1)), install_timeout_( check_range(Conf::EvsInstallTimeout, param( conf, uri, Conf::EvsInstallTimeout, gu::to_string(inactive_timeout_)), retrans_period_*2, inactive_timeout_ + 1)), join_retrans_period_( check_range(Conf::EvsJoinRetransPeriod, param( conf, uri, Conf::EvsJoinRetransPeriod, Defaults::EvsJoinRetransPeriod), gu::from_string( Defaults::EvsRetransPeriodMin), gu::datetime::Period::max())), stats_report_period_( check_range(Conf::EvsStatsReportPeriod, param( conf, uri, Conf::EvsStatsReportPeriod, Defaults::EvsStatsReportPeriod), gu::from_string( Defaults::EvsStatsReportPeriodMin), gu::datetime::Period::max())), causal_keepalive_period_(retrans_period_), last_inactive_check_ (gu::datetime::Date::now()), last_causal_keepalive_ (gu::datetime::Date::now()), current_view_(ViewId(V_TRANS, my_uuid, 0)), previous_view_(), previous_views_(), input_map_(new InputMap()), causal_queue_(), consensus_(my_uuid_, known_, *input_map_, current_view_), install_message_(0), attempt_seq_(1), max_install_timeouts_( check_range(Conf::EvsMaxInstallTimeouts, param(conf, uri, Conf::EvsMaxInstallTimeouts, Defaults::EvsMaxInstallTimeouts), 0, std::numeric_limits::max())), install_timeout_count_(0), fifo_seq_(-1), last_sent_(-1), send_window_( check_range(Conf::EvsSendWindow, param(conf, uri, Conf::EvsSendWindow, Defaults::EvsSendWindow), gu::from_string(Defaults::EvsSendWindowMin), std::numeric_limits::max())), user_send_window_( check_range(Conf::EvsUserSendWindow, param(conf, uri, Conf::EvsUserSendWindow, Defaults::EvsUserSendWindow), gu::from_string(Defaults::EvsUserSendWindowMin), send_window_ + 1)), output_(), send_buf_(), max_output_size_(128), mtu_(mtu), use_aggregate_(param(conf, uri, Conf::EvsUseAggregate, "true")), self_loopback_(false), state_(S_CLOSED), shift_to_rfcnt_(0), pending_leave_(false) { log_info << "EVS version " << version_; conf.set(Conf::EvsVersion, gu::to_string(version_)); conf.set(Conf::EvsViewForgetTimeout, gu::to_string(view_forget_timeout_)); conf.set(Conf::EvsSuspectTimeout, gu::to_string(suspect_timeout_)); conf.set(Conf::EvsInactiveTimeout, gu::to_string(inactive_timeout_)); conf.set(Conf::EvsKeepalivePeriod, gu::to_string(retrans_period_)); conf.set(Conf::EvsInactiveCheckPeriod, gu::to_string(inactive_check_period_)); conf.set(Conf::EvsJoinRetransPeriod, gu::to_string(join_retrans_period_)); conf.set(Conf::EvsInstallTimeout, gu::to_string(install_timeout_)); conf.set(Conf::EvsStatsReportPeriod, gu::to_string(stats_report_period_)); conf.set(Conf::EvsCausalKeepalivePeriod, gu::to_string(causal_keepalive_period_)); conf.set(Conf::EvsSendWindow, gu::to_string(send_window_)); conf.set(Conf::EvsUserSendWindow, gu::to_string(user_send_window_)); conf.set(Conf::EvsUseAggregate, gu::to_string(use_aggregate_)); conf.set(Conf::EvsDebugLogMask, gu::to_string(debug_mask_, std::hex)); conf.set(Conf::EvsInfoLogMask, gu::to_string(info_mask_, std::hex)); conf.set(Conf::EvsMaxInstallTimeouts, gu::to_string(max_install_timeouts_)); // known_.insert_unique( std::make_pair(my_uuid_, Node(inactive_timeout_, suspect_timeout_))); self_i_ = known_.begin(); assert(NodeMap::value(self_i_).operational() == true); NodeMap::value(self_i_).set_index(0); input_map_->reset(1); current_view_.add_member(my_uuid_, ""); if (mtu_ != std::numeric_limits::max()) { send_buf_.reserve(mtu_); } } gcomm::evs::Proto::~Proto() { output_.clear(); delete install_message_; delete input_map_; } bool gcomm::evs::Proto::set_param(const std::string& key, const std::string& val) { if (key == gcomm::Conf::EvsSendWindow) { send_window_ = check_range(Conf::EvsSendWindow, gu::from_string(val), user_send_window_, std::numeric_limits::max()); conf_.set(Conf::EvsSendWindow, gu::to_string(send_window_)); return true; } else if (key == gcomm::Conf::EvsUserSendWindow) { user_send_window_ = check_range( Conf::EvsUserSendWindow, gu::from_string(val), gu::from_string(Defaults::EvsUserSendWindowMin), send_window_ + 1); conf_.set(Conf::EvsUserSendWindow, gu::to_string(user_send_window_)); return true; } else if (key == gcomm::Conf::EvsMaxInstallTimeouts) { max_install_timeouts_ = check_range( Conf::EvsMaxInstallTimeouts, gu::from_string(val), 0, std::numeric_limits::max()); conf_.set(Conf::EvsMaxInstallTimeouts, gu::to_string(max_install_timeouts_)); return true; } else if (key == Conf::EvsStatsReportPeriod) { stats_report_period_ = check_range( Conf::EvsStatsReportPeriod, gu::from_string(val), gu::from_string(Defaults::EvsStatsReportPeriodMin), gu::datetime::Period::max()); conf_.set(Conf::EvsStatsReportPeriod, gu::to_string(stats_report_period_)); reset_timers(); return true; } else if (key == Conf::EvsInfoLogMask) { info_mask_ = gu::from_string(val, std::hex); conf_.set(Conf::EvsInfoLogMask, gu::to_string(info_mask_, std::hex)); return true; } else if (key == Conf::EvsDebugLogMask) { debug_mask_ = gu::from_string(val, std::hex); conf_.set(Conf::EvsDebugLogMask, gu::to_string(debug_mask_, std::hex)); return true; } else if (key == Conf::EvsSuspectTimeout) { suspect_timeout_ = check_range( Conf::EvsSuspectTimeout, gu::from_string(val), gu::from_string(Defaults::EvsSuspectTimeoutMin), gu::datetime::Period::max()); conf_.set(Conf::EvsSuspectTimeout, gu::to_string(suspect_timeout_)); for (NodeMap::iterator i(known_.begin()); i != known_.end(); ++i) { NodeMap::value(i).set_suspect_timeout(suspect_timeout_); } reset_timers(); return true; } else if (key == Conf::EvsInactiveTimeout) { inactive_timeout_ = check_range( Conf::EvsInactiveTimeout, gu::from_string(val), gu::from_string(Defaults::EvsInactiveTimeoutMin), gu::datetime::Period::max()); conf_.set(Conf::EvsInactiveTimeout, gu::to_string(inactive_timeout_)); for (NodeMap::iterator i(known_.begin()); i != known_.end(); ++i) { NodeMap::value(i).set_inactive_timeout(inactive_timeout_); } reset_timers(); return true; } else if (key == Conf::EvsKeepalivePeriod) { retrans_period_ = check_range( Conf::EvsKeepalivePeriod, gu::from_string(val), gu::from_string(Defaults::EvsRetransPeriodMin), gu::datetime::Period::max()); conf_.set(Conf::EvsKeepalivePeriod, gu::to_string(retrans_period_)); reset_timers(); return true; } else if (key == Conf::EvsCausalKeepalivePeriod) { causal_keepalive_period_ = check_range( Conf::EvsCausalKeepalivePeriod, gu::from_string(val), gu::datetime::Period(0), gu::datetime::Period::max()); conf_.set(Conf::EvsCausalKeepalivePeriod, gu::to_string(causal_keepalive_period_)); // no timer reset here, causal keepalives don't rely on timer return true; } else if (key == Conf::EvsJoinRetransPeriod) { join_retrans_period_ = check_range( Conf::EvsJoinRetransPeriod, gu::from_string(val), gu::from_string(Defaults::EvsRetransPeriodMin), gu::datetime::Period::max()); conf_.set(Conf::EvsJoinRetransPeriod, gu::to_string(join_retrans_period_)); reset_timers(); return true; } else if (key == Conf::EvsInstallTimeout) { install_timeout_ = check_range( Conf::EvsInstallTimeout, gu::from_string(val), retrans_period_*2, inactive_timeout_ + 1); conf_.set(Conf::EvsInstallTimeout, gu::to_string(install_timeout_)); reset_timers(); return true; } else if (key == Conf::EvsUseAggregate) { use_aggregate_ = gu::from_string(val); conf_.set(Conf::EvsUseAggregate, gu::to_string(use_aggregate_)); return true; } else if (key == Conf::EvsViewForgetTimeout || key == Conf::EvsInactiveCheckPeriod) { gu_throw_error(EPERM) << "can't change value for '" << key << "' during runtime"; } return false; } std::ostream& gcomm::evs::operator<<(std::ostream& os, const Proto& p) { os << "evs::proto(" << p.self_string() << ", " << p.to_string(p.state()) << ") {\n"; os << "current_view=" << p.current_view_ << ",\n"; os << "input_map=" << *p.input_map_ << ",\n"; os << "fifo_seq=" << p.fifo_seq_ << ",\n"; os << "last_sent=" << p.last_sent_ << ",\n"; os << "known={\n" << p.known_ << " } \n"; if (p.install_message_ != 0) os << "install msg=" << *p.install_message_ << "\n"; os << " }"; return os; } std::string gcomm::evs::Proto::stats() const { std::ostringstream os; os << "\n\tnodes " << current_view_.members().size(); os << "\n\tagreed deliv hist {" << hs_agreed_ << "} "; os << "\n\tsafe deliv hist {" << hs_safe_ << "} "; os << "\n\tcaus deliv hist {" << hs_local_causal_ << "} "; os << "\n\toutq avg " << double(send_queue_s_)/double(n_send_queue_s_); os << "\n\tsent {"; std::copy(sent_msgs_.begin(), sent_msgs_.end(), std::ostream_iterator(os, ",")); os << "}\n\tsent per sec {"; const double norm(double(gu::datetime::Date::now().get_utc() - last_stats_report_.get_utc())/gu::datetime::Sec); std::vector result(7, norm); std::transform(sent_msgs_.begin(), sent_msgs_.end(), result.begin(), result.begin(), std::divides()); std::copy(result.begin(), result.end(), std::ostream_iterator(os, ",")); os << "}\n\trecvd { "; std::copy(recvd_msgs_.begin(), recvd_msgs_.end(), std::ostream_iterator(os, ",")); os << "}\n\trecvd per sec {"; std::fill(result.begin(), result.end(), norm); std::transform(recvd_msgs_.begin(), recvd_msgs_.end(), result.begin(), result.begin(), std::divides()); std::copy(result.begin(), result.end(), std::ostream_iterator(os, ",")); os << "}\n\tretransmitted " << retrans_msgs_ << " "; os << "\n\trecovered " << recovered_msgs_; os << "\n\tdelivered {"; std::copy(delivered_msgs_.begin(), delivered_msgs_.end(), std::ostream_iterator(os, ", ")); os << "}\n\teff(delivered/sent) " << double(accumulate(delivered_msgs_.begin() + 1, delivered_msgs_.begin() + O_SAFE + 1, 0)) /double(accumulate(sent_msgs_.begin(), sent_msgs_.end(), 0)); return os.str(); } void gcomm::evs::Proto::reset_stats() { hs_agreed_.clear(); hs_safe_.clear(); hs_local_causal_.clear(); send_queue_s_ = 0; n_send_queue_s_ = 0; fill(sent_msgs_.begin(), sent_msgs_.end(), 0LL); fill(recvd_msgs_.begin(), recvd_msgs_.end(), 0LL); retrans_msgs_ = 0LL; recovered_msgs_ = 0LL; fill(delivered_msgs_.begin(), delivered_msgs_.end(), 0LL); last_stats_report_ = gu::datetime::Date::now(); } bool gcomm::evs::Proto::is_msg_from_previous_view(const Message& msg) { for (std::list >::const_iterator i = previous_views_.begin(); i != previous_views_.end(); ++i) { if (msg.source_view_id() == i->first) { evs_log_debug(D_FOREIGN_MSGS) << " message " << msg << " from previous view " << i->first; return true; } } // If node is in current view, check message source view seq, if it is // smaller than current view seq then the message is also from some // previous (but unknown to us) view NodeList::const_iterator ni(current_view_.members().find(msg.source())); if (ni != current_view_.members().end()) { if (msg.source_view_id().seq() < current_view_.id().seq()) { log_warn << "stale message from unknown origin " << msg; return true; } } return false; } void gcomm::evs::Proto::handle_inactivity_timer() { gu_trace(check_inactive()); gu_trace(cleanup_views()); } void gcomm::evs::Proto::handle_retrans_timer() { evs_log_debug(D_TIMERS) << "retrans timer"; if (state() == S_GATHER) { gu_trace(send_join(true)); if (install_message_ != 0) { gu_trace(send_gap(UUID::nil(), install_message_->install_view_id(), Range(), true)); } } else if (state() == S_INSTALL) { gcomm_assert(install_message_ != 0); gu_trace(send_gap(UUID::nil(), install_message_->install_view_id(), Range(), true)); gu_trace(send_gap(UUID::nil(), install_message_->install_view_id(), Range())); } else if (state() == S_OPERATIONAL) { const seqno_t prev_last_sent(last_sent_); evs_log_debug(D_TIMERS) << "send user timer, last_sent=" << last_sent_; Datagram dg; gu_trace((void)send_user(dg, 0xff, O_DROP, -1, -1)); if (prev_last_sent == last_sent_) { log_warn << "could not send keepalive"; } } else if (state() == S_LEAVING) { evs_log_debug(D_TIMERS) << "send leave timer"; profile_enter(send_leave_prof_); send_leave(false); profile_leave(send_leave_prof_); } } void gcomm::evs::Proto::handle_install_timer() { gcomm_assert(state() == S_GATHER || state() == S_INSTALL); log_warn << self_string() << " install timer expired"; bool is_cons(consensus_.is_consensus()); bool is_repr(is_representative(uuid())); evs_log_info(I_STATE) << "before inspection:"; evs_log_info(I_STATE) << "consensus: " << is_cons; evs_log_info(I_STATE) << "repr : " << is_repr; evs_log_info(I_STATE) << "state dump for diagnosis:"; std::cerr << *this << std::endl; if (install_timeout_count_ == max_install_timeouts_ - 1) { // before reaching max_install_timeouts, declare only inconsistent // nodes as inactive for (NodeMap::iterator i = known_.begin(); i != known_.end(); ++i) { const Node& node(NodeMap::value(i)); if (NodeMap::key(i) != uuid() && (node.join_message() == 0 || consensus_.is_consistent(*node.join_message()) == false)) { evs_log_info(I_STATE) << " setting source " << NodeMap::key(i) << " as inactive due to expired install timer"; set_inactive(NodeMap::key(i)); } } } else if (install_timeout_count_ == max_install_timeouts_) { // max install timeouts reached, declare all other nodes // as inactive for (NodeMap::iterator i = known_.begin(); i != known_.end(); ++i) { if (NodeMap::key(i) != uuid()) { evs_log_info(I_STATE) << " setting source " << NodeMap::key(i) << " as inactive due to expired install timer"; set_inactive(NodeMap::key(i)); } } } else if (install_timeout_count_ > max_install_timeouts_) { log_info << "going to give up, state dump for diagnosis:"; std::cerr << *this << std::endl; gu_throw_fatal << self_string() << " failed to form singleton view after exceeding " << "max_install_timeouts " << max_install_timeouts_ << ", giving up"; } if (install_message_ != 0) { for (NodeMap::iterator i = known_.begin(); i != known_.end(); ++i) { if (NodeMap::value(i).committed() == false) { log_info << self_string() << " node " << NodeMap::key(i) << " failed to commit for install message, " << "declaring inactive"; if (NodeMap::key(i) != uuid()) { set_inactive(NodeMap::key(i)); } } } } else { log_info << "no install message received"; } shift_to(S_GATHER, true); is_cons = consensus_.is_consensus(); is_repr = is_representative(uuid()); evs_log_info(I_STATE) << "after inspection:"; evs_log_info(I_STATE) << "consensus: " << is_cons; evs_log_info(I_STATE) << "repr : " << is_repr; if (is_cons == true && is_repr == true) { send_install(); } install_timeout_count_++; } void gcomm::evs::Proto::handle_stats_timer() { if (info_mask_ & I_STATISTICS) { evs_log_info(I_STATISTICS) << "statistics (stderr):"; std::cerr << stats() << std::endl; } reset_stats(); #ifdef GCOMM_PROFILE evs_log_info(I_PROFILING) << "\nprofiles:\n"; evs_log_info(I_PROFILING) << send_user_prof_ << "\n"; evs_log_info(I_PROFILING) << send_gap_prof_ << "\n"; evs_log_info(I_PROFILING) << send_join_prof_ << "\n"; evs_log_info(I_PROFILING) << send_install_prof_ << "\n"; evs_log_info(I_PROFILING) << send_leave_prof_ << "\n"; evs_log_info(I_PROFILING) << consistent_prof_ << "\n"; evs_log_info(I_PROFILING) << consensus_prof_ << "\n"; evs_log_info(I_PROFILING) << shift_to_prof_ << "\n"; evs_log_info(I_PROFILING) << input_map_prof_ << "\n"; evs_log_info(I_PROFILING) << delivery_prof_ << "\n"; #endif // GCOMM_PROFILE } class TimerSelectOp { public: TimerSelectOp(const gcomm::evs::Proto::Timer t_) : t(t_) { } bool operator()(const gcomm::evs::Proto::TimerList::value_type& vt) const { return (gcomm::evs::Proto::TimerList::value(vt) == t); } private: gcomm::evs::Proto::Timer const t; }; gu::datetime::Date gcomm::evs::Proto::next_expiration(const Timer t) const { gcomm_assert(state() != S_CLOSED); gu::datetime::Date now(gu::datetime::Date::now()); switch (t) { case T_INACTIVITY: return (now + inactive_check_period_); case T_RETRANS: switch (state()) { case S_OPERATIONAL: case S_LEAVING: return (now + retrans_period_); case S_GATHER: case S_INSTALL: return (now + join_retrans_period_); default: gu_throw_fatal; } case T_INSTALL: switch (state()) { case S_GATHER: case S_INSTALL: return (now + install_timeout_); default: return gu::datetime::Date::max(); } case T_STATS: return (now + stats_report_period_); } gu_throw_fatal; } void gcomm::evs::Proto::reset_timers() { timers_.clear(); gu_trace((void)timers_.insert( std::make_pair( next_expiration(T_INACTIVITY), T_INACTIVITY))); gu_trace((void)timers_.insert( std::make_pair( next_expiration(T_RETRANS), T_RETRANS))); gu_trace((void)timers_.insert( std::make_pair( next_expiration(T_INSTALL), T_INSTALL))); gu_trace((void)timers_.insert( std::make_pair(next_expiration(T_STATS), T_STATS))); } gu::datetime::Date gcomm::evs::Proto::handle_timers() { gu::datetime::Date now(gu::datetime::Date::now()); while (timers_.empty() == false && TimerList::key(timers_.begin()) <= now) { Timer t(TimerList::value(timers_.begin())); timers_.erase(timers_.begin()); switch (t) { case T_INACTIVITY: handle_inactivity_timer(); break; case T_RETRANS: handle_retrans_timer(); break; case T_INSTALL: handle_install_timer(); break; case T_STATS: handle_stats_timer(); break; } if (state() == S_CLOSED) { return gu::datetime::Date::max(); } // Make sure that timer was not inserted twice TimerList::iterator ii(find_if(timers_.begin(), timers_.end(), TimerSelectOp(t))); if (ii != timers_.end()) { timers_.erase(ii); } gu_trace((void)timers_.insert( std::make_pair(next_expiration(t), t))); } if (timers_.empty() == true) { evs_log_debug(D_TIMERS) << "no timers set"; return gu::datetime::Date::max(); } return TimerList::key(timers_.begin()); } void gcomm::evs::Proto::check_inactive() { const gu::datetime::Date now(gu::datetime::Date::now()); if (last_inactive_check_ + inactive_check_period_*3 < now) { log_warn << "last inactive check more than " << inactive_check_period_*3 << " ago (" << (now - last_inactive_check_) << "), skipping check"; last_inactive_check_ = now; return; } NodeMap::value(self_i_).set_tstamp(gu::datetime::Date::now()); std::for_each(known_.begin(), known_.end(), InspectNode()); bool has_inactive(false); size_t n_suspected(0); for (NodeMap::iterator i = known_.begin(); i != known_.end(); ++i) { const UUID& node_uuid(NodeMap::key(i)); Node& node(NodeMap::value(i)); if (node_uuid != uuid() && (node.is_inactive() == true || node.is_suspected() == true )) { if (node.operational() == true && node.is_inactive() == true) { log_info << self_string() << " detected inactive node: " << node_uuid; } else if (node.is_suspected() == true && node.is_inactive() == false) { log_info << self_string() << " suspecting node: " << node_uuid; } if (node.is_inactive() == true) { set_inactive(node_uuid); } if (node.is_suspected() == true) { ++n_suspected; } has_inactive = true; } } // All other nodes are under suspicion, set all others as inactive. // This will speed up recovery when this node has been isolated from // other group. Note that this should be done only if known size is // greater than 2 in order to avoid immediate split brain. if (known_.size() > 2 && n_suspected + 1 == known_.size()) { for (NodeMap::iterator i = known_.begin(); i != known_.end(); ++i) { if (NodeMap::key(i) != uuid()) { evs_log_info(I_STATE) << " setting source " << NodeMap::key(i) << " inactive (other nodes under suspicion)"; set_inactive(NodeMap::key(i)); } } } if (has_inactive == true && state() == S_OPERATIONAL) { profile_enter(shift_to_prof_); gu_trace(shift_to(S_GATHER, true)); profile_leave(shift_to_prof_); } else if (has_inactive == true && state() == S_LEAVING && n_operational() == 1) { profile_enter(shift_to_prof_); gu_trace(shift_to(S_CLOSED)); profile_leave(shift_to_prof_); } last_inactive_check_ = now; } void gcomm::evs::Proto::set_inactive(const UUID& node_uuid) { NodeMap::iterator i; gcomm_assert(node_uuid != uuid()); gu_trace(i = known_.find_checked(node_uuid)); evs_log_debug(D_STATE) << "setting " << node_uuid << " inactive"; Node& node(NodeMap::value(i)); node.set_tstamp(gu::datetime::Date::zero()); node.set_join_message(0); // node.set_leave_message(0); node.set_operational(false); } void gcomm::evs::Proto::cleanup_unoperational() { NodeMap::iterator i, i_next; for (i = known_.begin(); i != known_.end(); i = i_next) { i_next = i, ++i_next; if (NodeMap::value(i).installed() == false) { evs_log_debug(D_STATE) << "erasing " << NodeMap::key(i); known_.erase(i); } } } void gcomm::evs::Proto::cleanup_views() { gu::datetime::Date now(gu::datetime::Date::now()); std::list >::iterator i(previous_views_.begin()); while (i != previous_views_.end()) { if (i->second + view_forget_timeout_ <= now) { evs_log_debug(D_STATE) << " erasing view: " << i->first; previous_views_.erase(i); } else { break; } i = previous_views_.begin(); } } size_t gcomm::evs::Proto::n_operational() const { NodeMap::const_iterator i; size_t ret = 0; for (i = known_.begin(); i != known_.end(); ++i) { if (i->second.operational() == true) ret++; } return ret; } void gcomm::evs::Proto::deliver_reg_view() { if (install_message_ == 0) { gu_throw_fatal << "Protocol error: no install message in deliver reg view"; } if (previous_views_.size() == 0) gu_throw_fatal << "Zero-size view"; const View& prev_view (previous_view_); View view (install_message_->install_view_id()); for (NodeMap::iterator i = known_.begin(); i != known_.end(); ++i) { if (NodeMap::value(i).installed() == true) { view.add_member(NodeMap::key(i), ""); if (prev_view.members().find(NodeMap::key(i)) == prev_view.members().end()) { view.add_joined(NodeMap::key(i), ""); } } else if (NodeMap::value(i).installed() == false) { const MessageNodeList& instances = install_message_->node_list(); MessageNodeList::const_iterator inst_i; if ((inst_i = instances.find(NodeMap::key(i))) != instances.end()) { if (MessageNodeList::value(inst_i).leaving() == true) { view.add_left(NodeMap::key(i), ""); } else { view.add_partitioned(NodeMap::key(i), ""); } } gcomm_assert(NodeMap::key(i) != uuid()); gcomm_assert(NodeMap::value(i).operational() == false); } } evs_log_info(I_VIEWS) << "delivering view " << view; set_stable_view(view); ProtoUpMeta up_meta(UUID::nil(), ViewId(), &view); send_up(Datagram(), up_meta); } void gcomm::evs::Proto::deliver_trans_view(bool local) { if (local == false && install_message_ == 0) { gu_throw_fatal << "Protocol error: no install message in deliver trans view"; } View view(ViewId(V_TRANS, current_view_.id().uuid(), current_view_.id().seq())); for (NodeMap::const_iterator i = known_.begin(); i != known_.end(); ++i) { const UUID& uuid(NodeMap::key(i)); const Node& inst(NodeMap::value(i)); if (inst.installed() == true && current_view_.members().find(uuid) != current_view_.members().end() && (local == true || MessageNodeList::value(install_message_->node_list().find_checked(uuid)).view_id() == current_view_.id())) { view.add_member(NodeMap::key(i), ""); } else if (inst.installed() == false) { if (local == false) { const MessageNodeList& instances(install_message_->node_list()); MessageNodeList::const_iterator inst_i; if ((inst_i = instances.find(NodeMap::key(i))) != instances.end()) { if (MessageNodeList::value(inst_i).leaving()) { view.add_left(NodeMap::key(i), ""); } else { view.add_partitioned(NodeMap::key(i), ""); } } else if (current_view_.is_member(NodeMap::key(i)) == true) { view.add_partitioned(NodeMap::key(i), ""); } } else { // Just assume others have partitioned, it does not matter // for leaving node anyway and it is not guaranteed if // the others get the leave message, so it is not safe // to assume then as left. view.add_partitioned(NodeMap::key(i), ""); } } else { // merging nodes, these won't be visible in trans view } } gcomm_assert(view.is_member(uuid()) == true); evs_log_info(I_VIEWS) << " delivering view " << view; ProtoUpMeta up_meta(UUID::nil(), ViewId(), &view); gu_trace(send_up(Datagram(), up_meta)); } void gcomm::evs::Proto::deliver_empty_view() { View view(V_REG); evs_log_info(I_VIEWS) << "delivering view " << view; ProtoUpMeta up_meta(UUID::nil(), ViewId(), &view); send_up(Datagram(), up_meta); } void gcomm::evs::Proto::setall_committed(bool val) { for (NodeMap::iterator i = known_.begin(); i != known_.end(); ++i) { NodeMap::value(i).set_committed(val); } } bool gcomm::evs::Proto::is_all_committed() const { for (NodeMap::const_iterator i = known_.begin(); i != known_.end(); ++i) { const Node& inst(NodeMap::value(i)); if (inst.operational() == true && inst.committed() == false) { return false; } } return true; } void gcomm::evs::Proto::setall_installed(bool val) { for (NodeMap::iterator i = known_.begin(); i != known_.end(); ++i) { NodeMap::value(i).set_installed(val); } } void gcomm::evs::Proto::cleanup_joins() { for (NodeMap::iterator i = known_.begin(); i != known_.end(); ++i) { NodeMap::value(i).set_join_message(0); } } bool gcomm::evs::Proto::is_all_installed() const { for (NodeMap::const_iterator i = known_.begin(); i != known_.end(); ++i) { const Node& inst(NodeMap::value(i)); if (inst.operational() == true && inst.installed() == false) { return false; } } return true; } bool gcomm::evs::Proto::is_representative(const UUID& uuid) const { for (NodeMap::const_iterator i = known_.begin(); i != known_.end(); ++i) { if (NodeMap::value(i).operational() == true && NodeMap::value(i).is_inactive() == false) { gcomm_assert(NodeMap::value(i).leave_message() == 0); return (uuid == NodeMap::key(i)); } } return false; } ///////////////////////////////////////////////////////////////////////////// // Message sending ///////////////////////////////////////////////////////////////////////////// bool gcomm::evs::Proto::is_flow_control(const seqno_t seq, const seqno_t win) const { gcomm_assert(seq != -1 && win != -1); const seqno_t base(input_map_->safe_seq()); if (seq > base + win) { return true; } return false; } int gcomm::evs::Proto::send_user(Datagram& dg, uint8_t const user_type, Order const order, seqno_t const win, seqno_t const up_to_seqno, size_t const n_aggregated) { assert(state() == S_LEAVING || state() == S_GATHER || state() == S_OPERATIONAL); assert(dg.offset() == 0); assert(n_aggregated == 1 || output_.size() >= n_aggregated); gcomm_assert(up_to_seqno == -1 || up_to_seqno >= last_sent_); gcomm_assert(up_to_seqno == -1 || win == -1); int ret; const seqno_t seq(last_sent_ + 1); if (win != -1 && is_flow_control(seq, win) == true) { return EAGAIN; } // seq_range max 0xff because of Message seq_range_ field limitation seqno_t seq_range( std::min(up_to_seqno == -1 ? 0 : up_to_seqno - seq, evs::seqno_t(0xff))); seqno_t last_msg_seq(seq + seq_range); uint8_t flags; // If output queue wont contain messages after this patch, // up_to_seqno is given (msg completion) or flow contol would kick in // at next batch, don't set F_MSG_MORE. if (output_.size() <= n_aggregated || up_to_seqno != -1 || (win != -1 && is_flow_control(last_msg_seq + 1, win) == true)) { flags = 0; } else { flags = Message::F_MSG_MORE; } if (n_aggregated > 1) { flags |= Message::F_AGGREGATE; } // Maximize seq range in the case next message batch won't be sent // immediately. if ((flags & Message::F_MSG_MORE) == 0 && up_to_seqno == -1) { seq_range = input_map_->max_hs() - seq; seq_range = std::max(static_cast(0), seq_range); seq_range = std::min(static_cast(0xff), seq_range); if (seq_range != 0) { log_debug << "adjusted seq range to: " << seq_range; last_msg_seq = seq + seq_range; } } gcomm_assert(last_msg_seq >= seq && last_msg_seq - seq <= 0xff); gcomm_assert(seq_range >= 0 && seq_range <= 0xff); UserMessage msg(version_, uuid(), current_view_.id(), seq, input_map_->aru_seq(), seq_range, order, ++fifo_seq_, user_type, flags); // Insert first to input map to determine correct aru seq Range range; gu_trace(range = input_map_->insert(NodeMap::value(self_i_).index(), msg, dg)); gcomm_assert(range.hs() == last_msg_seq) << msg << " " << *input_map_ << " " << *this; last_sent_ = last_msg_seq; assert(range.hs() == last_sent_); update_im_safe_seq(NodeMap::value(self_i_).index(), input_map_->aru_seq()); msg.set_aru_seq(input_map_->aru_seq()); evs_log_debug(D_USER_MSGS) << " sending " << msg; gu_trace(push_header(msg, dg)); if ((ret = send_down(dg, ProtoDownMeta())) != 0) { log_debug << "send failed: " << strerror(ret); } gu_trace(pop_header(msg, dg)); sent_msgs_[Message::T_USER]++; if (delivering_ == false && input_map_->has_deliverables() == true) { gu_trace(deliver()); } gu_trace(deliver_local()); return 0; } size_t gcomm::evs::Proto::aggregate_len() const { bool is_aggregate(false); size_t ret(0); AggregateMessage am; std::deque >::const_iterator i(output_.begin()); const Order ord(i->second.order()); ret += i->first.len() + am.serial_size(); for (++i; i != output_.end() && i->second.order() == ord; ++i) { if (ret + i->first.len() + am.serial_size() <= mtu()) { ret += i->first.len() + am.serial_size(); is_aggregate = true; } else { break; } } evs_log_debug(D_USER_MSGS) << "is aggregate " << is_aggregate << " ret " << ret; return (is_aggregate == true ? ret : 0); } int gcomm::evs::Proto::send_user(const seqno_t win) { gcomm_assert(output_.empty() == false); gcomm_assert(state() == S_OPERATIONAL); gcomm_assert(win <= send_window_); int ret; size_t alen; if (use_aggregate_ == true && (alen = aggregate_len()) > 0) { // Messages can be aggregated into single message send_buf_.resize(alen); size_t offset(0); size_t n(0); std::deque >::iterator i(output_.begin()); Order ord(i->second.order()); while ((alen > 0 && i != output_.end())) { const Datagram& dg(i->first); const ProtoDownMeta dm(i->second); AggregateMessage am(0, dg.len(), dm.user_type()); gcomm_assert(alen >= dg.len() + am.serial_size()); gu_trace(offset = am.serialize(&send_buf_[0], send_buf_.size(), offset)); std::copy(dg.header() + dg.header_offset(), dg.header() + dg.header_size(), &send_buf_[0] + offset); offset += (dg.header_len()); std::copy(dg.payload().begin(), dg.payload().end(), &send_buf_[0] + offset); offset += dg.payload().size(); alen -= dg.len() + am.serial_size(); ++n; ++i; } Datagram dg(gu::SharedBuffer(new gu::Buffer(send_buf_.begin(), send_buf_.end()))); if ((ret = send_user(dg, 0xff, ord, win, -1, n)) == 0) { while (n-- > 0) { output_.pop_front(); } } } else { std::pair wb(output_.front()); if ((ret = send_user(wb.first, wb.second.user_type(), wb.second.order(), win, -1)) == 0) { output_.pop_front(); } } return ret; } void gcomm::evs::Proto::complete_user(const seqno_t high_seq) { gcomm_assert(state() == S_OPERATIONAL || state() == S_GATHER); evs_log_debug(D_USER_MSGS) << "completing seqno to " << high_seq;; Datagram wb; int err; profile_enter(send_user_prof_); err = send_user(wb, 0xff, O_DROP, -1, high_seq); profile_leave(send_user_prof_); if (err != 0) { log_debug << "failed to send completing msg " << strerror(err) << " seq=" << high_seq << " send_window=" << send_window_ << " last_sent=" << last_sent_; } } int gcomm::evs::Proto::send_delegate(Datagram& wb) { DelegateMessage dm(version_, uuid(), current_view_.id(), ++fifo_seq_); push_header(dm, wb); int ret = send_down(wb, ProtoDownMeta()); pop_header(dm, wb); sent_msgs_[Message::T_DELEGATE]++; return ret; } void gcomm::evs::Proto::send_gap(const UUID& range_uuid, const ViewId& source_view_id, const Range range, const bool commit) { evs_log_debug(D_GAP_MSGS) << "sending gap to " << range_uuid << " requesting range " << range; gcomm_assert((commit == false && source_view_id == current_view_.id()) || install_message_ != 0); // TODO: Investigate if gap sending can be somehow limited, // message loss happen most probably during congestion and // flooding network with gap messages won't probably make // conditions better GapMessage gm(version_, uuid(), source_view_id, (source_view_id == current_view_.id() ? last_sent_ : (commit == true ? install_message_->fifo_seq() : -1)), (source_view_id == current_view_.id() ? input_map_->aru_seq() : -1), ++fifo_seq_, range_uuid, range, (commit == true ? Message::F_COMMIT : static_cast(0))); gu::Buffer buf; serialize(gm, buf); Datagram dg(buf); int err = send_down(dg, ProtoDownMeta()); if (err != 0) { log_debug << "send failed: " << strerror(err); } sent_msgs_[Message::T_GAP]++; gu_trace(handle_gap(gm, self_i_)); } void gcomm::evs::Proto::populate_node_list(MessageNodeList* node_list) const { for (NodeMap::const_iterator i = known_.begin(); i != known_.end(); ++i) { const UUID& node_uuid(NodeMap::key(i)); const Node& node(NodeMap::value(i)); MessageNode mnode(node.operational(), node.suspected()); if (node_uuid != uuid()) { const JoinMessage* jm(node.join_message()); const LeaveMessage* lm(node.leave_message()); // if (jm != 0) { const ViewId& nsv(jm->source_view_id()); const MessageNode& mn(MessageNodeList::value(jm->node_list().find_checked(node_uuid))); mnode = MessageNode(node.operational(), node.is_suspected(), -1, jm->source_view_id(), (nsv == current_view_.id() ? input_map_->safe_seq(node.index()) : mn.safe_seq()), (nsv == current_view_.id() ? input_map_->range(node.index()) : mn.im_range())); } else if (lm != 0) { const ViewId& nsv(lm->source_view_id()); mnode = MessageNode(node.operational(), node.is_suspected(), lm->seq(), nsv, (nsv == current_view_.id() ? input_map_->safe_seq(node.index()) : -1), (nsv == current_view_.id() ? input_map_->range(node.index()) : Range())); } else if (current_view_.is_member(node_uuid) == true) { mnode = MessageNode(node.operational(), node.is_suspected(), -1, current_view_.id(), input_map_->safe_seq(node.index()), input_map_->range(node.index())); } } else { mnode = MessageNode(true, false, -1, current_view_.id(), input_map_->safe_seq(node.index()), input_map_->range(node.index())); } gu_trace((void)node_list->insert_unique(std::make_pair(node_uuid, mnode))); } evs_log_debug(D_CONSENSUS) << "populate node list:\n" << *node_list; } const gcomm::evs::JoinMessage& gcomm::evs::Proto::create_join() { MessageNodeList node_list; gu_trace(populate_node_list(&node_list)); JoinMessage jm(version_, uuid(), current_view_.id(), input_map_->safe_seq(), input_map_->aru_seq(), ++fifo_seq_, node_list); NodeMap::value(self_i_).set_join_message(&jm); evs_log_debug(D_JOIN_MSGS) << " created join message " << jm; // Note: This assertion does not hold anymore, join message is // not necessarily equal to local state. // gcomm_assert(consensus_.is_consistent_same_view(jm) == true) // << "created inconsistent JOIN message " << jm // << " local state " << *this; return *NodeMap::value(self_i_).join_message(); } void gcomm::evs::Proto::set_join(const JoinMessage& jm, const UUID& source) { NodeMap::iterator i; gu_trace(i = known_.find_checked(source)); NodeMap::value(i).set_join_message(&jm);; } void gcomm::evs::Proto::set_leave(const LeaveMessage& lm, const UUID& source) { NodeMap::iterator i; gu_trace(i = known_.find_checked(source)); Node& inst(NodeMap::value(i)); if (inst.leave_message()) { evs_log_debug(D_LEAVE_MSGS) << "Duplicate leave:\told: " << *inst.leave_message() << "\tnew: " << lm; } else { inst.set_leave_message(&lm); } } void gcomm::evs::Proto::send_join(bool handle) { assert(output_.empty() == true); JoinMessage jm(create_join()); gu::Buffer buf; serialize(jm, buf); Datagram dg(buf); int err = send_down(dg, ProtoDownMeta()); if (err != 0) { log_debug << "send failed: " << strerror(err); } sent_msgs_[Message::T_JOIN]++; if (handle == true) { handle_join(jm, self_i_); } } void gcomm::evs::Proto::send_leave(bool handle) { gcomm_assert(state() == S_LEAVING); // If no messages have been sent, generate one dummy to // trigger message acknowledgement mechanism if (last_sent_ == -1 && output_.empty() == true) { Datagram wb; gu_trace(send_user(wb, 0xff, O_DROP, -1, -1)); } /* Move all pending messages from output to input map */ while (output_.empty() == false) { std::pair wb = output_.front(); if (send_user(wb.first, wb.second.user_type(), wb.second.order(), -1, -1) != 0) { gu_throw_fatal << "send_user() failed"; } output_.pop_front(); } LeaveMessage lm(version_, uuid(), current_view_.id(), last_sent_, input_map_->aru_seq(), ++fifo_seq_); evs_log_debug(D_LEAVE_MSGS) << "sending leave msg " << lm; gu::Buffer buf; serialize(lm, buf); Datagram dg(buf); int err = send_down(dg, ProtoDownMeta()); if (err != 0) { log_debug << "send failed " << strerror(err); } sent_msgs_[Message::T_LEAVE]++; if (handle == true) { handle_leave(lm, self_i_); } } struct ViewIdCmp { bool operator()(const gcomm::evs::NodeMap::value_type& a, const gcomm::evs::NodeMap::value_type& b) const { using gcomm::evs::NodeMap; gcomm_assert(NodeMap::value(a).join_message() != 0 && NodeMap::value(b).join_message() != 0); return (NodeMap::value(a).join_message()->source_view_id().seq() < NodeMap::value(b).join_message()->source_view_id().seq()); } }; void gcomm::evs::Proto::send_install() { gcomm_assert(consensus_.is_consensus() == true && is_representative(uuid()) == true) << *this; NodeMap oper_list; for_each(known_.begin(), known_.end(), OperationalSelect(oper_list)); NodeMap::const_iterator max_node = max_element(oper_list.begin(), oper_list.end(), ViewIdCmp()); const uint32_t max_view_id_seq = NodeMap::value(max_node).join_message()->source_view_id().seq(); MessageNodeList node_list; populate_node_list(&node_list); InstallMessage imsg(version_, uuid(), current_view_.id(), ViewId(V_REG, uuid(), max_view_id_seq + attempt_seq_), input_map_->safe_seq(), input_map_->aru_seq(), ++fifo_seq_, node_list); ++attempt_seq_; evs_log_debug(D_INSTALL_MSGS) << "sending install " << imsg; gcomm_assert(consensus_.is_consistent(imsg)); gu::Buffer buf; serialize(imsg, buf); Datagram dg(buf); int err = send_down(dg, ProtoDownMeta()); if (err != 0) { log_debug << "send failed: " << strerror(err); } sent_msgs_[Message::T_INSTALL]++; handle_install(imsg, self_i_); } void gcomm::evs::Proto::resend(const UUID& gap_source, const Range range) { gcomm_assert(gap_source != uuid()); gcomm_assert(range.lu() <= range.hs()) << "lu (" << range.lu() << ") > hs(" << range.hs() << ")"; if (range.lu() <= input_map_->safe_seq()) { log_warn << self_string() << "lu (" << range.lu() << ") <= safe_seq(" << input_map_->safe_seq() << "), can't recover message"; return; } evs_log_debug(D_RETRANS) << " retrans requested by " << gap_source << " " << range.lu() << " -> " << range.hs(); seqno_t seq(range.lu()); while (seq <= range.hs()) { InputMap::iterator msg_i = input_map_->find(NodeMap::value(self_i_).index(), seq); if (msg_i == input_map_->end()) { gu_trace(msg_i = input_map_->recover(NodeMap::value(self_i_).index(), seq)); } const UserMessage& msg(InputMapMsgIndex::value(msg_i).msg()); gcomm_assert(msg.source() == uuid()); Datagram rb(InputMapMsgIndex::value(msg_i).rb()); assert(rb.offset() == 0); UserMessage um(msg.version(), msg.source(), msg.source_view_id(), msg.seq(), input_map_->aru_seq(), msg.seq_range(), msg.order(), msg.fifo_seq(), msg.user_type(), static_cast( Message::F_RETRANS | (msg.flags() & Message::F_AGGREGATE))); push_header(um, rb); int err = send_down(rb, ProtoDownMeta()); if (err != 0) { log_debug << "send failed: " << strerror(err); break; } else { evs_log_debug(D_RETRANS) << "retransmitted " << um; } seq = seq + msg.seq_range() + 1; retrans_msgs_++; } } void gcomm::evs::Proto::recover(const UUID& gap_source, const UUID& range_uuid, const Range range) { gcomm_assert(gap_source != uuid()) << "gap_source (" << gap_source << ") == uuid() (" << uuid() << " state " << *this; gcomm_assert(range.lu() <= range.hs()) << "lu (" << range.lu() << ") > hs (" << range.hs() << ")"; if (range.lu() <= input_map_->safe_seq()) { log_warn << self_string() << "lu (" << range.lu() << ") <= safe_seq(" << input_map_->safe_seq() << "), can't recover message"; return; } const Node& range_node(NodeMap::value(known_.find_checked(range_uuid))); const Range im_range(input_map_->range(range_node.index())); evs_log_debug(D_RETRANS) << " recovering message from " << range_uuid << " requested by " << gap_source << " requested range " << range << " available " << im_range; seqno_t seq(range.lu()); while (seq <= range.hs() && seq <= im_range.hs()) { InputMap::iterator msg_i = input_map_->find(range_node.index(), seq); if (msg_i == input_map_->end()) { try { gu_trace(msg_i = input_map_->recover(range_node.index(), seq)); } catch (...) { seq = seq + 1; continue; } } const UserMessage& msg(InputMapMsgIndex::value(msg_i).msg()); assert(msg.source() == range_uuid); Datagram rb(InputMapMsgIndex::value(msg_i).rb()); assert(rb.offset() == 0); UserMessage um(msg.version(), msg.source(), msg.source_view_id(), msg.seq(), msg.aru_seq(), msg.seq_range(), msg.order(), msg.fifo_seq(), msg.user_type(), static_cast( Message::F_SOURCE | Message::F_RETRANS | (msg.flags() & Message::F_AGGREGATE))); push_header(um, rb); int err = send_delegate(rb); if (err != 0) { log_debug << "send failed: " << strerror(err); break; } else { evs_log_debug(D_RETRANS) << "recover " << um; } seq = seq + msg.seq_range() + 1; recovered_msgs_++; } } void gcomm::evs::Proto::handle_foreign(const Message& msg) { // no need to handle foreign LEAVE message if (msg.type() == Message::T_LEAVE) { return; } // don't handle foreing messages in install phase if (state() == S_INSTALL) { //evs_log_debug(D_FOREIGN_MSGS) log_warn << self_string() << " dropping foreign message from " << msg.source() << " in install state"; return; } if (is_msg_from_previous_view(msg) == true) { return; } const UUID& source = msg.source(); evs_log_debug(D_FOREIGN_MSGS) << " detected new message source " << source; NodeMap::iterator i; gu_trace(i = known_.insert_unique( std::make_pair( source, Node(inactive_timeout_, suspect_timeout_)))); assert(NodeMap::value(i).operational() == true); if (state() == S_JOINING || state() == S_GATHER || state() == S_OPERATIONAL) { evs_log_info(I_STATE) << " shift to GATHER due to foreign message from " << msg.source(); gu_trace(shift_to(S_GATHER, false)); } // Set join message after shift to recovery, shift may clean up // join messages if (msg.type() == Message::T_JOIN) { set_join(static_cast(msg), msg.source()); } send_join(true); } void gcomm::evs::Proto::handle_msg(const Message& msg, const Datagram& rb) { assert(msg.type() <= Message::T_LEAVE); if (state() == S_CLOSED) { return; } if (msg.source() == uuid()) { return; } if (msg.version() != version_) { log_info << "incompatible protocol version " << msg.version(); return; } gcomm_assert(msg.source() != UUID::nil()); // Figure out if the message is from known source NodeMap::iterator ii = known_.find(msg.source()); if (ii == known_.end()) { gu_trace(handle_foreign(msg)); return; } Node& node(NodeMap::value(ii)); if (node.operational() == false && node.leave_message() == 0 && (msg.flags() & Message::F_RETRANS) == 0) { // We have set this node unoperational and there was // probably good reason to do so. Don't accept messages // from it before new view has been formed. // Exceptions: // - Node that is leaving // - Retransmitted messages return; } // Filter out non-fifo messages if (msg.fifo_seq() != -1 && (msg.flags() & Message::F_RETRANS) == 0) { if (node.fifo_seq() >= msg.fifo_seq()) { evs_log_debug(D_FOREIGN_MSGS) << "droppoing non-fifo message " << msg << " fifo seq " << node.fifo_seq(); return; } else { node.set_fifo_seq(msg.fifo_seq()); } } // Accept non-membership messages only from current view // or from view to be installed if (msg.is_membership() == false && msg.source_view_id() != current_view_.id() && (install_message_ == 0 || install_message_->install_view_id() != msg.source_view_id())) { // If source node seems to be operational but it has proceeded // into new view, mark it as unoperational in order to create // intermediate views before re-merge. if (node.installed() == true && node.operational() == true && is_msg_from_previous_view(msg) == false && state() != S_LEAVING) { evs_log_info(I_STATE) << " detected new view from operational source " << msg.source() << ": " << msg.source_view_id(); // Note: Commented out, this causes problems with // attempt_seq. Newly (remotely?) generated install message // followed by commit gap may cause undesired // node inactivation and shift to gather. // // set_inactive(msg.source()); // gu_trace(shift_to(S_GATHER, true)); } return; } recvd_msgs_[msg.type()]++; switch (msg.type()) { case Message::T_USER: gu_trace(handle_user(static_cast(msg), ii, rb)); break; case Message::T_DELEGATE: gu_trace(handle_delegate(static_cast(msg), ii, rb)); break; case Message::T_GAP: gu_trace(handle_gap(static_cast(msg), ii)); break; case Message::T_JOIN: gu_trace(handle_join(static_cast(msg), ii)); break; case Message::T_LEAVE: gu_trace(handle_leave(static_cast(msg), ii)); break; case Message::T_INSTALL: gu_trace(handle_install(static_cast(msg), ii)); break; default: log_warn << "invalid message type " << msg.type(); } } //////////////////////////////////////////////////////////////////////// // Protolay interface //////////////////////////////////////////////////////////////////////// size_t gcomm::evs::Proto::unserialize_message(const UUID& source, const Datagram& rb, Message* msg) { size_t offset; const gu::byte_t* begin(gcomm::begin(rb)); const size_t available(gcomm::available(rb)); gu_trace(offset = msg->unserialize(begin, available, 0)); if ((msg->flags() & Message::F_SOURCE) == 0) { gcomm_assert(source != UUID::nil()); msg->set_source(source); } switch (msg->type()) { case Message::T_NONE: gu_throw_fatal; break; case Message::T_USER: gu_trace(offset = static_cast(*msg).unserialize( begin, available, offset, true)); break; case Message::T_DELEGATE: gu_trace(offset = static_cast(*msg).unserialize( begin, available, offset, true)); break; case Message::T_GAP: gu_trace(offset = static_cast(*msg).unserialize( begin, available, offset, true)); break; case Message::T_JOIN: gu_trace(offset = static_cast(*msg).unserialize( begin, available, offset, true)); break; case Message::T_INSTALL: gu_trace(offset = static_cast(*msg).unserialize( begin, available, offset, true)); break; case Message::T_LEAVE: gu_trace(offset = static_cast(*msg).unserialize( begin, available, offset, true)); break; } return (offset + rb.offset()); } void gcomm::evs::Proto::handle_up(const void* cid, const Datagram& rb, const ProtoUpMeta& um) { Message msg; if (state() == S_CLOSED || um.source() == uuid()) { // Silent drop return; } gcomm_assert(um.source() != UUID::nil()); try { size_t offset; gu_trace(offset = unserialize_message(um.source(), rb, &msg)); handle_msg(msg, Datagram(rb, offset)); } catch (gu::Exception& e) { switch (e.get_errno()) { case EPROTONOSUPPORT: log_warn << e.what(); break; case EINVAL: log_warn << "invalid message: " << msg; break; default: log_fatal << "exception caused by message: " << msg; std::cerr << " state after handling message: " << *this; throw; } } } int gcomm::evs::Proto::handle_down(Datagram& wb, const ProtoDownMeta& dm) { if (state() == S_GATHER || state() == S_INSTALL) { return EAGAIN; } else if (state() != S_OPERATIONAL) { log_warn << "user message in state " << to_string(state()); return ENOTCONN; } if (dm.order() == O_LOCAL_CAUSAL) { gu::datetime::Date now(gu::datetime::Date::now()); if (causal_queue_.empty() == true && last_sent_ == input_map_->safe_seq() && causal_keepalive_period_ > gu::datetime::Period(0) && last_causal_keepalive_ + causal_keepalive_period_ > now) { hs_local_causal_.insert(0.0); deliver_causal(dm.user_type(), last_sent_, wb); } else { seqno_t causal_seqno(input_map_->aru_seq()); if (causal_keepalive_period_ == gu::datetime::Period(0) || last_causal_keepalive_ + causal_keepalive_period_ <= now) { // generate traffic to make sure that group is live Datagram dg; int err(send_user(dg, 0xff, O_DROP, -1, -1)); if (err != 0) { return err; } // reassign causal_seqno to be last_sent: // in order to make sure that the group is live, // safe seqno must be advanced and in this case // safe seqno equals to aru seqno. causal_seqno = last_sent_; last_causal_keepalive_ = now; } causal_queue_.push_back(CausalMessage(dm.user_type(), causal_seqno, wb)); } return 0; } send_queue_s_ += output_.size(); ++n_send_queue_s_; int ret = 0; if (output_.empty() == true) { int err; err = send_user(wb, dm.user_type(), dm.order(), user_send_window_, -1); switch (err) { case EAGAIN: { output_.push_back(std::make_pair(wb, dm)); // Fall through } case 0: ret = 0; break; default: log_error << "send error: " << err; ret = err; } } else if (output_.size() < max_output_size_) { output_.push_back(std::make_pair(wb, dm)); } else { ret = EAGAIN; } return ret; } ///////////////////////////////////////////////////////////////////////////// // State handler ///////////////////////////////////////////////////////////////////////////// void gcomm::evs::Proto::shift_to(const State s, const bool send_j) { if (shift_to_rfcnt_ > 0) gu_throw_fatal << *this; shift_to_rfcnt_++; static const bool allowed[S_MAX][S_MAX] = { // CLOSED JOINING LEAVING GATHER INSTALL OPERAT { false, true, false, false, false, false }, // CLOSED { false, false, true, true, false, false }, // JOINING { true, false, false, false, false, false }, // LEAVING { false, false, true, true, true, false }, // GATHER { false, false, false, true, false, true }, // INSTALL { false, false, true, true, false, false } // OPERATIONAL }; assert(s < S_MAX); if (allowed[state_][s] == false) { gu_throw_fatal << "Forbidden state transition: " << to_string(state_) << " -> " << to_string(s); } if (state() != s) { evs_log_info(I_STATE) << " state change: " << to_string(state_) << " -> " << to_string(s); } switch (s) { case S_CLOSED: gcomm_assert(state() == S_LEAVING); gu_trace(deliver()); gu_trace(deliver_local()); setall_installed(false); NodeMap::value(self_i_).set_installed(true); gu_trace(deliver_trans_view(true)); gu_trace(deliver_trans()); gu_trace(deliver_local(true)); gcomm_assert(causal_queue_.empty() == true); if (collect_stats_ == true) { handle_stats_timer(); } gu_trace(deliver_empty_view()); cleanup_unoperational(); cleanup_views(); timers_.clear(); state_ = S_CLOSED; break; case S_JOINING: state_ = S_JOINING; break; case S_LEAVING: state_ = S_LEAVING; reset_timers(); break; case S_GATHER: { setall_committed(false); setall_installed(false); delete install_message_; install_message_ = 0; if (state() == S_OPERATIONAL) { profile_enter(send_user_prof_); while (output_.empty() == false) { int err; gu_trace(err = send_user(-1)); if (err != 0) { gu_throw_fatal << self_string() << "send_user() failed in shifto " << "to S_GATHER: " << strerror(err); } } profile_leave(send_user_prof_); } else { gcomm_assert(output_.empty() == true); } state_ = S_GATHER; if (send_j == true) { profile_enter(send_join_prof_); gu_trace(send_join(false)); profile_leave(send_join_prof_); } gcomm_assert(state() == S_GATHER); reset_timers(); break; } case S_INSTALL: { gcomm_assert(install_message_ != 0); gcomm_assert(is_all_committed() == true); state_ = S_INSTALL; reset_timers(); break; } case S_OPERATIONAL: { gcomm_assert(output_.empty() == true); gcomm_assert(install_message_ != 0); gcomm_assert(NodeMap::value(self_i_).join_message() != 0 && consensus_.equal( *NodeMap::value(self_i_).join_message(), *install_message_)) << "install message not consistent with own join, state: " << *this; gcomm_assert(is_all_installed() == true); gu_trace(deliver()); gu_trace(deliver_local()); gu_trace(deliver_trans_view(false)); gu_trace(deliver_trans()); gu_trace(deliver_local(true)); gcomm_assert(causal_queue_.empty() == true); input_map_->clear(); if (collect_stats_ == true) { handle_stats_timer(); } previous_view_ = current_view_; previous_views_.push_back( std::make_pair(current_view_.id(), gu::datetime::Date::now())); const MessageNodeList& imap(install_message_->node_list()); for (MessageNodeList::const_iterator i = imap.begin(); i != imap.end(); ++i) { previous_views_.push_back( std::make_pair(MessageNodeList::value(i).view_id(), gu::datetime::Date::now())); } current_view_ = View(install_message_->install_view_id()); size_t idx = 0; for (NodeMap::iterator i = known_.begin(); i != known_.end(); ++i) { if (NodeMap::value(i).installed() == true) { gu_trace(current_view_.add_member(NodeMap::key(i), "")); NodeMap::value(i).set_index(idx++); } else { NodeMap::value(i).set_index( std::numeric_limits::max()); } } if (previous_view_.id().type() == V_REG && previous_view_.members() == current_view_.members()) { log_warn << "subsequent views have same members, prev view " << previous_view_ << " current view " << current_view_; } input_map_->reset(current_view_.members().size()); last_sent_ = -1; state_ = S_OPERATIONAL; deliver_reg_view(); cleanup_unoperational(); cleanup_views(); cleanup_joins(); delete install_message_; install_message_ = 0; attempt_seq_ = 1; install_timeout_count_ = 0; profile_enter(send_gap_prof_); gu_trace(send_gap(UUID::nil(), current_view_.id(), Range()));; profile_leave(send_gap_prof_); gcomm_assert(state() == S_OPERATIONAL); reset_timers(); break; } default: gu_throw_fatal << "invalid state"; } shift_to_rfcnt_--; } //////////////////////////////////////////////////////////////////////////// // Message delivery //////////////////////////////////////////////////////////////////////////// void gcomm::evs::Proto::deliver_causal(uint8_t user_type, seqno_t seqno, const Datagram& datagram) { send_up(datagram, ProtoUpMeta(uuid(), current_view_.id(), 0, user_type, O_LOCAL_CAUSAL, seqno)); ++delivered_msgs_[O_LOCAL_CAUSAL]; } void gcomm::evs::Proto::deliver_local(bool trans) { // local causal const seqno_t causal_seq(trans == false ? input_map_->safe_seq() : last_sent_); gu::datetime::Date now(gu::datetime::Date::now()); while (causal_queue_.empty() == false && causal_queue_.front().seqno() <= causal_seq) { const CausalMessage& cm(causal_queue_.front()); hs_local_causal_.insert(double(now.get_utc() - cm.tstamp().get_utc())/gu::datetime::Sec); deliver_causal(cm.user_type(), cm.seqno(), cm.datagram()); causal_queue_.pop_front(); } } void gcomm::evs::Proto::validate_reg_msg(const UserMessage& msg) { if (msg.source_view_id() != current_view_.id()) { // Note: This implementation should guarantee same view delivery, // this is sanity check for that. gu_throw_fatal << "reg validate: not current view"; } if (collect_stats_ == true) { if (msg.order() == O_SAFE) { gu::datetime::Date now(gu::datetime::Date::now()); hs_safe_.insert(double(now.get_utc() - msg.tstamp().get_utc())/gu::datetime::Sec); } else if (msg.order() == O_AGREED) { gu::datetime::Date now(gu::datetime::Date::now()); hs_agreed_.insert(double(now.get_utc() - msg.tstamp().get_utc())/gu::datetime::Sec); } } } void gcomm::evs::Proto::deliver_finish(const InputMapMsg& msg) { if ((msg.msg().flags() & Message::F_AGGREGATE) == 0) { ++delivered_msgs_[msg.msg().order()]; if (msg.msg().order() != O_DROP) { gu_trace(validate_reg_msg(msg.msg())); profile_enter(delivery_prof_); ProtoUpMeta um(msg.msg().source(), msg.msg().source_view_id(), 0, msg.msg().user_type(), msg.msg().order(), msg.msg().seq()); try { send_up(msg.rb(), um); } catch (...) { log_info << msg.msg() << " " << msg.rb().len(); throw; } profile_leave(delivery_prof_); } } else { size_t offset(0); while (offset < msg.rb().len()) { ++delivered_msgs_[msg.msg().order()]; AggregateMessage am; gu_trace(am.unserialize(&msg.rb().payload()[0], msg.rb().payload().size(), offset)); Datagram dg( gu::SharedBuffer( new gu::Buffer( &msg.rb().payload()[0] + offset + am.serial_size(), &msg.rb().payload()[0] + offset + am.serial_size() + am.len()))); ProtoUpMeta um(msg.msg().source(), msg.msg().source_view_id(), 0, am.user_type(), msg.msg().order(), msg.msg().seq()); gu_trace(send_up(dg, um)); offset += am.serial_size() + am.len(); } gcomm_assert(offset == msg.rb().len()); } } void gcomm::evs::Proto::deliver() { if (delivering_ == true) { gu_throw_fatal << "Recursive enter to delivery"; } delivering_ = true; if (state() != S_OPERATIONAL && state() != S_GATHER && state() != S_INSTALL && state() != S_LEAVING) { gu_throw_fatal << "invalid state: " << to_string(state()); } evs_log_debug(D_DELIVERY) << " aru_seq=" << input_map_->aru_seq() << " safe_seq=" << input_map_->safe_seq(); InputMapMsgIndex::iterator i, i_next; for (i = input_map_->begin(); i != input_map_->end(); i = i_next) { i_next = i; ++i_next; const InputMapMsg& msg(InputMapMsgIndex::value(i)); bool deliver = false; switch (msg.msg().order()) { case O_SAFE: if (input_map_->is_safe(i) == true) { deliver = true; } break; case O_AGREED: if (input_map_->is_agreed(i) == true) { deliver = true; } break; case O_FIFO: case O_DROP: if (input_map_->is_fifo(i) == true) { deliver = true; } break; default: gu_throw_fatal << "invalid safety prefix " << msg.msg().order(); } if (deliver == true) { deliver_finish(msg); gu_trace(input_map_->erase(i)); } else if (input_map_->has_deliverables() == false) { break; } } delivering_ = false; } void gcomm::evs::Proto::deliver_trans() { if (delivering_ == true) { gu_throw_fatal << "Recursive enter to delivery"; } delivering_ = true; if (state() != S_INSTALL && state() != S_LEAVING) gu_throw_fatal << "invalid state"; evs_log_debug(D_DELIVERY) << " aru_seq=" << input_map_->aru_seq() << " safe_seq=" << input_map_->safe_seq(); // In transitional configuration we must deliver all messages that // are fifo. This is because: // - We know that it is possible to deliver all fifo messages originated // from partitioned component as safe in partitioned component // - Aru in this component is at least the max known fifo seq // from partitioned component due to message recovery // - All FIFO messages originated from this component must be // delivered to fulfill self delivery requirement and // - FIFO messages originated from this component qualify as AGREED // in transitional configuration InputMap::iterator i, i_next; for (i = input_map_->begin(); i != input_map_->end(); i = i_next) { i_next = i; ++i_next; const InputMapMsg& msg(InputMapMsgIndex::value(i)); bool deliver = false; switch (msg.msg().order()) { case O_SAFE: case O_AGREED: case O_FIFO: case O_DROP: if (input_map_->is_fifo(i) == true) { deliver = true; } break; default: gu_throw_fatal; } if (deliver == true) { if (install_message_ != 0) { const MessageNode& mn( MessageNodeList::value( install_message_->node_list().find_checked( msg.msg().source()))); if (msg.msg().seq() <= mn.im_range().hs()) { deliver_finish(msg); } else { gcomm_assert(mn.operational() == false); log_info << "filtering out trans message higher than " << "install message hs " << mn.im_range().hs() << ": " << msg.msg(); } } else { deliver_finish(msg); } gu_trace(input_map_->erase(i)); } } // Sanity check: // There must not be any messages left that // - Are originated from outside of trans conf and are FIFO // - Are originated from trans conf for (i = input_map_->begin(); i != input_map_->end(); i = i_next) { i_next = i; ++i_next; const InputMapMsg& msg(InputMapMsgIndex::value(i)); NodeMap::iterator ii; gu_trace(ii = known_.find_checked(msg.msg().source())); if (NodeMap::value(ii).installed() == true) { gu_throw_fatal << "Protocol error in transitional delivery " << "(self delivery constraint)"; } else if (input_map_->is_fifo(i) == true) { gu_throw_fatal << "Protocol error in transitional delivery " << "(fifo from partitioned component)"; } gu_trace(input_map_->erase(i)); } delivering_ = false; } ///////////////////////////////////////////////////////////////////////////// // Message handlers ///////////////////////////////////////////////////////////////////////////// gcomm::evs::seqno_t gcomm::evs::Proto::update_im_safe_seq(const size_t uuid, const seqno_t seq) { const seqno_t im_safe_seq(input_map_->safe_seq(uuid)); if (im_safe_seq < seq) { input_map_->set_safe_seq(uuid, seq); } return im_safe_seq; } void gcomm::evs::Proto::handle_user(const UserMessage& msg, NodeMap::iterator ii, const Datagram& rb) { assert(ii != known_.end()); assert(state() != S_CLOSED && state() != S_JOINING); Node& inst(NodeMap::value(ii)); evs_log_debug(D_USER_MSGS) << "received " << msg; if (msg.source_view_id() != current_view_.id()) { if (state() == S_LEAVING) { // Silent drop return; } if (is_msg_from_previous_view(msg) == true) { evs_log_debug(D_FOREIGN_MSGS) << "user message " << msg << " from previous view"; return; } if (inst.operational() == false) { evs_log_debug(D_STATE) << "dropping message from unoperational source " << msg.source(); return; } else if (inst.installed() == false) { if (install_message_ != 0 && msg.source_view_id() == install_message_->install_view_id()) { assert(state() == S_INSTALL); evs_log_debug(D_STATE) << " recovery user message " << msg; // Other instances installed view before this one, so it is // safe to shift to S_OPERATIONAL if consensus has been reached for (MessageNodeList::const_iterator mi = install_message_->node_list().begin(); mi != install_message_->node_list().end(); ++mi) { if (MessageNodeList::value(mi).operational() == true) { NodeMap::iterator jj; gu_trace(jj = known_.find_checked( MessageNodeList::key(mi))); NodeMap::value(jj).set_installed(true); } } inst.set_tstamp(gu::datetime::Date::now()); if (consensus_.is_consensus() == true) { if (state() == S_INSTALL) { profile_enter(shift_to_prof_); gu_trace(shift_to(S_OPERATIONAL)); profile_leave(shift_to_prof_); if (pending_leave_ == true) { close(); } } else { log_warn << self_string() << "received user message from new " << "view while in GATHER, dropping"; return; } } else { profile_enter(shift_to_prof_); evs_log_info(I_STATE) << " no consensus after " << "handling user message from new view"; // Don't change state here but continue waiting for // install gaps. Other nodes should time out eventually // if consensus can't be reached. // gu_trace(shift_to(S_GATHER)); profile_leave(shift_to_prof_); return; } } else { return; } } else { log_debug << self_string() << " unhandled user message " << msg; return; } } gcomm_assert(msg.source_view_id() == current_view_.id()); Range range; Range prev_range; seqno_t prev_aru; seqno_t prev_safe; profile_enter(input_map_prof_); prev_aru = input_map_->aru_seq(); prev_range = input_map_->range(inst.index()); // Insert only if msg seq is greater or equal than current lowest unseen if (msg.seq() >= prev_range.lu()) { Datagram im_dgram(rb, rb.offset()); im_dgram.normalize(); gu_trace(range = input_map_->insert(inst.index(), msg, im_dgram)); if (range.lu() > prev_range.lu()) { inst.set_tstamp(gu::datetime::Date::now()); } } else { range = prev_range; } // Update im safe seq for self update_im_safe_seq(NodeMap::value(self_i_).index(), input_map_->aru_seq()); // Update safe seq for message source prev_safe = update_im_safe_seq(inst.index(), msg.aru_seq()); profile_leave(input_map_prof_); // Check for missing messages if (range.hs() > range.lu() && (msg.flags() & Message::F_RETRANS) == 0 ) { evs_log_debug(D_RETRANS) << " requesting retrans from " << msg.source() << " " << range << " due to input map gap, aru " << input_map_->aru_seq(); profile_enter(send_gap_prof_); gu_trace(send_gap(msg.source(), current_view_.id(), range)); profile_leave(send_gap_prof_); } // Seqno range completion and acknowledgement const seqno_t max_hs(input_map_->max_hs()); if (output_.empty() == true && (state() == S_OPERATIONAL || state() == S_GATHER) && (msg.flags() & Message::F_MSG_MORE) == 0 && (last_sent_ < max_hs)) { // Message not originated from this instance, output queue is empty // and last_sent seqno should be advanced gu_trace(complete_user(max_hs)); } else if (output_.empty() == true && input_map_->aru_seq() != prev_aru) { // Output queue empty and aru changed, send gap to inform others evs_log_debug(D_GAP_MSGS) << "sending empty gap"; profile_enter(send_gap_prof_); gu_trace(send_gap(UUID::nil(), current_view_.id(), Range())); profile_leave(send_gap_prof_); } // Send messages if (state() == S_OPERATIONAL) { profile_enter(send_user_prof_); while (output_.empty() == false) { int err; gu_trace(err = send_user(send_window_)); if (err != 0) { break; } } profile_leave(send_user_prof_); } // Deliver messages profile_enter(delivery_prof_); if (input_map_->has_deliverables() == true) { gu_trace(deliver()); } gu_trace(deliver_local()); profile_leave(delivery_prof_); // If in recovery state, send join each time input map aru seq reaches // last sent and either input map aru or safe seq has changed. if (state() == S_GATHER && consensus_.highest_reachable_safe_seq() == input_map_->aru_seq() && (prev_aru != input_map_->aru_seq() || prev_safe != input_map_->safe_seq()) && (msg.flags() & Message::F_RETRANS) == 0) { gcomm_assert(output_.empty() == true); if (consensus_.is_consensus() == false) { profile_enter(send_join_prof_); gu_trace(send_join()); profile_leave(send_join_prof_); } } } void gcomm::evs::Proto::handle_delegate(const DelegateMessage& msg, NodeMap::iterator ii, const Datagram& rb) { gcomm_assert(ii != known_.end()); // evs_log_debug(D_DELEGATE_MSGS) << "delegate message " << msg; log_debug << "delegate"; Message umsg; size_t offset; gu_trace(offset = unserialize_message(UUID::nil(), rb, &umsg)); gu_trace(handle_msg(umsg, Datagram(rb, offset))); } void gcomm::evs::Proto::handle_gap(const GapMessage& msg, NodeMap::iterator ii) { assert(ii != known_.end()); assert(state() != S_CLOSED && state() != S_JOINING); Node& inst(NodeMap::value(ii)); evs_log_debug(D_GAP_MSGS) << "gap message " << msg; if ((msg.flags() & Message::F_COMMIT) != 0) { log_debug << self_string() << " commit gap from " << msg.source(); if (state() == S_GATHER && install_message_ != 0 && install_message_->fifo_seq() == msg.seq()) { inst.set_committed(true); inst.set_tstamp(gu::datetime::Date::now()); if (is_all_committed() == true) { shift_to(S_INSTALL); gu_trace(send_gap(UUID::nil(), install_message_->install_view_id(), Range()));; } } else if (state() == S_GATHER && install_message_ != 0 && install_message_->fifo_seq() < msg.seq()) { // new install message has been generated shift_to(S_GATHER, true); } else { evs_log_debug(D_GAP_MSGS) << " unhandled commit gap " << msg; } return; } else if (state() == S_INSTALL && install_message_ != 0 && install_message_->install_view_id() == msg.source_view_id()) { evs_log_debug(D_STATE) << "install gap " << msg; inst.set_installed(true); inst.set_tstamp(gu::datetime::Date::now()); if (is_all_installed() == true) { profile_enter(shift_to_prof_); gu_trace(shift_to(S_OPERATIONAL)); profile_leave(shift_to_prof_); if (pending_leave_ == true) { close(); } } return; } else if (msg.source_view_id() != current_view_.id()) { if (state() == S_LEAVING) { // Silently drop return; } if (is_msg_from_previous_view(msg) == true) { evs_log_debug(D_FOREIGN_MSGS) << "gap message from previous view"; return; } if (inst.operational() == false) { evs_log_debug(D_STATE) << "dropping message from unoperational source " << msg.source(); } else if (inst.installed() == false) { evs_log_debug(D_STATE) << "dropping message from uninstalled source " << msg.source(); } else { log_debug << "unhandled gap message " << msg; } return; } gcomm_assert(msg.source_view_id() == current_view_.id()); // seqno_t prev_safe; profile_enter(input_map_prof_); prev_safe = update_im_safe_seq(inst.index(), msg.aru_seq()); // Deliver messages and update tstamp only if safe_seq changed // for the source. if (prev_safe != input_map_->safe_seq(inst.index())) { inst.set_tstamp(gu::datetime::Date::now()); } profile_leave(input_map_prof_); // if (msg.range_uuid() == uuid()) { if (msg.range().hs() > last_sent_ && (state() == S_OPERATIONAL || state() == S_GATHER)) { // This could be leaving node requesting messages up to // its last sent. gu_trace(complete_user(msg.range().hs())); } const seqno_t upper_bound( std::min(msg.range().hs(), last_sent_)); if (msg.range().lu() <= upper_bound) { gu_trace(resend(msg.source(), Range(msg.range().lu(), upper_bound))); } } // if (state() == S_OPERATIONAL) { if (output_.empty() == false) { profile_enter(send_user_prof_); while (output_.empty() == false) { int err; gu_trace(err = send_user(send_window_)); if (err != 0) break; } profile_leave(send_user_prof_); } else { const seqno_t max_hs(input_map_->max_hs()); if (last_sent_ < max_hs) { gu_trace(complete_user(max_hs)); } } } profile_enter(delivery_prof_); if (input_map_->has_deliverables() == true) { gu_trace(deliver()); } gu_trace(deliver_local()); profile_leave(delivery_prof_); // if (state() == S_GATHER && consensus_.highest_reachable_safe_seq() == input_map_->aru_seq() && prev_safe != input_map_->safe_seq() ) { gcomm_assert(output_.empty() == true); if (consensus_.is_consensus() == false) { profile_enter(send_join_prof_); gu_trace(send_join()); profile_leave(send_join_prof_); } } } bool gcomm::evs::Proto::update_im_safe_seqs(const MessageNodeList& node_list) { bool updated = false; // Update input map state for (MessageNodeList::const_iterator i = node_list.begin(); i != node_list.end(); ++i) { const UUID& node_uuid(MessageNodeList::key(i)); const Node& local_node(NodeMap::value(known_.find_checked(node_uuid))); const MessageNode& node(MessageNodeList::value(i)); gcomm_assert(node.view_id() == current_view_.id()); const seqno_t safe_seq(node.safe_seq()); seqno_t prev_safe_seq; gu_trace(prev_safe_seq = update_im_safe_seq(local_node.index(), safe_seq)); if (prev_safe_seq != safe_seq && input_map_->safe_seq(local_node.index()) == safe_seq) { updated = true; } } return updated; } void gcomm::evs::Proto::retrans_user(const UUID& nl_uuid, const MessageNodeList& node_list) { for (MessageNodeList::const_iterator i = node_list.begin(); i != node_list.end(); ++i) { const UUID& node_uuid(MessageNodeList::key(i)); const MessageNode& mn(MessageNodeList::value(i)); const Node& n(NodeMap::value(known_.find_checked(node_uuid))); const Range r(input_map_->range(n.index())); if (node_uuid == uuid() && mn.im_range().lu() != r.lu()) { // Source member is missing messages from us gcomm_assert(mn.im_range().hs() <= last_sent_); gu_trace(resend(nl_uuid, Range(mn.im_range().lu(), last_sent_))); } else if ((mn.operational() == false || mn.leaving() == true) && node_uuid != uuid() && (mn.im_range().lu() < r.lu() || mn.im_range().hs() < r.hs())) { gu_trace(recover(nl_uuid, node_uuid, Range(mn.im_range().lu(), r.hs()))); } } } void gcomm::evs::Proto::retrans_leaves(const MessageNodeList& node_list) { for (NodeMap::const_iterator li = known_.begin(); li != known_.end(); ++li) { const Node& local_node(NodeMap::value(li)); if (local_node.leave_message() != 0 && local_node.is_inactive() == false) { MessageNodeList::const_iterator msg_li( node_list.find(NodeMap::key(li))); if (msg_li == node_list.end() || MessageNodeList::value(msg_li).leaving() == false) { const LeaveMessage& lm(*NodeMap::value(li).leave_message()); LeaveMessage send_lm(lm.version(), lm.source(), lm.source_view_id(), lm.seq(), lm.aru_seq(), lm.fifo_seq(), Message::F_RETRANS | Message::F_SOURCE); gu::Buffer buf; serialize(send_lm, buf); Datagram dg(buf); gu_trace(send_delegate(dg)); } } } } class SelectSuspectsOp { public: SelectSuspectsOp(gcomm::evs::MessageNodeList& nl) : nl_(nl) { } void operator()(const gcomm::evs::MessageNodeList::value_type& vt) const { if (gcomm::evs::MessageNodeList::value(vt).suspected() == true) { nl_.insert_unique(vt); } } private: gcomm::evs::MessageNodeList& nl_; }; void gcomm::evs::Proto::check_suspects(const UUID& source, const MessageNodeList& nl) { assert(source != uuid()); MessageNodeList suspected; for_each(nl.begin(), nl.end(), SelectSuspectsOp(suspected)); for (MessageNodeList::const_iterator i(suspected.begin()); i != suspected.end(); ++i) { const UUID& node_uuid(MessageNodeList::key(i)); const MessageNode& node(MessageNodeList::value(i)); if (node.suspected() == true) { if (node_uuid != uuid()) { size_t s_cnt(0); // Iterate over join messages to see if majority agrees // with the suspicion for (NodeMap::const_iterator j(known_.begin()); j != known_.end(); ++j) { const JoinMessage* jm(NodeMap::value(j).join_message()); if (jm != 0 && jm->source() != node_uuid) { MessageNodeList::const_iterator mni(jm->node_list().find(node_uuid)); if (mni != jm->node_list().end()) { const MessageNode& mn(MessageNodeList::value(mni)); if (mn.suspected() == true) { ++s_cnt; } } } } const Node& kn(NodeMap::value(known_.find_checked(node_uuid))); if (kn.operational() == true && s_cnt > current_view_.members().size()/2) { evs_log_info(I_STATE) << " declaring suspected " << node_uuid << " as inactive"; set_inactive(node_uuid); } } } } } // If one node has set some other as unoperational but we see it still // operational, do some arbitration. We wait until 2/3 of inactive timeout // from setting of consensus timer has passed. If both nodes express liveness, // select the one with greater uuid as a victim of exclusion. void gcomm::evs::Proto::cross_check_inactives(const UUID& source, const MessageNodeList& nl) { assert(source != uuid()); const gu::datetime::Date now(gu::datetime::Date::now()); const gu::datetime::Period wait_c_to((install_timeout_*2)/3); // Wait for consensus TimerList::const_iterator ti( find_if(timers_.begin(), timers_.end(), TimerSelectOp(T_INSTALL))); assert(ti != timers_.end()); if (now + wait_c_to < TimerList::key(ti)) { // No check yet return; } NodeMap::const_iterator source_i(known_.find_checked(source)); const Node& local_source_node(NodeMap::value(source_i)); const gu::datetime::Period ito((install_timeout_*1)/3); // Tighter inactive timeout MessageNodeList inactive; for_each(nl.begin(), nl.end(), SelectNodesOp(inactive, ViewId(), false, false)); for (MessageNodeList::const_iterator i = inactive.begin(); i != inactive.end(); ++i) { const UUID& node_uuid(MessageNodeList::key(i)); const MessageNode& node(MessageNodeList::value(i)); gcomm_assert(node.operational() == false); NodeMap::iterator local_i(known_.find(node_uuid)); if (local_i != known_.end() && node_uuid != uuid()) { Node& local_node(NodeMap::value(local_i)); if (local_node.operational() == true && local_source_node.tstamp() + ito >= now && local_node.tstamp() + ito >= now && node_uuid > source) { evs_log_info(I_STATE) << " arbitrating, select " << node_uuid; set_inactive(node_uuid); } } } } // For each node thas has no join message associated, iterate over other // known nodes' join messages to find out if the node without join message // should be declared inactive. void gcomm::evs::Proto::check_unseen() { for (NodeMap::iterator i(known_.begin()); i != known_.end(); ++i) { const UUID& node_uuid(NodeMap::key(i)); Node& node(NodeMap::value(i)); if (node_uuid != uuid() && current_view_.is_member(node_uuid) == false && node.join_message() == 0 && node.operational() == true) { evs_log_info(I_STATE) << "checking operational unseen " << node_uuid; size_t cnt(0), inact_cnt(0); for (NodeMap::iterator j(known_.begin()); j != known_.end(); ++j) { const JoinMessage* jm(NodeMap::value(j).join_message()); if (jm == 0 || NodeMap::key(j) == uuid()) { continue; } MessageNodeList::const_iterator mn_i; for (mn_i = jm->node_list().begin(); mn_i != jm->node_list().end(); ++mn_i) { NodeMap::const_iterator known_i( known_.find(MessageNodeList::key(mn_i))); if (known_i == known_.end() || (MessageNodeList::value(mn_i).operational() == true && NodeMap::value(known_i).join_message() == 0)) { evs_log_info(I_STATE) << "all joins not locally present for " << NodeMap::key(j) << " join message node list"; return; } } if ((mn_i = jm->node_list().find(node_uuid)) != jm->node_list().end()) { const MessageNode& mn(MessageNodeList::value(mn_i)); evs_log_info(I_STATE) << "found " << node_uuid << " from " << NodeMap::key(j) << " join message: " << mn.view_id() << " " << mn.operational(); if (mn.view_id() != ViewId(V_REG)) { ++cnt; if (mn.operational() == false) ++inact_cnt; } } } if (cnt > 0 && cnt == inact_cnt) { evs_log_info(I_STATE) << "unseen node marked inactive by others (cnt=" << cnt << ", inact_cnt=" << inact_cnt << ")"; set_inactive(node_uuid); } } } } // Iterate over all join messages. If some node has nil view id and suspected // flag true in all present join messages, declare it inactive. void gcomm::evs::Proto::check_nil_view_id() { size_t join_counts(0); std::map nil_counts; for (NodeMap::const_iterator i(known_.begin()); i != known_.end(); ++i) { const JoinMessage* jm(NodeMap::value(i).join_message()); if (jm == 0) { continue; } ++join_counts; for (MessageNodeList::const_iterator j(jm->node_list().begin()); j != jm->node_list().end(); ++j) { const MessageNode& mn(MessageNodeList::value(j)); if (mn.view_id() == ViewId(V_REG)) { if (mn.suspected() == true) { const UUID& uuid(MessageNodeList::key(j)); ++nil_counts[uuid]; } } } } for (std::map::const_iterator i(nil_counts.begin()); i != nil_counts.end(); ++i) { if (i->second == join_counts) { log_info << "node " << i->first << " marked with nil view id and suspected in all present" << " join messages, declaring inactive"; set_inactive(i->first); } } } void gcomm::evs::Proto::handle_join(const JoinMessage& msg, NodeMap::iterator ii) { assert(ii != known_.end()); assert(state() != S_CLOSED); Node& inst(NodeMap::value(ii)); evs_log_debug(D_JOIN_MSGS) << " " << msg; if (state() == S_LEAVING) { if (msg.source_view_id() == current_view_.id()) { inst.set_tstamp(gu::datetime::Date::now()); MessageNodeList same_view; for_each(msg.node_list().begin(), msg.node_list().end(), SelectNodesOp(same_view, current_view_.id(), true, true)); profile_enter(input_map_prof_); if (update_im_safe_seqs(same_view) == true) { profile_enter(send_leave_prof_); gu_trace(send_leave(false)); profile_leave(send_leave_prof_); } for (NodeMap::const_iterator i = known_.begin(); i != known_.end(); ++i) { const UUID& uuid(NodeMap::key(i)); const Node& node(NodeMap::value(i)); if (current_view_.is_member(uuid) == true) { const Range r(input_map_->range(node.index())); if (r.lu() <= last_sent_) { send_gap(uuid, current_view_.id(), Range(r.lu(), last_sent_)); } } } profile_leave(input_map_prof_); gu_trace(retrans_user(msg.source(), same_view)); } return; } else if (is_msg_from_previous_view(msg) == true) { return; } else if (install_message_ != 0) { // Note: don't send join from this branch yet, join is // sent at the end of this method if (install_message_->source() == msg.source()) { evs_log_info(I_STATE) << "shift to gather due to representative " << msg.source() << " join"; gu_trace(shift_to(S_GATHER, false)); } else if (consensus_.is_consistent(*install_message_) == true) { return; // Commented out: It seems to be better strategy to // just wait source of inconsistent join to time out // instead of shifting to gather. #443 // if (consensus_.is_consistent(msg) == true) // { // return; // } // else // { // log_warn << "join message not consistent " << msg; // log_info << "state (stderr): "; // std::cerr << *this << std::endl; // // gu_trace(shift_to(S_GATHER, false)); // } } else { evs_log_info(I_STATE) << "shift to GATHER, install message is " << "inconsistent when handling join from " << msg.source() << " " << msg.source_view_id(); evs_log_info(I_STATE) << "state: " << *this; gu_trace(shift_to(S_GATHER, false)); } } else if (state() != S_GATHER) { evs_log_info(I_STATE) << " shift to GATHER while handling join message from " << msg.source() << " " << msg.source_view_id(); gu_trace(shift_to(S_GATHER, false)); } gcomm_assert(output_.empty() == true); // @todo Figure out how to avoid setting tstamp from any join message if (inst.join_message() != 0) inst.set_tstamp(gu::datetime::Date::now()); inst.set_join_message(&msg); // Add unseen nodes to known list. No need to adjust node state here, // it is done later on in check_suspects()/cross_check_inactives(). for (MessageNodeList::const_iterator i(msg.node_list().begin()); i != msg.node_list().end(); ++i) { NodeMap::iterator ni(known_.find(MessageNodeList::key(i))); if (ni == known_.end()) { known_.insert_unique( std::make_pair(MessageNodeList::key(i), Node(inactive_timeout_, suspect_timeout_))); } } // Select nodes that are coming from the same view as seen by // message source MessageNodeList same_view; for_each(msg.node_list().begin(), msg.node_list().end(), SelectNodesOp(same_view, current_view_.id(), true, true)); // Find out self from node list MessageNodeList::const_iterator nlself_i(same_view.find(uuid())); // Other node coming from the same view if (msg.source() != uuid() && msg.source_view_id() == current_view_.id()) { gcomm_assert(nlself_i != same_view.end()); // Update input map state (void)update_im_safe_seqs(same_view); // Find out max hs and complete up to that if needed MessageNodeList::const_iterator max_hs_i( max_element(same_view.begin(), same_view.end(), RangeHsCmp())); const seqno_t max_hs(MessageNodeList::value(max_hs_i).im_range().hs()); if (last_sent_ < max_hs) { gu_trace(complete_user(max_hs)); } } // gu_trace(retrans_user(msg.source(), same_view)); // Retrans leave messages that others are missing gu_trace(retrans_leaves(same_view)); // Check if this node is set inactive by the other, // if yes, the other must be marked as inactive too if (msg.source() != uuid() && nlself_i != same_view.end()) { if (MessageNodeList::value(nlself_i).operational() == false) { evs_log_info(I_STATE) << " declaring source " << msg.source() << " as inactive (mutual exclusion)"; set_inactive(msg.source()); } } // Make cross check to resolve conflict if two nodes // declare each other inactive. There is no need to make // this for own messages. if (msg.source() != uuid()) { gu_trace(check_suspects(msg.source(), same_view)); gu_trace(cross_check_inactives(msg.source(), same_view)); gu_trace(check_unseen()); gu_trace(check_nil_view_id()); } // If current join message differs from current state, send new join const JoinMessage* curr_join(NodeMap::value(self_i_).join_message()); MessageNodeList new_nl; populate_node_list(&new_nl); if (curr_join == 0 || (curr_join->aru_seq() != input_map_->aru_seq() || curr_join->seq() != input_map_->safe_seq() || curr_join->node_list() != new_nl)) { gu_trace(create_join()); if (consensus_.is_consensus() == false) { send_join(false); } } if (consensus_.is_consensus() == true) { if (is_representative(uuid()) == true) { gu_trace(send_install()); } } } void gcomm::evs::Proto::handle_leave(const LeaveMessage& msg, NodeMap::iterator ii) { assert(ii != known_.end()); assert(state() != S_CLOSED && state() != S_JOINING); Node& node(NodeMap::value(ii)); evs_log_debug(D_LEAVE_MSGS) << "leave message " << msg; if (msg.source() != uuid() && node.is_inactive() == true) { evs_log_debug(D_LEAVE_MSGS) << "dropping leave from already inactive"; return; } node.set_leave_message(&msg); if (msg.source() == uuid()) { // The last one to live, instant close. Otherwise continue // serving until it becomes apparent that others have // leave message. if (current_view_.members().size() == 1) { profile_enter(shift_to_prof_); gu_trace(shift_to(S_CLOSED)); profile_leave(shift_to_prof_); } } else { if (msg.source_view_id() != current_view_.id() || is_msg_from_previous_view(msg) == true) { // Silent drop return; } node.set_operational(false); const seqno_t prev_safe_seq(update_im_safe_seq(node.index(), msg.aru_seq())); if (prev_safe_seq != input_map_->safe_seq(node.index())) { node.set_tstamp(gu::datetime::Date::now()); } if (state() == S_OPERATIONAL) { profile_enter(shift_to_prof_); evs_log_info(I_STATE) << " shift to GATHER when handling leave from " << msg.source() << " " << msg.source_view_id(); gu_trace(shift_to(S_GATHER, true)); profile_leave(shift_to_prof_); } else if (state() == S_GATHER && prev_safe_seq != input_map_->safe_seq(node.index())) { profile_enter(send_join_prof_); gu_trace(send_join()); profile_leave(send_join_prof_); } } } void gcomm::evs::Proto::handle_install(const InstallMessage& msg, NodeMap::iterator ii) { assert(ii != known_.end()); assert(state() != S_CLOSED && state() != S_JOINING); Node& inst(NodeMap::value(ii)); evs_log_debug(D_INSTALL_MSGS) << "install msg " << msg; if (state() == S_LEAVING) { MessageNodeList::const_iterator mn_i = msg.node_list().find(uuid()); if (mn_i != msg.node_list().end()) { const MessageNode& mn(MessageNodeList::value(mn_i)); if (mn.operational() == false || mn.leaving() == true) { profile_enter(shift_to_prof_); gu_trace(shift_to(S_CLOSED)); profile_leave(shift_to_prof_); } } return; } else if (state() == S_OPERATIONAL && current_view_.id() == msg.source_view_id()) { evs_log_debug(D_INSTALL_MSGS) << "dropping install message in already installed view"; return; } else if (inst.operational() == false) { evs_log_debug(D_INSTALL_MSGS) << "previously unoperatioal message source " << msg.source() << " discarding message"; return; } else if (is_msg_from_previous_view(msg) == true) { evs_log_debug(D_FOREIGN_MSGS) << " dropping install message from previous view"; return; } else if (install_message_ != 0) { log_warn << self_string() << " shift to GATHER due to duplicate install"; gu_trace(shift_to(S_GATHER)); return; } else if (inst.installed() == true) { log_warn << self_string() << " shift to GATHER due to inconsistent state"; profile_enter(shift_to_prof_); gu_trace(shift_to(S_GATHER)); profile_leave(shift_to_prof_); return; } else if (is_representative(msg.source()) == false) { log_warn << self_string() << " source " << msg.source() << " is not supposed to be representative"; // Isolate node from my group // set_inactive(msg.source()); profile_enter(shift_to_prof_); gu_trace(shift_to(S_GATHER)); profile_leave(shift_to_prof_); return; } assert(install_message_ == 0); bool ic; if ((ic = consensus_.is_consistent(msg)) == false) { gcomm_assert(msg.source() != uuid()); // Construct join from install message so that the most recent // information from representative is updated to local state. const MessageNode& mn( MessageNodeList::value( msg.node_list().find_checked(msg.source()))); JoinMessage jm(msg.version(), msg.source(), mn.view_id(), msg.seq(), msg.aru_seq(), msg.fifo_seq(), msg.node_list()); handle_join(jm, ii); ic = consensus_.is_consistent(msg); } if (ic == true) { inst.set_tstamp(gu::datetime::Date::now()); install_message_ = new InstallMessage(msg); profile_enter(send_gap_prof_); gu_trace(send_gap(UUID::nil(), install_message_->install_view_id(), Range(), true)); profile_leave(send_gap_prof_); } else { evs_log_debug(D_INSTALL_MSGS) << "install message " << msg << " not consistent with state " << *this; profile_enter(shift_to_prof_); gu_trace(shift_to(S_GATHER, true)); profile_leave(shift_to_prof_); } } percona-xtradb-cluster-galera/gcomm/src/evs_proto.hpp0000644000000000000000000003103712247075736023300 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ /*! * @file evs_proto.hpp * * @brief EVS protocol implementation header. */ #ifndef GCOMM_EVS_PROTO_HPP #define GCOMM_EVS_PROTO_HPP #include "gcomm/protolay.hpp" #include "gcomm/view.hpp" #include "gcomm/transport.hpp" #include "gcomm/map.hpp" #include "histogram.hpp" #include "profile.hpp" #include "evs_seqno.hpp" #include "evs_node.hpp" #include "evs_consensus.hpp" #include "gu_datetime.hpp" #include #include #include #include #ifndef GCOMM_EVS_MAX_VERSION #define GCOMM_EVS_MAX_VERSION 0 #endif // GCOMM_EVS_MAX_VERSION namespace gcomm { namespace evs { class Message; class MessageNodeList; class UserMessage; class DelegateMessage; class GapMessage; class JoinMessage; class InstallMessage; class LeaveMessage; class InputMap; class InputMapMsg; class Proto; std::ostream& operator<<(std::ostream&, const Proto&); } } /*! * @brief Class implementing EVS protocol */ class gcomm::evs::Proto : public Protolay { public: enum State { S_CLOSED, S_JOINING, S_LEAVING, S_GATHER, S_INSTALL, S_OPERATIONAL, S_MAX }; static std::string to_string(const State s) { switch (s) { case S_CLOSED: return "CLOSED"; case S_JOINING: return "JOINING"; case S_LEAVING: return "LEAVING"; case S_GATHER: return "GATHER"; case S_INSTALL: return "INSTALL"; case S_OPERATIONAL: return "OPERATIONAL"; default: gu_throw_fatal << "Invalid state"; } } friend std::ostream& operator<<(std::ostream&, const Proto&); /*! * Default constructor. */ Proto(gu::Config& conf, const UUID& my_uuid, const gu::URI& uri = gu::URI("evs://"), const size_t mtu = std::numeric_limits::max()); ~Proto(); const UUID& uuid() const { return my_uuid_; } std::string self_string() const { std::ostringstream os; os << "evs::proto(" << uuid() << ", " << to_string(state()) << ", " << current_view_.id() << ")"; return os.str(); } State state() const { return state_; } size_t known_size() const { return known_.size(); } bool is_output_empty() const { return output_.empty(); } std::string stats() const; void reset_stats(); bool is_flow_control(const seqno_t, const seqno_t win) const; int send_user(Datagram&, uint8_t, Order, seqno_t, seqno_t, size_t n_aggregated = 1); size_t mtu() const { return mtu_; } size_t aggregate_len() const; int send_user(const seqno_t); void complete_user(const seqno_t); int send_delegate(Datagram&); void send_gap(const UUID&, const ViewId&, const Range, bool commit = false); const JoinMessage& create_join(); void send_join(bool tval = true); void set_join(const JoinMessage&, const UUID&); void set_leave(const LeaveMessage&, const UUID&); void send_leave(bool handle = true); void send_install(); void resend(const UUID&, const Range); void recover(const UUID&, const UUID&, const Range); void retrans_user(const UUID&, const MessageNodeList&); void retrans_leaves(const MessageNodeList&); void set_inactive(const UUID&); void check_inactive(); void cleanup_unoperational(); void cleanup_views(); void cleanup_joins(); size_t n_operational() const; void validate_reg_msg(const UserMessage&); void deliver_finish(const InputMapMsg&); void deliver(); void deliver_local(bool trans = false); void deliver_causal(uint8_t user_type, seqno_t seqno, const Datagram&); void validate_trans_msg(const UserMessage&); void deliver_trans(); void deliver_reg_view(); void deliver_trans_view(bool local); void deliver_empty_view(); void setall_committed(bool val); bool is_all_committed() const; void setall_installed(bool val); bool is_all_installed() const; bool is_representative(const UUID& pid) const; void shift_to(const State, const bool send_j = true); // Message handlers private: /*! * Update input map safe seq * @param uuid Node uuid * @param seq Sequence number * @return Input map seqno before updating */ seqno_t update_im_safe_seq(const size_t uuid, const seqno_t seq); /*! * Update input map safe seqs according to message node list. Only * inactive nodes are allowed to be in */ bool update_im_safe_seqs(const MessageNodeList&); bool is_msg_from_previous_view(const Message&); void check_suspects(const UUID&, const MessageNodeList&); void cross_check_inactives(const UUID&, const MessageNodeList&); void check_unseen(); void check_nil_view_id(); void handle_foreign(const Message&); void handle_user(const UserMessage&, NodeMap::iterator, const Datagram&); void handle_delegate(const DelegateMessage&, NodeMap::iterator, const Datagram&); void handle_gap(const GapMessage&, NodeMap::iterator); void handle_join(const JoinMessage&, NodeMap::iterator); void handle_leave(const LeaveMessage&, NodeMap::iterator); void handle_install(const InstallMessage&, NodeMap::iterator); void populate_node_list(MessageNodeList*) const; public: static size_t unserialize_message(const UUID&, const Datagram&, Message*); void handle_msg(const Message& msg, const Datagram& dg = Datagram()); // Protolay void handle_up(const void*, const Datagram&, const ProtoUpMeta&); int handle_down(Datagram& wb, const ProtoDownMeta& dm); void handle_stable_view(const View& view) { set_stable_view(view); } void connect(bool first) { gu_trace(shift_to(S_JOINING)); gu_trace(send_join(first)); } void close() { // shifting to S_LEAVING from S_INSTALL is troublesome, // instead of that raise a boolean flag to indicate that // shifting to S_LEAVING should be done once S_OPERATIONAL // is reached // // #760 - pending leave should be done also from S_GATHER, // changing state to S_LEAVING resets timers and may prevent // remaining nodes to reach new group until install timer // times out log_debug << self_string() << " closing in state " << state(); if (state() != S_GATHER && state() != S_INSTALL) { gu_trace(shift_to(S_LEAVING)); gu_trace(send_leave()); pending_leave_ = false; } else { pending_leave_ = true; } } void close(const UUID& uuid) { set_inactive(uuid); } bool set_param(const std::string& key, const std::string& val); // gu::datetime::Date functions do appropriate actions for timer handling // and return next expiration time private: public: enum Timer { T_INACTIVITY, T_RETRANS, T_INSTALL, T_STATS }; /*! * Internal timer list */ class TimerList : public MultiMap { }; private: TimerList timers_; public: // These need currently to be public for unit tests void handle_inactivity_timer(); void handle_retrans_timer(); void handle_install_timer(); void handle_stats_timer(); gu::datetime::Date next_expiration(const Timer) const; void reset_timers(); gu::datetime::Date handle_timers(); /*! * @brief Flags controlling what debug information is logged if * debug logging is turned on. */ enum DebugFlags { D_STATE = 1 << 0, /*!< State changes */ D_TIMERS = 1 << 1, /*!< Timer handling */ D_CONSENSUS = 1 << 2, /*!< Consensus protocol */ D_USER_MSGS = 1 << 3, /*!< User messages */ D_DELEGATE_MSGS = 1 << 4, /*!< Delegate messages */ D_GAP_MSGS = 1 << 5, /*!< Gap messages */ D_JOIN_MSGS = 1 << 6, /*!< Join messages */ D_INSTALL_MSGS = 1 << 7, /*!< Install messages */ D_LEAVE_MSGS = 1 << 8, /*!< Leave messages */ D_FOREIGN_MSGS = 1 << 9, /*!< Foreing messages */ D_RETRANS = 1 << 10, /*!< Retransmitted/recovered messages */ D_DELIVERY = 1 << 11 /*!< Message delivery */ }; /*! * @brief Flags controlling what info log is printed in logs. */ enum InfoFlags { I_VIEWS = 1 << 0, /*!< View changes */ I_STATE = 1 << 1, /*!< State change information */ I_STATISTICS = 1 << 2, /*!< Statistics */ I_PROFILING = 1 << 3 /*!< Profiling information */ }; private: int version_; static const int max_version_ = GCOMM_EVS_MAX_VERSION; int debug_mask_; int info_mask_; gu::datetime::Date last_stats_report_; bool collect_stats_; Histogram hs_agreed_; Histogram hs_safe_; Histogram hs_local_causal_; long long int send_queue_s_; long long int n_send_queue_s_; std::vector sent_msgs_; long long int retrans_msgs_; long long int recovered_msgs_; std::vector recvd_msgs_; std::vector delivered_msgs_; prof::Profile send_user_prof_; prof::Profile send_gap_prof_; prof::Profile send_join_prof_; prof::Profile send_install_prof_; prof::Profile send_leave_prof_; prof::Profile consistent_prof_; prof::Profile consensus_prof_; prof::Profile shift_to_prof_; prof::Profile input_map_prof_; prof::Profile delivery_prof_; bool delivering_; UUID my_uuid_; // // Known instances NodeMap known_; NodeMap::iterator self_i_; // gu::datetime::Period view_forget_timeout_; gu::datetime::Period inactive_timeout_; gu::datetime::Period suspect_timeout_; gu::datetime::Period inactive_check_period_; gu::datetime::Period retrans_period_; gu::datetime::Period install_timeout_; gu::datetime::Period join_retrans_period_; gu::datetime::Period stats_report_period_; gu::datetime::Period causal_keepalive_period_; gu::datetime::Date last_inactive_check_; gu::datetime::Date last_causal_keepalive_; // Current view id // ViewId current_view; View current_view_; View previous_view_; std::list > previous_views_; // Map containing received messages and aru/safe seqnos InputMap* input_map_; // Helper container for local causal messages class CausalMessage { public: CausalMessage(uint8_t user_type, seqno_t seqno, const Datagram& datagram) : user_type_(user_type), seqno_ (seqno ), datagram_ (datagram ), tstamp_ (gu::datetime::Date::now()) { } uint8_t user_type() const { return user_type_; } seqno_t seqno() const { return seqno_ ; } const Datagram& datagram() const { return datagram_ ; } const gu::datetime::Date& tstamp() const { return tstamp_ ; } private: uint8_t user_type_; seqno_t seqno_; Datagram datagram_; gu::datetime::Date tstamp_; }; // Queue containing local causal messages std::deque causal_queue_; // Consensus module Consensus consensus_; // Last received install message InstallMessage* install_message_; // Install attempt counter uint32_t attempt_seq_; // Install timeout counting int max_install_timeouts_; int install_timeout_count_; // Sequence number to maintain membership message FIFO order int64_t fifo_seq_; // Last sent seq seqno_t last_sent_; // Protocol send window size seqno_t send_window_; // User send window size seqno_t user_send_window_; // Output message queue std::deque > output_; std::vector send_buf_; uint32_t max_output_size_; size_t mtu_; bool use_aggregate_; bool self_loopback_; State state_; int shift_to_rfcnt_; bool pending_leave_; // non-copyable Proto(const Proto&); void operator=(const Proto&); }; #endif // EVS_PROTO_HPP percona-xtradb-cluster-galera/gcomm/src/evs_seqno.hpp0000644000000000000000000000325012247075736023256 0ustar rootroot00000000000000/* * Copyright (C) 2009-2012 Codership Oy */ #ifndef EVS_SEQNO_HPP #define EVS_SEQNO_HPP #include "gcomm/types.hpp" #include "gu_serialize.hpp" //#include // for uint16_t #include #include namespace gcomm { namespace evs { typedef int64_t seqno_t; class Range; std::ostream& operator<<(std::ostream&, const Range&); } } /*! * */ class gcomm::evs::Range { public: Range(const seqno_t lu = -1, const seqno_t hs = -1) : lu_(lu), hs_(hs) {} seqno_t lu() const { return lu_; } seqno_t hs() const { return hs_; } void set_lu(const seqno_t s) { lu_ = s; } void set_hs(const seqno_t s) { hs_ = s; } size_t serialize(gu::byte_t* buf, size_t buflen, size_t offset) const { gu_trace(offset = gu::serialize8(lu_, buf, buflen, offset)); gu_trace(offset = gu::serialize8(hs_, buf, buflen, offset)); return offset; } size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset) { gu_trace(offset = gu::unserialize8(buf, buflen, offset, lu_)); gu_trace(offset = gu::unserialize8(buf, buflen, offset, hs_)); return offset; } static size_t serial_size() { return 2 * sizeof(seqno_t); } bool operator==(const Range& cmp) const { return (lu_ == cmp.lu_ && hs_ == cmp.hs_); } private: seqno_t lu_; /*!< Lowest unseen seqno */ seqno_t hs_; /*!< Highest seen seqno */ }; inline std::ostream& gcomm::evs::operator<<(std::ostream& os, const gcomm::evs::Range& r) { return (os << "[" << r.lu() << "," << r.hs() << "]"); } #endif // EVS_SEQNO_HPP percona-xtradb-cluster-galera/gcomm/src/gcomm/0000755000000000000000000000000012247075736021645 5ustar rootroot00000000000000percona-xtradb-cluster-galera/gcomm/src/gmcast.cpp0000644000000000000000000013157412247075736022540 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #include "gmcast.hpp" #include "gmcast_proto.hpp" #include "gcomm/common.hpp" #include "gcomm/conf.hpp" #include "gcomm/util.hpp" #include "gcomm/map.hpp" #include "defaults.hpp" #include "gu_convert.hpp" #include "gu_resolver.hpp" using namespace std::rel_ops; using gcomm::gmcast::Proto; using gcomm::gmcast::ProtoMap; using gcomm::gmcast::Link; using gcomm::gmcast::LinkMap; using gcomm::gmcast::Message; const long gcomm::GMCast::max_retry_cnt_(std::numeric_limits::max()); static void set_tcp_defaults (gu::URI* uri) { // what happens if there is already this parameter? uri->set_option(gcomm::Conf::TcpNonBlocking, gu::to_string(1)); } static bool check_tcp_uri(const gu::URI& uri) { return (uri.get_scheme() == gcomm::TCP_SCHEME || uri.get_scheme() == gcomm::SSL_SCHEME); } static std::string get_scheme(bool use_ssl) { if (use_ssl == true) { return gcomm::SSL_SCHEME; } return gcomm::TCP_SCHEME; } gcomm::GMCast::GMCast(Protonet& net, const gu::URI& uri) : Transport (net, uri), version_(check_range(Conf::GMCastVersion, param(conf_, uri, Conf::GMCastVersion, "0"), 0, max_version_ + 1)), my_uuid_ (0, 0), use_ssl_ (param(conf_, uri, Conf::SocketUseSsl, "false")), // @todo: technically group name should be in path component group_name_ (param(conf_, uri, Conf::GMCastGroup, "")), listen_addr_ ( param( conf_, uri, Conf::GMCastListenAddr, get_scheme(use_ssl_) + "://0.0.0.0")), // how to make it IPv6 safe? initial_addrs_(), mcast_addr_ (param(conf_, uri, Conf::GMCastMCastAddr, "")), bind_ip_ (""), mcast_ttl_ (check_range( Conf::GMCastMCastTTL, param(conf_, uri, Conf::GMCastMCastTTL, "1"), 1, 256)), listener_ (0), mcast_ (), pending_addrs_(), remote_addrs_ (), addr_blacklist_(), relaying_ (false), isolate_ (false), proto_map_ (new ProtoMap()), mcast_tree_ (), relay_set_ (), time_wait_ (param(conf_, uri, Conf::GMCastTimeWait, "PT5S")), check_period_ ("PT0.5S"), peer_timeout_ (param(conf_, uri, Conf::GMCastPeerTimeout, "PT3S")), max_initial_reconnect_attempts_( param(conf_, uri, Conf::GMCastMaxInitialReconnectAttempts, gu::to_string(max_retry_cnt_))), next_check_ (gu::datetime::Date::now()) { log_info << "GMCast version " << version_; if (group_name_ == "") { gu_throw_error (EINVAL) << "Group not defined in URL: " << uri_.to_string(); } set_initial_addr(uri_); try { listen_addr_ = uri_.get_option (Conf::GMCastListenAddr); } catch (gu::NotFound&) {} try { gu::URI uri(listen_addr_); /* check validity of the address */ } catch (gu::Exception&) { /* most probably no scheme, try to append one and see if it succeeds */ listen_addr_ = uri_string(get_scheme(use_ssl_), listen_addr_); gu_trace(gu::URI uri(listen_addr_)); } gu::URI listen_uri(listen_addr_); if (check_tcp_uri(listen_uri) == false) { gu_throw_error (EINVAL) << "listen addr '" << listen_addr_ << "' does not specify supported protocol"; } if (gu::net::resolve(listen_uri).get_addr().is_anyaddr() == false) { // bind outgoing connections to the same address as listening. gu_trace(bind_ip_ = listen_uri.get_host()); } std::string port(Defaults::GMCastTcpPort); try { port = listen_uri.get_port(); } catch (gu::NotSet&) { // if no listen port is set for listen address in the options, // see if base port was configured try { port = conf_.get(BASE_PORT_KEY); } catch (gu::NotFound&) { // if no base port configured, try port from the connection address try { port = uri_.get_port(); } catch (gu::NotSet&) {} } listen_addr_ += ":" + port; } // if (!conf_.has(BASE_PORT_KEY)) { conf_.set(BASE_PORT_KEY, port); // } listen_addr_ = gu::net::resolve(listen_addr_).to_string(); // resolving sets scheme to tcp, have to rewrite for ssl if (use_ssl_ == true) { listen_addr_.replace(0, 3, gcomm::SSL_SCHEME); } if (initial_addrs_.find(listen_addr_) != initial_addrs_.end()) { gu_throw_error(EINVAL) << "connect address points to listen address '" << listen_addr_ << "', check that cluster address '" << uri.get_host() << ":" << port << "' is correct"; } if (mcast_addr_ != "") { try { port = uri_.get_option(Conf::GMCastMCastPort); } catch (gu::NotFound&) {} mcast_addr_ = gu::net::resolve( uri_string(gcomm::UDP_SCHEME, mcast_addr_, port)).to_string(); } log_info << self_string() << " listening at " << listen_addr_; log_info << self_string() << " multicast: " << mcast_addr_ << ", ttl: " << mcast_ttl_; conf_.set(Conf::GMCastListenAddr, listen_addr_); conf_.set(Conf::GMCastMCastAddr, mcast_addr_); conf_.set(Conf::GMCastVersion, gu::to_string(version_)); conf_.set(Conf::GMCastTimeWait, gu::to_string(time_wait_)); conf_.set(Conf::GMCastMCastTTL, gu::to_string(mcast_ttl_)); conf_.set(Conf::GMCastPeerTimeout, gu::to_string(peer_timeout_)); } gcomm::GMCast::~GMCast() { if (listener_ != 0) close(); delete proto_map_; } void gcomm::GMCast::set_initial_addr(const gu::URI& uri) { const gu::URI::AuthorityList& al(uri.get_authority_list()); for (gu::URI::AuthorityList::const_iterator i(al.begin()); i != al.end(); ++i) { std::string host; try { host = i->host(); } catch (gu::NotSet& ns) { gu_throw_error(EINVAL) << "Unset host in URL " << uri; } if (host_is_any(host)) continue; std::string port; try { port = i->port(); } catch (gu::NotSet& ) { try { port = conf_.get(BASE_PORT_KEY); } catch (gu::NotFound&) { port = Defaults::GMCastTcpPort; } } std::string initial_addr = gu::net::resolve( uri_string(get_scheme(use_ssl_), host, port) ).to_string(); // resolving sets scheme to tcp, have to rewrite for ssl if (use_ssl_ == true) { initial_addr.replace(0, 3, gcomm::SSL_SCHEME); } if (check_tcp_uri(initial_addr) == false) { gu_throw_error (EINVAL) << "initial addr '" << initial_addr << "' is not valid"; } log_debug << self_string() << " initial addr: " << initial_addr; initial_addrs_.insert(initial_addr); } } void gcomm::GMCast::connect() { pstack_.push_proto(this); log_debug << "gmcast " << uuid() << " connect"; gu::URI listen_uri(listen_addr_); set_tcp_defaults (&listen_uri); listener_ = pnet().acceptor(listen_uri); gu_trace (listener_->listen(listen_uri)); if (!mcast_addr_.empty()) { gu::URI mcast_uri( mcast_addr_ + '?' + gcomm::Socket::OptIfAddr + '=' + gu::URI(listen_addr_).get_host()+'&' + gcomm::Socket::OptNonBlocking + "=1&" + gcomm::Socket::OptMcastTTL + '=' + gu::to_string(mcast_ttl_) ); mcast_ = pnet().socket(mcast_uri); gu_trace(mcast_->connect(mcast_uri)); } if (!initial_addrs_.empty()) { for (std::set::const_iterator i(initial_addrs_.begin()); i != initial_addrs_.end(); ++i) { insert_address(*i, UUID(), pending_addrs_); AddrList::iterator ai(pending_addrs_.find(*i)); AddrList::value(ai).set_max_retries(max_retry_cnt_); gu_trace (gmcast_connect(*i)); } } } void gcomm::GMCast::connect(const gu::URI& uri) { set_initial_addr(uri); connect(); } void gcomm::GMCast::close(bool force) { log_debug << "gmcast " << uuid() << " close"; pstack_.pop_proto(this); if (mcast_ != 0) { mcast_->close(); // delete mcast; // mcast = 0; } gcomm_assert(listener_ != 0); listener_->close(); delete listener_; listener_ = 0; mcast_tree_.clear(); for (ProtoMap::iterator i = proto_map_->begin(); i != proto_map_->end(); ++i) { delete ProtoMap::value(i); } proto_map_->clear(); pending_addrs_.clear(); remote_addrs_.clear(); } void gcomm::GMCast::gmcast_accept() { SocketPtr tp; try { tp = listener_->accept(); } catch (gu::Exception& e) { log_warn << e.what(); return; } if (isolate_ == true) { log_debug << "dropping accepted socket due to isolation"; tp->close(); return; } Proto* peer = new Proto ( version_, tp, listener_->listen_addr() /* listen_addr */, "", mcast_addr_, uuid(), group_name_); std::pair ret = proto_map_->insert(std::make_pair(tp->id(), peer)); if (ret.second == false) { delete peer; gu_throw_fatal << "Failed to add peer to map"; } if (tp->state() == Socket::S_CONNECTED) { peer->send_handshake(); } else { log_debug << "accepted socket is connecting"; } log_debug << "handshake sent"; } void gcomm::GMCast::gmcast_connect(const std::string& remote_addr) { if (remote_addr == listen_addr_) return; gu::URI connect_uri(remote_addr); set_tcp_defaults (&connect_uri); if (!bind_ip_.empty()) { connect_uri.set_option(gcomm::Socket::OptIfAddr, bind_ip_); } SocketPtr tp = pnet().socket(connect_uri); try { tp->connect(connect_uri); } catch (gu::Exception& e) { log_debug << "Connect failed: " << e.what(); // delete tp; return; } Proto* peer = new Proto ( version_, tp, listener_->listen_addr()/* listen_addr*/ , remote_addr, mcast_addr_, uuid(), group_name_); std::pair ret = proto_map_->insert(std::make_pair(tp->id(), peer)); if (ret.second == false) { delete peer; gu_throw_fatal << "Failed to add peer to map"; } ret.first->second->wait_handshake(); } void gcomm::GMCast::gmcast_forget(const UUID& uuid) { /* Close all proto entries corresponding to uuid */ ProtoMap::iterator pi, pi_next; for (pi = proto_map_->begin(); pi != proto_map_->end(); pi = pi_next) { pi_next = pi, ++pi_next; Proto* rp = ProtoMap::value(pi); if (rp->remote_uuid() == uuid) { delete rp; proto_map_->erase(pi); } } /* Set all corresponding entries in address list to have retry cnt * greater than max retries and next reconnect time after some period */ AddrList::iterator ai; for (ai = remote_addrs_.begin(); ai != remote_addrs_.end(); ++ai) { AddrEntry& ae(AddrList::value(ai)); if (ae.uuid() == uuid) { log_info << "forgetting " << uuid << " (" << AddrList::key(ai) << ")"; ProtoMap::iterator pi, pi_next; for (pi = proto_map_->begin(); pi != proto_map_->end(); pi = pi_next) { pi_next = pi, ++pi_next; Proto* rp = ProtoMap::value(pi); if (rp->remote_addr() == AddrList::key(ai)) { log_info << "deleting entry " << AddrList::key(ai); delete rp; proto_map_->erase(pi); } } ae.set_max_retries(0); ae.set_retry_cnt(1); ae.set_next_reconnect(gu::datetime::Date::now() + time_wait_); } } /* Update state */ update_addresses(); } void gcomm::GMCast::handle_connected(Proto* rp) { const SocketPtr tp(rp->socket()); assert(tp->state() == Socket::S_CONNECTED); log_debug << "transport " << tp << " connected"; if (rp->state() == Proto::S_INIT) { log_debug << "sending hanshake"; // accepted socket was waiting for underlying transport // handshake to finish rp->send_handshake(); } } void gcomm::GMCast::handle_established(Proto* est) { log_debug << self_string() << " connection established to " << est->remote_uuid() << " " << est->remote_addr(); if (est->remote_uuid() == uuid()) { std::set::iterator ia_i(initial_addrs_.find(est->remote_addr())); if (ia_i != initial_addrs_.end()) { initial_addrs_.erase(ia_i); } AddrList::iterator i(pending_addrs_.find(est->remote_addr())); if (i != pending_addrs_.end()) { log_warn << self_string() << " address '" << est->remote_addr() << "' points to own listening address, blacklisting"; pending_addrs_.erase(i); addr_blacklist_.insert(make_pair(est->remote_addr(), AddrEntry(gu::datetime::Date::now(), gu::datetime::Date::now(), est->remote_uuid()))); } proto_map_->erase( proto_map_->find_checked(est->socket()->id())); delete est; update_addresses(); return; } // If address is found from pending_addrs_, move it to remote_addrs list // and set retry cnt to -1 const std::string& remote_addr(est->remote_addr()); AddrList::iterator i(pending_addrs_.find(remote_addr)); if (i != pending_addrs_.end()) { log_debug << "Erasing " << remote_addr << " from panding list"; pending_addrs_.erase(i); } if ((i = remote_addrs_.find(remote_addr)) == remote_addrs_.end()) { log_debug << "Inserting " << remote_addr << " to remote list"; insert_address (remote_addr, est->remote_uuid(), remote_addrs_); i = remote_addrs_.find(remote_addr); } else if (AddrList::value(i).uuid() != est->remote_uuid()) { log_info << "remote endpoint " << est->remote_addr() << " changed identity " << AddrList::value(i).uuid() << " -> " << est->remote_uuid(); remote_addrs_.erase(i); i = remote_addrs_.insert_unique( make_pair(est->remote_addr(), AddrEntry(gu::datetime::Date::now(), gu::datetime::Date::max(), est->remote_uuid()))); } if (AddrList::value(i).retry_cnt() > AddrList::value(i).max_retries()) { log_warn << "discarding established (time wait) " << est->remote_uuid() << " (" << est->remote_addr() << ") "; proto_map_->erase(proto_map_->find(est->socket()->id())); delete est; update_addresses(); return; } // send_up(Datagram(), p->remote_uuid()); // init retry cnt to -1 to avoid unnecessary logging at first attempt // max retries will be readjusted in handle stable view AddrList::value(i).set_retry_cnt(-1); AddrList::value(i).set_max_retries(max_initial_reconnect_attempts_); // Cleanup all previously established entries with same // remote uuid. It is assumed that the most recent connection // is usually the healthiest one. ProtoMap::iterator j, j_next; for (j = proto_map_->begin(); j != proto_map_->end(); j = j_next) { j_next = j, ++j_next; Proto* p(ProtoMap::value(j)); if (p->remote_uuid() == est->remote_uuid()) { if (p->handshake_uuid() < est->handshake_uuid()) { log_info << self_string() << " cleaning up duplicate " << p->socket() << " after established " << est->socket(); proto_map_->erase(j); delete p; } else if (p->handshake_uuid() > est->handshake_uuid()) { log_info << self_string() << " cleaning up established " << est->socket() << " which is duplicate of " << p->socket(); proto_map_->erase( proto_map_->find_checked(est->socket()->id())); delete est; break; } else { assert(p == est); } } } update_addresses(); } void gcomm::GMCast::handle_failed(Proto* failed) { log_debug << "handle failed: " << *failed; const std::string& remote_addr = failed->remote_addr(); bool found_ok(false); for (ProtoMap::const_iterator i = proto_map_->begin(); i != proto_map_->end(); ++i) { Proto* p(ProtoMap::value(i)); if (p != failed && p->state() <= Proto::S_OK && p->remote_addr() == failed->remote_addr()) { log_debug << "found live " << *p; found_ok = true; break; } } if (found_ok == false && remote_addr != "") { AddrList::iterator i; if ((i = pending_addrs_.find(remote_addr)) != pending_addrs_.end() || (i = remote_addrs_.find(remote_addr)) != remote_addrs_.end()) { AddrEntry& ae(AddrList::value(i)); ae.set_retry_cnt(ae.retry_cnt() + 1); gu::datetime::Date rtime = gu::datetime::Date::now() + gu::datetime::Period("PT1S"); log_debug << self_string() << " setting next reconnect time to " << rtime << " for " << remote_addr; ae.set_next_reconnect(rtime); } } std::set::iterator si(relay_set_.find(failed->socket().get())); if (si != relay_set_.end()) { relay_set_.erase(si); } proto_map_->erase(failed->socket()->id()); delete failed; update_addresses(); } bool gcomm::GMCast::is_connected(const std::string& addr, const UUID& uuid) const { for (ProtoMap::const_iterator i = proto_map_->begin(); i != proto_map_->end(); ++i) { Proto* conn = ProtoMap::value(i); if (addr == conn->remote_addr() || uuid == conn->remote_uuid()) { return true; } } return false; } void gcomm::GMCast::insert_address (const std::string& addr, const UUID& uuid, AddrList& alist) { if (addr == listen_addr_) { gu_throw_fatal << "Trying to add self addr " << addr << " to addr list"; } if (alist.insert(make_pair(addr, AddrEntry(gu::datetime::Date::now(), gu::datetime::Date::now(), uuid))).second == false) { log_warn << "Duplicate entry: " << addr; } else { log_debug << self_string() << ": new address entry " << uuid << ' ' << addr; } } void gcomm::GMCast::update_addresses() { LinkMap link_map; std::set uuids; /* Add all established connections into uuid_map and update * list of remote addresses */ ProtoMap::iterator i, i_next; for (i = proto_map_->begin(); i != proto_map_->end(); i = i_next) { i_next = i, ++i_next; Proto* rp = ProtoMap::value(i); if (rp->state() == Proto::S_OK) { if (rp->remote_addr() == "" || rp->remote_uuid() == UUID::nil()) { gu_throw_fatal << "Protocol error: local: (" << my_uuid_ << ", '" << listen_addr_ << "'), remote: (" << rp->remote_uuid() << ", '" << rp->remote_addr() << "')"; } if (remote_addrs_.find(rp->remote_addr()) == remote_addrs_.end()) { log_warn << "Connection exists but no addr on addr list for " << rp->remote_addr(); insert_address(rp->remote_addr(), rp->remote_uuid(), remote_addrs_); } if (uuids.insert(rp->remote_uuid()).second == false) { // Duplicate entry, drop this one // @todo Deeper inspection about the connection states log_debug << self_string() << " dropping duplicate entry"; proto_map_->erase(i); delete rp; } else { link_map.insert(Link(rp->remote_uuid(), rp->remote_addr(), rp->mcast_addr())); } } } /* Send topology change message containing only established * connections */ for (ProtoMap::iterator i = proto_map_->begin(); i != proto_map_->end(); ++i) { Proto* gp = ProtoMap::value(i); // @todo: a lot of stuff here is done for each connection, including // message creation and serialization. Need a mcast_msg() call // and move this loop in there. if (gp->state() == Proto::S_OK) gp->send_topology_change(link_map); } /* Add entries reported by all other nodes to address list to * get complete view of existing uuids/addresses */ for (ProtoMap::iterator i = proto_map_->begin(); i != proto_map_->end(); ++i) { Proto* rp = ProtoMap::value(i); if (rp->state() == Proto::S_OK) { for (LinkMap::const_iterator j = rp->link_map().begin(); j != rp->link_map().end(); ++j) { const UUID& link_uuid(LinkMap::key(j)); const std::string& link_addr(LinkMap::value(j).addr()); gcomm_assert(link_uuid != UUID::nil() && link_addr != ""); if (addr_blacklist_.find(link_addr) != addr_blacklist_.end()) { log_info << self_string() << " address '" << link_addr << "' pointing to uuid " << link_uuid << " is blacklisted, skipping"; continue; } if (link_uuid != uuid() && remote_addrs_.find(link_addr) == remote_addrs_.end() && pending_addrs_.find(link_addr) == pending_addrs_.end()) { log_debug << self_string() << " conn refers to but no addr in addr list for " << link_addr; insert_address(link_addr, link_uuid, remote_addrs_); AddrList::iterator pi(remote_addrs_.find(link_addr)); assert(pi != remote_addrs_.end()); AddrEntry& ae(AddrList::value(pi)); // init retry cnt to -1 to avoid unnecessary logging // at first attempt // max retries will be readjusted in handle stable view ae.set_retry_cnt(-1); ae.set_max_retries(max_initial_reconnect_attempts_); // Add some randomness for first reconnect to avoid // simultaneous connects gu::datetime::Date rtime(gu::datetime::Date::now()); rtime = rtime + ::rand() % (100*gu::datetime::MSec); ae.set_next_reconnect(rtime); next_check_ = std::min(next_check_, rtime); } } } } // Build multicast tree log_debug << self_string() << " --- mcast tree begin ---"; mcast_tree_.clear(); if (mcast_ != 0) { log_debug << mcast_addr_; mcast_tree_.push_back(mcast_.get()); } for (ProtoMap::const_iterator i(proto_map_->begin()); i != proto_map_->end(); ++i) { const Proto& p(*ProtoMap::value(i)); log_debug << "Proto: " << p.state() << " " << p.remote_addr() << " " << p.mcast_addr(); if (p.state() == Proto::S_OK && (p.mcast_addr() == "" || p.mcast_addr() != mcast_addr_)) { log_debug << p.remote_addr(); mcast_tree_.push_back(p.socket().get()); } } log_debug << self_string() << " --- mcast tree end ---"; } void gcomm::GMCast::reconnect() { if (isolate_ == true) { log_debug << "skipping reconnect due to isolation"; return; } /* Loop over known remote addresses and connect if proto entry * does not exist */ gu::datetime::Date now = gu::datetime::Date::now(); AddrList::iterator i, i_next; for (i = pending_addrs_.begin(); i != pending_addrs_.end(); i = i_next) { i_next = i, ++i_next; const std::string& pending_addr(AddrList::key(i)); const AddrEntry& ae(AddrList::value(i)); if (is_connected (pending_addr, UUID::nil()) == false && ae.next_reconnect() <= now) { if (ae.retry_cnt() > ae.max_retries()) { log_info << "cleaning up pending addr " << pending_addr; pending_addrs_.erase(i); continue; // no reference to pending_addr after this } else if (ae.next_reconnect() <= now) { log_debug << "connecting to pending " << pending_addr; gmcast_connect (pending_addr); } } } for (i = remote_addrs_.begin(); i != remote_addrs_.end(); i = i_next) { i_next = i, ++i_next; const std::string& remote_addr(AddrList::key(i)); const AddrEntry& ae(AddrList::value(i)); const UUID& remote_uuid(ae.uuid()); gcomm_assert(remote_uuid != uuid()); if (is_connected(remote_addr, remote_uuid) == false && ae.next_reconnect() <= now) { if (ae.retry_cnt() > ae.max_retries()) { log_info << " cleaning up " << remote_uuid << " (" << remote_addr << ")"; remote_addrs_.erase(i); continue;//no reference to remote_addr or remote_uuid after this } else if (ae.next_reconnect() <= now) { if (ae.retry_cnt() % 30 == 0) { log_info << self_string() << " reconnecting to " << remote_uuid << " (" << remote_addr << "), attempt " << ae.retry_cnt(); } gmcast_connect(remote_addr); } else { // } } } } namespace { class CmpUuidCounts { public: CmpUuidCounts(const std::set& uuids) : uuids_(uuids) { } size_t count(const gcomm::gmcast::Proto* p) const { size_t cnt(0); for (std::set::const_iterator i(uuids_.begin()); i != uuids_.end(); ++i) { for (gcomm::gmcast::LinkMap::const_iterator lm_i(p->link_map().begin()); lm_i != p->link_map().end(); ++lm_i) { if (lm_i->uuid() == *i) { ++cnt; break; } } } return cnt; } bool operator()(const gcomm::gmcast::Proto* a, const gcomm::gmcast::Proto* b) const { return (count(a) < count(b)); } private: const std::set& uuids_; }; } void gcomm::GMCast::check_liveness() { std::set live_uuids; // iterate over proto map and mark all timed out entries as failed gu::datetime::Date now(gu::datetime::Date::now()); for (ProtoMap::iterator i(proto_map_->begin()); i != proto_map_->end(); ) { ProtoMap::iterator i_next(i); ++i_next; Proto* p(ProtoMap::value(i)); if (p->state() > Proto::S_INIT && p->state() < Proto::S_FAILED && p->tstamp() + peer_timeout_ < now) { log_debug << self_string() << " connection to peer " << p->remote_uuid() << " with addr " << p->remote_addr() << " timed out"; p->set_state(Proto::S_FAILED); handle_failed(p); } else if (p->state() == Proto::S_OK) { // log_info << "live proto " << *p; live_uuids.insert(p->remote_uuid()); } i = i_next; } bool should_relay(false); // iterate over addr list and check if there is at least one live // proto entry associated to each addr entry std::set nonlive_uuids; std::string nonlive_peers; for (AddrList::const_iterator i(remote_addrs_.begin()); i != remote_addrs_.end(); ++i) { const AddrEntry& ae(AddrList::value(i)); if (ae.retry_cnt() <= ae.max_retries() && live_uuids.find(ae.uuid()) == live_uuids.end()) { // log_info << self_string() // << " missing live proto entry for " << ae.uuid(); nonlive_uuids.insert(ae.uuid()); nonlive_peers += AddrList::key(i) + " "; should_relay = true; } } if (should_relay == true) { if (relaying_ == false) { log_info << self_string() << " turning message relay requesting on, nonlive peers: " << nonlive_peers; relaying_ = true; } relay_set_.clear(); // build set of protos having OK status std::set proto_set; for (ProtoMap::iterator i(proto_map_->begin()); i != proto_map_->end(); ++i) { Proto* p(ProtoMap::value(i)); if (p->state() == Proto::S_OK) { proto_set.insert(p); } } // find minimal set of proto entries required to reach maximum set // of nonlive peers while (nonlive_uuids.empty() == false && proto_set.empty() == false) { std::set::iterator maxel( std::max_element(proto_set.begin(), proto_set.end(), CmpUuidCounts(nonlive_uuids))); Proto* p(*maxel); log_debug << "relay set maxel :" << *p << " count: " << CmpUuidCounts(nonlive_uuids).count(p); relay_set_.insert(p->socket().get()); const LinkMap& lm(p->link_map()); for (LinkMap::const_iterator lm_i(lm.begin()); lm_i != lm.end(); ++lm_i) { nonlive_uuids.erase((*lm_i).uuid()); } proto_set.erase(maxel); } } else if (relaying_ == true && should_relay == false) { log_info << self_string() << " turning message relay requesting off"; relay_set_.clear(); relaying_ = false; } } gu::datetime::Date gcomm::GMCast::handle_timers() { const gu::datetime::Date now(gu::datetime::Date::now()); if (now >= next_check_) { check_liveness(); reconnect(); next_check_ = now + check_period_; } return next_check_; } void gcomm::GMCast::relay(const Message& msg, const Datagram& dg, const void* exclude_id) { Message relay_msg(msg); relay_msg.set_flags(relay_msg.flags() & ~Message::F_RELAY); Datagram relay_dg(dg); relay_dg.normalize(); gu_trace(push_header(relay_msg, relay_dg)); for (std::list::iterator i(mcast_tree_.begin()); i != mcast_tree_.end(); ++i) { int err; if ((*i)->id() != exclude_id && (err = (*i)->send(relay_dg)) != 0) { log_debug << "transport: " << ::strerror(err); } } } void gcomm::GMCast::handle_up(const void* id, const Datagram& dg, const ProtoUpMeta& um) { ProtoMap::iterator i; if (listener_ == 0) { return; } if (id == listener_->id()) { gmcast_accept(); } else if (mcast_.get() != 0 && id == mcast_->id()) { Message msg; try { if (dg.offset() < dg.header_len()) { gu_trace(msg.unserialize(dg.header(), dg.header_size(), dg.header_offset() + dg.offset())); } else { gu_trace(msg.unserialize(&dg.payload()[0], dg.len(), dg.offset())); } } catch (gu::Exception& e) { GU_TRACE(e); log_warn << e.what(); return; } if (msg.type() >= Message::T_USER_BASE) { gu_trace(send_up(Datagram(dg, dg.offset() + msg.serial_size()), ProtoUpMeta(msg.source_uuid()))); } else { log_warn << "non-user message " << msg.type() << " from multicast socket"; } } else if ((i = proto_map_->find(id)) != proto_map_->end()) { Proto* p(ProtoMap::value(i)); if (dg.len() > 0) { const Proto::State prev_state(p->state()); if (prev_state == Proto::S_FAILED) { log_warn << "unhandled failed proto"; handle_failed(p); return; } Message msg; try { msg.unserialize(&dg.payload()[0], dg.len(), dg.offset()); } catch (gu::Exception& e) { GU_TRACE(e); log_warn << e.what(); p->set_state(Proto::S_FAILED); handle_failed(p); return; } if (msg.type() >= Message::T_USER_BASE) { if (msg.flags() & Message::F_RELAY) { relay(msg, Datagram(dg, dg.offset() + msg.serial_size()), id); } p->set_tstamp(gu::datetime::Date::now()); send_up(Datagram(dg, dg.offset() + msg.serial_size()), ProtoUpMeta(msg.source_uuid())); return; } else { try { p->set_tstamp(gu::datetime::Date::now()); gu_trace(p->handle_message(msg)); } catch (gu::Exception& e) { log_warn << "handling gmcast protocol message failed: " << e.what(); handle_failed(p); return; } if (p->state() == Proto::S_FAILED) { handle_failed(p); return; } else if (p->changed() == true) { update_addresses(); check_liveness(); reconnect(); } } if (prev_state != Proto::S_OK && p->state() == Proto::S_OK) { handle_established(p); } } else if (p->socket()->state() == Socket::S_CONNECTED && (p->state() == Proto::S_HANDSHAKE_WAIT || p->state() == Proto::S_INIT)) { handle_connected(p); } else if (p->socket()->state() == Socket::S_CONNECTED) { log_warn << "connection " << p->socket()->id() << " closed by peer"; p->set_state(Proto::S_FAILED); handle_failed(p); } else { log_debug << "socket in state " << p->socket()->state(); p->set_state(Proto::S_FAILED); handle_failed(p); } } else { // log_info << "proto entry " << id << " not found"; } } int gcomm::GMCast::handle_down(Datagram& dg, const ProtoDownMeta& dm) { Message msg(version_, Message::T_USER_BASE, uuid(), 1); gu_trace(push_header(msg, dg)); for (std::list::iterator i(mcast_tree_.begin()); i != mcast_tree_.end(); ++i) { bool relay(false); if (relay_set_.empty() == false && relay_set_.find(*i) != relay_set_.end()) { relay = true; gu_trace(pop_header(msg, dg)); msg.set_flags(msg.flags() | Message::F_RELAY); gu_trace(push_header(msg, dg)); } int err; if ((err = (*i)->send(dg)) != 0) { log_debug << "transport: " << ::strerror(err); } if (relay == true) { gu_trace(pop_header(msg, dg)); msg.set_flags(msg.flags() & ~Message::F_RELAY); gu_trace(push_header(msg, dg)); } } gu_trace(pop_header(msg, dg)); return 0; } void gcomm::GMCast::handle_stable_view(const View& view) { log_debug << "GMCast::handle_stable_view: " << view; if (view.type() == V_PRIM) { // discard addr list entries not in view std::set gmcast_lst; for (AddrList::const_iterator i(remote_addrs_.begin()); i != remote_addrs_.end(); ++i) { gmcast_lst.insert(i->second.uuid()); } std::set view_lst; for (NodeList::const_iterator i(view.members().begin()); i != view.members().end(); ++i) { view_lst.insert(i->first); } std::list diff; std::set_difference(gmcast_lst.begin(), gmcast_lst.end(), view_lst.begin(), view_lst.end(), std::back_inserter(diff)); for (std::list::const_iterator i(diff.begin()); i != diff.end(); ++i) { gmcast_forget(*i); } for (std::set::const_iterator i(view_lst.begin()); i != view_lst.end(); ++i) { AddrList::iterator ai; if ((ai = find_if(remote_addrs_.begin(), remote_addrs_.end(), AddrListUUIDCmp(*i))) != remote_addrs_.end()) { ai->second.set_retry_cnt(-1); ai->second.set_max_retries(max_retry_cnt_); } } // iterate over pending address list and discard entries without UUID for (AddrList::iterator i(pending_addrs_.begin()); i != pending_addrs_.end(); ) { AddrList::iterator i_next(i); ++i_next; const AddrEntry& ae(AddrList::value(i)); if (ae.uuid() == UUID()) { const std::string addr(AddrList::key(i)); log_info << "discarding pending addr without UUID: " << addr; for (ProtoMap::iterator pi(proto_map_->begin()); pi != proto_map_->end();) { ProtoMap::iterator pi_next(pi); ++pi_next; Proto* p(ProtoMap::value(pi)); if (p->remote_addr() == addr) { log_info << "discarding pending addr proto entry " << p; delete p; proto_map_->erase(pi); } pi = pi_next; } pending_addrs_.erase(i); } i = i_next; } } else if (view.type() == V_REG) { for (NodeList::const_iterator i(view.members().begin()); i != view.members().end(); ++i) { AddrList::iterator ai; if ((ai = find_if(remote_addrs_.begin(), remote_addrs_.end(), AddrListUUIDCmp(NodeList::key(i)))) != remote_addrs_.end()) { log_info << "declaring " << NodeList::key(i) << " stable"; ai->second.set_retry_cnt(-1); ai->second.set_max_retries(max_retry_cnt_); } } } check_liveness(); for (ProtoMap::const_iterator i(proto_map_->begin()); i != proto_map_->end(); ++i) { log_debug << "proto: " << *ProtoMap::value(i); } } void gcomm::GMCast::add_or_del_addr(const std::string& val) { if (val.compare(0, 4, "add:") == 0) { gu::URI uri(val.substr(4)); std::string addr(gu::net::resolve(uri_string(get_scheme(use_ssl_), uri.get_host(), uri.get_port())).to_string()); log_info << "inserting address '" << addr << "'"; insert_address(addr, UUID(), remote_addrs_); AddrList::iterator ai(remote_addrs_.find(addr)); AddrList::value(ai).set_max_retries( max_initial_reconnect_attempts_); AddrList::value(ai).set_retry_cnt(-1); } else if (val.compare(0, 4, "del:") == 0) { std::string addr(val.substr(4)); AddrList::iterator ai(remote_addrs_.find(addr)); if (ai != remote_addrs_.end()) { ProtoMap::iterator pi, pi_next; for (pi = proto_map_->begin(); pi != proto_map_->end(); pi = pi_next) { pi_next = pi, ++pi_next; Proto* rp = ProtoMap::value(pi); if (rp->remote_addr() == AddrList::key(ai)) { log_info << "deleting entry " << AddrList::key(ai); delete rp; proto_map_->erase(pi); } } AddrEntry& ae(AddrList::value(ai)); ae.set_max_retries(0); ae.set_retry_cnt(1); ae.set_next_reconnect(gu::datetime::Date::now() + time_wait_); update_addresses(); } else { log_info << "address '" << addr << "' not found from remote addrs list"; } } else { gu_throw_error(EINVAL) << "invalid addr spec '" << val << "'"; } } bool gcomm::GMCast::set_param(const std::string& key, const std::string& val) { if (key == Conf::GMCastMaxInitialReconnectAttempts) { max_initial_reconnect_attempts_ = gu::from_string(val); return true; } else if (key == Conf::GMCastPeerAddr) { try { add_or_del_addr(val); } catch (gu::NotFound& nf) { gu_throw_error(EINVAL) << "invalid addr spec '" << val << "'"; } catch (gu::NotSet& ns) { gu_throw_error(EINVAL) << "invalid addr spec '" << val << "'"; } return true; } else if (key == Conf::GMCastIsolate) { isolate_ = gu::from_string(val); log_info << "turning isolation " << (isolate_ == true ? "on" : "off"); if (isolate_ == true) { // delete all entries in proto map ProtoMap::iterator pi, pi_next; for (pi = proto_map_->begin(); pi != proto_map_->end(); pi = pi_next) { pi_next = pi, ++pi_next; Proto* rp = ProtoMap::value(pi); delete rp; proto_map_->erase(pi); } mcast_tree_.clear(); } return true; } else if (key == Conf::GMCastGroup || key == Conf::GMCastListenAddr || key == Conf::GMCastMCastAddr || key == Conf::GMCastMCastPort || key == Conf::GMCastMCastTTL || key == Conf::GMCastTimeWait || key == Conf::GMCastPeerTimeout) { gu_throw_error(EPERM) << "can't change value for '" << key << "' during runtime"; } return false; } percona-xtradb-cluster-galera/gcomm/src/gmcast.hpp0000644000000000000000000001515312247075736022537 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ /* * Generic multicast transport. Uses tcp connections if real multicast * is not available. */ #ifndef GCOMM_GMCAST_HPP #define GCOMM_GMCAST_HPP #include "gcomm/uuid.hpp" #include "gcomm/exception.hpp" #include "gcomm/transport.hpp" #include "gcomm/types.hpp" #include #ifndef GCOMM_GMCAST_MAX_VERSION #define GCOMM_GMCAST_MAX_VERSION 0 #endif // GCOMM_GMCAST_MAX_VERSION namespace gcomm { namespace gmcast { class Proto; class ProtoMap; class Node; class Message; } class GMCast : public Transport { public: GMCast (Protonet&, const gu::URI&); ~GMCast(); // Protolay interface void handle_up(const void*, const Datagram&, const ProtoUpMeta&); int handle_down(Datagram&, const ProtoDownMeta&); void handle_stable_view(const View& view); bool set_param(const std::string& key, const std::string& val); // Transport interface const UUID& uuid() const { return my_uuid_; } void connect(); void connect(const gu::URI&); void close(bool force = false); void close(const UUID& uuid) { gmcast_forget(uuid); } void listen() { gu_throw_fatal << "gmcast transport listen not implemented"; } std::string listen_addr() const { if (listener_ == 0) { gu_throw_error(ENOTCONN) << "not connected"; } return listener_->listen_addr(); } Transport* accept() { gu_throw_fatal << "gmcast transport accept not implemented"; } size_t mtu() const { return pnet_.mtu() - (4 + UUID::serial_size()); } private: GMCast (const GMCast&); GMCast& operator=(const GMCast&); static const long max_retry_cnt_; class AddrEntry { public: AddrEntry(const gu::datetime::Date& last_seen, const gu::datetime::Date& next_reconnect, const UUID& uuid) : uuid_ (uuid), last_seen_ (last_seen), next_reconnect_ (next_reconnect), retry_cnt_ (0), max_retries_ (0) { } const UUID& uuid() const { return uuid_; } void set_last_seen(const gu::datetime::Date& d) { last_seen_ = d; } const gu::datetime::Date& last_seen() const { return last_seen_; } void set_next_reconnect(const gu::datetime::Date& d) { next_reconnect_ = d; } const gu::datetime::Date& next_reconnect() const { return next_reconnect_; } void set_retry_cnt(const int r) { retry_cnt_ = r; } int retry_cnt() const { return retry_cnt_; } void set_max_retries(int mr) { max_retries_ = mr; } int max_retries() const { return max_retries_; } private: friend std::ostream& operator<<(std::ostream&, const AddrEntry&); void operator=(const AddrEntry&); UUID uuid_; gu::datetime::Date last_seen_; gu::datetime::Date next_reconnect_; int retry_cnt_; int max_retries_; }; typedef Map AddrList; class AddrListUUIDCmp { public: AddrListUUIDCmp(const UUID& uuid) : uuid_(uuid) { } bool operator()(const AddrList::value_type& cmp) const { return (cmp.second.uuid() == uuid_); } private: UUID uuid_; }; int version_; static const int max_version_ = GCOMM_GMCAST_MAX_VERSION; UUID my_uuid_; bool use_ssl_; std::string group_name_; std::string listen_addr_; std::set initial_addrs_; std::string mcast_addr_; std::string bind_ip_; int mcast_ttl_; Acceptor* listener_; SocketPtr mcast_; AddrList pending_addrs_; AddrList remote_addrs_; AddrList addr_blacklist_; bool relaying_; bool isolate_; gmcast::ProtoMap* proto_map_; std::list mcast_tree_; std::set relay_set_; gu::datetime::Period time_wait_; gu::datetime::Period check_period_; gu::datetime::Period peer_timeout_; int max_initial_reconnect_attempts_; gu::datetime::Date next_check_; gu::datetime::Date handle_timers(); // Accept new connection void gmcast_accept(); // Initialize connecting to remote host void gmcast_connect(const std::string&); // Forget node void gmcast_forget(const gcomm::UUID&); // Handle proto entry that has established connection to remote host void handle_connected(gmcast::Proto*); // Handle proto entry that has succesfully finished handshake // sequence void handle_established(gmcast::Proto*); // Handle proto entry that has failed void handle_failed(gmcast::Proto*); // Check if there exists connection that matches to either // remote addr or uuid bool is_connected(const std::string& addr, const UUID& uuid) const; // Inset address to address list void insert_address(const std::string& addr, const UUID& uuid, AddrList&); // Scan through proto entries and update address lists void update_addresses(); // void check_liveness(); void relay(const gmcast::Message& msg, const Datagram& dg, const void* exclude_id); // Reconnecting void reconnect(); void set_initial_addr(const gu::URI&); void add_or_del_addr(const std::string&); std::string self_string() const { std::ostringstream os; os << '(' << my_uuid_ << ", '" << listen_addr_ << "')"; return os.str(); } friend std::ostream& operator<<(std::ostream&, const AddrEntry&); }; inline std::ostream& operator<<(std::ostream& os, const GMCast::AddrEntry& ae) { return (os << ae.uuid_ << " last_seen=" << ae.last_seen_ << " next_reconnect=" << ae.next_reconnect_ << " retry_cnt=" << ae.retry_cnt_); } } #endif // GCOMM_GMCAST_HPP percona-xtradb-cluster-galera/gcomm/src/gmcast_link.hpp0000644000000000000000000000474512247075736023561 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #ifndef GCOMM_GMCAST_LINK_HPP #define GCOMM_GMCAST_LINK_HPP #include "gcomm/uuid.hpp" #include #include namespace gcomm { namespace gmcast { class Link; class LinkMapCmp; class LinkMap; std::ostream& operator<<(std::ostream& os, const LinkMap&); } } class gcomm::gmcast::Link { public: Link(const gcomm::UUID& uuid, const std::string& addr, const std::string& mcast_addr) : uuid_ (uuid), addr_ (addr), mcast_addr_(mcast_addr) { } bool operator==(const Link& cmp) const { return (uuid_ == cmp.uuid_ && addr_ == cmp.addr_); } bool operator<(const Link& cmp) const { return (uuid_ < cmp.uuid_ || (uuid_ == cmp.uuid_ && addr_ < cmp.addr_)); } const gcomm::UUID& uuid() const { return uuid_; } const std::string& addr() const { return addr_; } const std::string& mcast_addr() const { return mcast_addr_; } private: UUID uuid_; std::string addr_; std::string mcast_addr_; }; class gcomm::gmcast::LinkMap { typedef std::set MType; public: LinkMap() : link_map_() { } typedef MType::iterator iterator; typedef MType::const_iterator const_iterator; typedef MType::value_type value_type; std::pair insert(const Link& i) { return link_map_.insert(i); } iterator begin() { return link_map_.begin(); } const_iterator begin() const { return link_map_.begin(); } iterator end() { return link_map_.end(); } const_iterator end() const { return link_map_.end(); } const_iterator find(const value_type& vt) const { return link_map_.find(vt); } size_t size() const { return link_map_.size(); } static const UUID& key(const_iterator i) { return i->uuid(); } static const Link& value(const_iterator i) { return *i; } static const UUID& key(const value_type& vt) { return vt.uuid(); } static const Link& value(const value_type& vt) { return vt; } bool operator==(const LinkMap& cmp) const { return (link_map_ == cmp.link_map_); } private: MType link_map_; }; inline std::ostream& gcomm::gmcast::operator<<(std::ostream& os, const LinkMap& lm) { for (LinkMap::const_iterator i = lm.begin(); i != lm.end(); ++i) { os << "\n(" << LinkMap::key(i) << "," << LinkMap::value(i).addr() << ")"; } return (os << "\n"); } #endif // GCOMM_GMCAST_LINK_HPP percona-xtradb-cluster-galera/gcomm/src/gmcast_message.hpp0000644000000000000000000002251512247075736024243 0ustar rootroot00000000000000/* * Copyright (C) 2009-2012 Codership Oy */ #ifndef GCOMM_GMCAST_MESSAGE_HPP #define GCOMM_GMCAST_MESSAGE_HPP #include "gcomm/types.hpp" #include "gcomm/uuid.hpp" #include "gmcast_node.hpp" #include "gcomm/map.hpp" namespace gcomm { namespace gmcast { class Message; } } class gcomm::gmcast::Message { public: enum Flags { F_GROUP_NAME = 1 << 0, F_NODE_NAME = 1 << 1, F_NODE_ADDRESS = 1 << 2, F_NODE_LIST = 1 << 3, F_HANDSHAKE_UUID = 1 << 4, F_RELAY = 1 << 5 }; enum Type { T_INVALID = 0, T_HANDSHAKE = 1, T_HANDSHAKE_RESPONSE = 2, T_HANDSHAKE_OK = 3, T_HANDSHAKE_FAIL = 4, T_TOPOLOGY_CHANGE = 5, /* Leave room for future use */ T_USER_BASE = 8, T_MAX = 255 }; class NodeList : public Map { }; private: gu::byte_t version_; Type type_; gu::byte_t flags_; gu::byte_t segment_id_; gcomm::UUID handshake_uuid_; gcomm::UUID source_uuid_; gcomm::String<64> node_address_; gcomm::String<32> group_name_; Message& operator=(const Message&); NodeList node_list_; public: static const char* type_to_string (Type t) { static const char* str[T_MAX] = { "INVALID", "HANDSHAKE", "HANDSHAKE_RESPONSE", "HANDSHAKE_OK", "HANDSHAKE_FAIL", "TOPOLOGY_CHANGE", "RESERVED_6", "RESERVED_7", "USER_BASE" }; if (T_MAX > t) return str[t]; return "UNDEFINED PACKET TYPE"; } Message(const Message& msg) : version_ (msg.version_), type_ (msg.type_), flags_ (msg.flags_), segment_id_ (msg.segment_id_), handshake_uuid_ (msg.handshake_uuid_), source_uuid_ (msg.source_uuid_), node_address_ (msg.node_address_), group_name_ (msg.group_name_), node_list_ (msg.node_list_) { } /* Default ctor */ Message () : version_ (0), type_ (T_INVALID), flags_ (0), segment_id_ (0), handshake_uuid_ (), source_uuid_ (), node_address_ (), group_name_ (), node_list_ () {} /* Ctor for handshake, handshake ok and handshake fail */ Message (int v, const Type type, const UUID& handshake_uuid, const UUID& source_uuid) : version_ (v), type_ (type), flags_ (F_HANDSHAKE_UUID), segment_id_ (0), handshake_uuid_ (handshake_uuid), source_uuid_ (source_uuid), node_address_ (), group_name_ (), node_list_ () { if (type_ != T_HANDSHAKE && type_ != T_HANDSHAKE_OK && type_ != T_HANDSHAKE_FAIL) gu_throw_fatal << "Invalid message type " << type_to_string(type_) << " in handshake constructor"; } /* Ctor for user message */ Message (int v, const Type type, const UUID& source_uuid, const int ttl) : version_ (v), type_ (type), flags_ (0), segment_id_ (0), handshake_uuid_ (), source_uuid_ (source_uuid), node_address_ (), group_name_ (), node_list_ () { if (type_ < T_USER_BASE) gu_throw_fatal << "Invalid message type " << type_to_string(type_) << " in user message constructor"; } /* Ctor for handshake response */ Message (int v, const Type type, const gcomm::UUID& handshake_uuid, const gcomm::UUID& source_uuid, const std::string& node_address, const std::string& group_name) : version_ (v), type_ (type), flags_ (F_GROUP_NAME | F_NODE_ADDRESS | F_HANDSHAKE_UUID), segment_id_ (0), handshake_uuid_ (handshake_uuid), source_uuid_ (source_uuid), node_address_ (node_address), group_name_ (group_name), node_list_ () { if (type_ != T_HANDSHAKE_RESPONSE) gu_throw_fatal << "Invalid message type " << type_to_string(type_) << " in handshake response constructor"; } /* Ctor for topology change */ Message (int v, const Type type, const gcomm::UUID& source_uuid, const std::string& group_name, const NodeList& nodes) : version_ (v), type_ (type), flags_ (F_GROUP_NAME | F_NODE_LIST), segment_id_ (0), handshake_uuid_ (), source_uuid_ (source_uuid), node_address_ (), group_name_ (group_name), node_list_ (nodes) { if (type_ != T_TOPOLOGY_CHANGE) gu_throw_fatal << "Invalid message type " << type_to_string(type_) << " in topology change constructor"; } ~Message() { } size_t serialize(gu::byte_t* buf, const size_t buflen, const size_t offset) const { size_t off; gu_trace (off = gu::serialize1(version_, buf, buflen, offset)); gu_trace (off = gu::serialize1(static_cast(type_),buf,buflen,off)); gu_trace (off = gu::serialize1(flags_, buf, buflen, off)); gu_trace (off = gu::serialize1(segment_id_, buf, buflen, off)); gu_trace (off = source_uuid_.serialize(buf, buflen, off)); if (flags_ & F_HANDSHAKE_UUID) { gu_trace(off = handshake_uuid_.serialize(buf, buflen, off)); } if (flags_ & F_NODE_ADDRESS) { gu_trace (off = node_address_.serialize(buf, buflen, off)); } if (flags_ & F_GROUP_NAME) { gu_trace (off = group_name_.serialize(buf, buflen, off)); } if (flags_ & F_NODE_LIST) { gu_trace(off = node_list_.serialize(buf, buflen, off)); } return off; } size_t read_v0(const gu::byte_t* buf, const size_t buflen, const size_t offset) { size_t off; gu::byte_t t; gu_trace (off = gu::unserialize1(buf, buflen, offset, t)); type_ = static_cast(t); switch (type_) { case T_HANDSHAKE: case T_HANDSHAKE_RESPONSE: case T_HANDSHAKE_OK: case T_HANDSHAKE_FAIL: case T_TOPOLOGY_CHANGE: case T_USER_BASE: break; default: gu_throw_error(EINVAL) << "invalid message type " << static_cast(type_); } gu_trace (off = gu::unserialize1(buf, buflen, off, flags_)); gu_trace (off = gu::unserialize1(buf, buflen, off, segment_id_)); gu_trace (off = source_uuid_.unserialize(buf, buflen, off)); if (flags_ & F_HANDSHAKE_UUID) { gu_trace(off = handshake_uuid_.unserialize(buf, buflen, off)); } if (flags_ & F_NODE_ADDRESS) { gu_trace (off = node_address_.unserialize(buf, buflen, off)); } if (flags_ & F_GROUP_NAME) { gu_trace (off = group_name_.unserialize(buf, buflen, off)); } if (flags_ & F_NODE_LIST) { gu_trace(off = node_list_.unserialize(buf, buflen, off)); } return off; } size_t unserialize(const gu::byte_t* buf, const size_t buflen, const size_t offset) { size_t off; gu_trace (off = gu::unserialize1(buf, buflen, offset, version_)); switch (version_) { case 0: gu_trace (return read_v0(buf, buflen, off)); default: gu_throw_error(EPROTONOSUPPORT) << "Unsupported/unrecognized gmcast protocol version: " << version_; } } size_t serial_size() const { return 4 /* Common header: version, type, flags, segment_id */ + source_uuid_.serial_size() + (flags_ & F_HANDSHAKE_UUID ? handshake_uuid_.serial_size() : 0) /* GMCast address if set */ + (flags_ & F_NODE_ADDRESS ? node_address_.serial_size() : 0) /* Group name if set */ + (flags_ & F_GROUP_NAME ? group_name_.serial_size() : 0) /* Node list if set */ + (flags_ & F_NODE_LIST ? node_list_.serial_size() : 0); } int version() const { return version_; } Type type() const { return type_; } void set_flags(uint8_t f) { flags_ = f; } uint8_t flags() const { return flags_; } const UUID& handshake_uuid() const { return handshake_uuid_; } const UUID& source_uuid() const { return source_uuid_; } const std::string& node_address() const { return node_address_.to_string(); } const std::string& group_name() const { return group_name_.to_string(); } const NodeList& node_list() const { return node_list_; } }; #endif // GCOMM_GMCAST_MESSAGE_HPP percona-xtradb-cluster-galera/gcomm/src/gmcast_node.hpp0000644000000000000000000000323312247075736023540 0ustar rootroot00000000000000/* * Copyright (C) 2009-2012 Codership Oy */ #ifndef GMCAST_NODE_HPP #define GMCAST_NODE_HPP #include "gcomm/types.hpp" #include "gcomm/uuid.hpp" #include "gu_serialize.hpp" namespace gcomm { namespace gmcast { class Node; std::ostream& operator<<(std::ostream&, const Node&); } } class gcomm::gmcast::Node { public: Node(const std::string& addr = "") : addr_(addr), mcast_addr_("") { } const std::string& addr() const { return addr_.to_string(); } const std::string& mcast_addr() const { return mcast_addr_.to_string(); } size_t unserialize(const gu::byte_t* buf, const size_t buflen, const size_t offset) { size_t off; uint32_t bits; gu_trace (off = gu::unserialize4(buf, buflen, offset, bits)); gu_trace (off = addr_.unserialize(buf, buflen, off)); gu_trace (off = mcast_addr_.unserialize(buf, buflen, off)); return off; } size_t serialize(gu::byte_t* buf, const size_t buflen, const size_t offset) const { size_t off; uint32_t bits(0); gu_trace (off = gu::serialize4(bits, buf, buflen, offset)); gu_trace (off = addr_.serialize(buf, buflen, off)); gu_trace (off = mcast_addr_.serialize(buf, buflen, off)); return off; } static size_t serial_size() { return (4 + 2 * ADDR_SIZE); } private: static const size_t ADDR_SIZE = 64; gcomm::String addr_; gcomm::String mcast_addr_; }; inline std::ostream& gcomm::gmcast::operator<<(std::ostream& os, const Node& n) { return os; } #endif // GMCAST_NODE_HPP percona-xtradb-cluster-galera/gcomm/src/gmcast_proto.cpp0000644000000000000000000001433312247075736023754 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #include "gmcast_proto.hpp" #include "gu_uri.hpp" using std::rel_ops::operator!=; void gcomm::gmcast::Proto:: set_state(State new_state) { log_debug << "State change: " << to_string(state_) << " -> " << to_string(new_state); static const bool allowed[][7] = { // INIT HS_SENT HS_WAIT HSR_SENT OK FAILED CLOSED { false, true, true, false, false, true, false },// INIT { false, false, false, false, true, true, false },// HS_SENT { false, false, false, true, false, true, false },// HS_WAIT { false, false, false, false, true, true, false },// HSR_SENT { false, false, false, false, false, true, true },// OK { false, false, false, false, false, true, true },// FAILED { false, false, false, false, false, false, false } // CLOSED }; if (!allowed[state_][new_state]) { gu_throw_fatal << "Invalid state change: " << to_string(state_) << " -> " << to_string(new_state); } state_ = new_state; } void gcomm::gmcast::Proto::send_msg(const Message& msg) { gu::Buffer buf; gu_trace(serialize(msg, buf)); Datagram dg(buf); int ret = tp_->send(dg); // @todo: This can happen during congestion, figure out how to // avoid terminating connection with topology change messages. if (ret != 0) { log_debug << "Send failed: " << strerror(ret); set_state(S_FAILED); } } void gcomm::gmcast::Proto::send_handshake() { handshake_uuid_ = UUID(0, 0); Message hs (version_, Message::T_HANDSHAKE, handshake_uuid_, local_uuid_); send_msg(hs); set_state(S_HANDSHAKE_SENT); } void gcomm::gmcast::Proto::wait_handshake() { if (state() != S_INIT) gu_throw_fatal << "Invalid state: " << to_string(state()); set_state(S_HANDSHAKE_WAIT); } void gcomm::gmcast::Proto::handle_handshake(const Message& hs) { if (state() != S_HANDSHAKE_WAIT) gu_throw_fatal << "Invalid state: " << to_string(state()); if (hs.version() != version_) { log_warn << "incompatible protocol version: " << hs.version(); set_state(S_FAILED); return; } handshake_uuid_ = hs.handshake_uuid(); remote_uuid_ = hs.source_uuid(); Message hsr (version_, Message::T_HANDSHAKE_RESPONSE, handshake_uuid_, local_uuid_, local_addr_, group_name_); send_msg(hsr); set_state(S_HANDSHAKE_RESPONSE_SENT); } void gcomm::gmcast::Proto::handle_handshake_response(const Message& hs) { if (state() != S_HANDSHAKE_SENT) gu_throw_fatal << "Invalid state: " << to_string(state()); const std::string& grp = hs.group_name(); try { if (grp != group_name_) { log_info << "handshake failed, my group: '" << group_name_ << "', peer group: '" << grp << "'"; Message failed(version_, Message::T_HANDSHAKE_FAIL, handshake_uuid_, local_uuid_); send_msg(failed); set_state(S_FAILED); return; } remote_uuid_ = hs.source_uuid(); gu::URI remote_uri(tp_->remote_addr()); remote_addr_ = uri_string(remote_uri.get_scheme(), remote_uri.get_host(), gu::URI(hs.node_address()).get_port()); propagate_remote_ = true; Message ok(version_, Message::T_HANDSHAKE_OK, handshake_uuid_, local_uuid_); send_msg(ok); set_state(S_OK); } catch (std::exception& e) { log_warn << "Parsing peer address '" << hs.node_address() << "' failed: " << e.what(); Message nok (version_, Message::T_HANDSHAKE_FAIL, handshake_uuid_, local_uuid_); send_msg (nok); set_state(S_FAILED); } } void gcomm::gmcast::Proto::handle_ok(const Message& hs) { propagate_remote_ = true; set_state(S_OK); } void gcomm::gmcast::Proto::handle_failed(const Message& hs) { set_state(S_FAILED); } void gcomm::gmcast::Proto::handle_topology_change(const Message& msg) { const Message::NodeList& nl(msg.node_list()); LinkMap new_map; for (Message::NodeList::const_iterator i = nl.begin(); i != nl.end(); ++i) { new_map.insert(Link(Message::NodeList::key(i), Message::NodeList::value(i).addr(), Message::NodeList::value(i).mcast_addr())); if (Message::NodeList::key(i) == remote_uuid() && mcast_addr_ == "" && Message::NodeList::value(i).mcast_addr() != "") { mcast_addr_ = Message::NodeList::value(i).mcast_addr(); } } if (link_map_ != new_map) { changed_ = true; } link_map_ = new_map; } void gcomm::gmcast::Proto::send_topology_change(LinkMap& um) { Message::NodeList nl; for (LinkMap::const_iterator i = um.begin(); i != um.end(); ++i) { if (LinkMap::key(i) == UUID::nil() || LinkMap::value(i).addr() == "") gu_throw_fatal << "nil uuid or empty address"; nl.insert_unique( std::make_pair(LinkMap::key(i), Node(LinkMap::value(i).addr()))); } Message msg(version_, Message::T_TOPOLOGY_CHANGE, local_uuid_, group_name_, nl); send_msg(msg); } void gcomm::gmcast::Proto::handle_message(const Message& msg) { switch (msg.type()) { case Message::T_HANDSHAKE: handle_handshake(msg); break; case Message::T_HANDSHAKE_RESPONSE: handle_handshake_response(msg); break; case Message::T_HANDSHAKE_OK: handle_ok(msg); break; case Message::T_HANDSHAKE_FAIL: handle_failed(msg); break; case Message::T_TOPOLOGY_CHANGE: handle_topology_change(msg); break; default: gu_throw_fatal << "invalid message type: " << msg.type(); } } percona-xtradb-cluster-galera/gcomm/src/gmcast_proto.hpp0000644000000000000000000001066312247075736023763 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #include "gu_datetime.hpp" #include "gcomm/uuid.hpp" #include "gcomm/util.hpp" #include "socket.hpp" #include "gmcast_message.hpp" #include "gmcast_link.hpp" namespace gcomm { namespace gmcast { class Proto; class ProtoMap; std::ostream& operator<<(std::ostream& os, const Proto& p); } } class gcomm::gmcast::Proto { public: enum State { S_INIT, S_HANDSHAKE_SENT, S_HANDSHAKE_WAIT, S_HANDSHAKE_RESPONSE_SENT, S_OK, S_FAILED, S_CLOSED }; public: void set_state(State new_state); State state() const { return state_; } static std::string to_string (State s) { switch (s) { case S_INIT: return "INIT"; case S_HANDSHAKE_SENT: return "HANDSHAKE_SENT"; case S_HANDSHAKE_WAIT: return "HANDSHAKE_WAIT"; case S_HANDSHAKE_RESPONSE_SENT: return "HANDSHAKE_RESPONSE_SENT"; case S_OK: return "OK"; case S_FAILED: return "FAILED"; case S_CLOSED: return "CLOSED"; default: return "UNKNOWN"; } } Proto (int v, SocketPtr tp, const std::string& local_addr, const std::string& remote_addr, const std::string& mcast_addr, const gcomm::UUID& local_uuid, const std::string& group_name) : version_(v), handshake_uuid_ (), local_uuid_ (local_uuid), remote_uuid_ (), local_addr_ (local_addr), remote_addr_ (remote_addr), mcast_addr_ (mcast_addr), group_name_ (group_name), changed_ (false), state_ (S_INIT), propagate_remote_ (false), tp_ (tp), link_map_ (), tstamp_ (gu::datetime::Date::now()) { } ~Proto() { tp_->close(); } void send_msg(const Message& msg); void send_handshake(); void wait_handshake(); void handle_handshake(const Message& hs); void handle_handshake_response(const Message& hs); void handle_ok(const Message& hs); void handle_failed(const Message& hs); void handle_topology_change(const Message& msg); void send_topology_change(LinkMap& um); void handle_message(const Message& msg); const gcomm::UUID& handshake_uuid() const { return handshake_uuid_; } const gcomm::UUID& local_uuid() const { return local_uuid_; } const gcomm::UUID& remote_uuid() const { return remote_uuid_; } SocketPtr socket() const { return tp_; } const std::string& remote_addr() const { return remote_addr_; } const std::string& mcast_addr() const { return mcast_addr_; } const LinkMap& link_map() const { return link_map_; } bool changed() { bool ret = changed_; changed_ = false; return ret; } int version() const { return version_; } void set_tstamp(gu::datetime::Date ts) { tstamp_ = ts; } gu::datetime::Date tstamp() const { return tstamp_; } private: friend std::ostream& operator<<(std::ostream&, const Proto&); Proto(const Proto&); void operator=(const Proto&); int version_; gcomm::UUID handshake_uuid_; gcomm::UUID local_uuid_; // @todo: do we need it here? gcomm::UUID remote_uuid_; std::string local_addr_; std::string remote_addr_; std::string mcast_addr_; std::string group_name_; bool changed_; State state_; bool propagate_remote_; SocketPtr tp_; LinkMap link_map_; gu::datetime::Date tstamp_; }; inline std::ostream& gcomm::gmcast::operator<<(std::ostream& os, const Proto& p) { os << "v=" << p.version_ << "," << "lu=" << p.local_uuid_ << "," << "ru=" << p.remote_uuid_ << "," << "la=" << p.local_addr_ << "," << "ra=" << p.remote_addr_ << "," << "mc=" << p.mcast_addr_ << "," << "gn=" << p.group_name_ << "," << "ch=" << p.changed_ << "," << "st=" << gcomm::gmcast::Proto::to_string(p.state_) << "," << "pr=" << p.propagate_remote_ << "," << "tp=" << p.tp_ << "," << "ts=" << p.tstamp_; return os; } class gcomm::gmcast::ProtoMap : public Map { }; percona-xtradb-cluster-galera/gcomm/src/histogram.cpp0000644000000000000000000000414412247075736023247 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #include "histogram.hpp" #include "gcomm/exception.hpp" #include "gcomm/types.hpp" #include "gu_logger.hpp" #include "gu_string.hpp" #include #include #include gcomm::Histogram::Histogram(const std::string& vals) : cnt_() { std::vector varr = gu::strsplit(vals, ','); for (std::vector::const_iterator i = varr.begin(); i != varr.end(); ++i) { double val; std::istringstream is(*i); is >> val; if (is.fail()) { gu_throw_fatal << "Parse error"; } if (cnt_.insert(std::make_pair(val, 0)).second == false) { gu_throw_fatal << "Failed to insert value: " << val; } } if (cnt_.insert( std::make_pair( std::numeric_limits::max(), 0)).second == false) { gu_throw_fatal << "Failed to insert numeric_limits::max()"; } } void gcomm::Histogram::insert(const double val) { if (val < 0.0) { log_warn << "Negative value (" << val << "), discarding"; return; } std::map::iterator i = cnt_.lower_bound(val); if (i == cnt_.end()) { gu_throw_fatal; } i->second++; } void gcomm::Histogram::clear() { for (std::map::iterator i = cnt_.begin(); i != cnt_.end(); ++i) { i->second = 0; } } std::ostream& gcomm::operator<<(std::ostream& os, const Histogram& hs) { std::map::const_iterator i, i_next; long long norm = 0; for (i = hs.cnt_.begin(); i != hs.cnt_.end(); ++i) { norm += i->second; } for (i = hs.cnt_.begin(); i != hs.cnt_.end(); i = i_next) { i_next = i; ++i_next; if (i_next == hs.cnt_.end()) break; os << i->first << " -> " << i_next->first << ": " << 100.*double(i_next->second + (i == hs.cnt_.begin() ? i->second : 0))/double(norm) << " "; } os << "total: " << norm; return os; } percona-xtradb-cluster-galera/gcomm/src/histogram.hpp0000644000000000000000000000103712247075736023252 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #ifndef GCOMM_HISTOGRAM_HPP #define GCOMM_HISTOGRAM_HPP #include #include namespace gcomm { class Histogram { public: Histogram(const std::string&); void insert(const double); void clear(); friend std::ostream& operator<<(std::ostream&, const Histogram&); private: std::map cnt_; }; std::ostream& operator<<(std::ostream&, const Histogram&); } #endif // GCOMM_HISTOGRAM_HPP percona-xtradb-cluster-galera/gcomm/src/pc.cpp0000644000000000000000000001367312247075736021663 0ustar rootroot00000000000000/* * Copyright (C) 2009-2012 Codership Oy */ #include "pc.hpp" #include "pc_proto.hpp" #include "evs_proto.hpp" #include "evs_message2.hpp" #include "gmcast.hpp" #include "defaults.hpp" #include "gcomm/conf.hpp" #include "gcomm/util.hpp" #include "gu_datetime.hpp" void gcomm::PC::handle_up(const void* cid, const Datagram& rb, const ProtoUpMeta& um) { send_up(rb, um); } int gcomm::PC::handle_down(Datagram& wb, const ProtoDownMeta& dm) { if (wb.len() == 0) { gu_throw_error(EMSGSIZE); } return send_down(wb, dm); } size_t gcomm::PC::mtu() const { // TODO: if (gmcast_ == 0) gu_throw_fatal << "not open"; evs::UserMessage evsm; pc::UserMessage pcm(0, 0); if (gmcast_->mtu() < 2*evsm.serial_size() + pcm.serial_size()) { gu_throw_fatal << "transport max msg size too small: " << gmcast_->mtu(); } return gmcast_->mtu() - 2*evsm.serial_size() - pcm.serial_size(); } const gcomm::UUID& gcomm::PC::uuid() const { return gmcast_->uuid(); } std::string gcomm::PC::listen_addr() const { return gmcast_->listen_addr(); } void gcomm::PC::connect(bool start_prim) { try { // for backward compatibility with old approach: gcomm://0.0.0.0 start_prim = (start_prim || host_is_any (uri_.get_host())); } catch (gu::NotSet& ns) { start_prim = true; } const bool wait_prim( gu::from_string( uri_.get_option(Conf::PcWaitPrim, Defaults::PcWaitPrim))); const gu::datetime::Period wait_prim_timeout( gu::from_string( uri_.get_option(Conf::PcWaitPrimTimeout, Defaults::PcWaitPrimTimeout))); pstack_.push_proto(gmcast_); pstack_.push_proto(evs_); pstack_.push_proto(pc_); pstack_.push_proto(this); pnet().insert(&pstack_); gmcast_->connect(); closed_ = false; evs_->shift_to(evs::Proto::S_JOINING); pc_->connect(start_prim); // Due to #658 there is limited announce period after which // node is allowed to proceed to non-prim if other nodes // are not detected. gu::datetime::Date try_until(gu::datetime::Date::now() + announce_timeout_); while (start_prim == false && evs_->known_size() <= 1) { // Send join messages without handling them evs_->send_join(false); pnet().event_loop(gu::datetime::Sec/2); if (try_until < gu::datetime::Date::now()) { break; } } log_debug << "PC/EVS Proto initial state: " << *evs_; if (evs_->state() != evs::Proto::S_OPERATIONAL) { log_debug << "PC/EVS Proto sending join request"; evs_->send_join(); } gcomm_assert(evs_->state() == evs::Proto::S_GATHER || evs_->state() == evs::Proto::S_INSTALL || evs_->state() == evs::Proto::S_OPERATIONAL); // - Due to #658 we loop here only if node is told to start in prim. // - Fix for #680, bypass waiting prim only if explicitly required try_until = gu::datetime::Date::now() + wait_prim_timeout; while ((wait_prim == true || start_prim == true) && pc_->state() != pc::Proto::S_PRIM) { pnet().event_loop(gu::datetime::Sec/2); if (try_until < gu::datetime::Date::now()) { pc_->close(); evs_->close(); gmcast_->close(); pnet().erase(&pstack_); pstack_.pop_proto(this); pstack_.pop_proto(pc_); pstack_.pop_proto(evs_); pstack_.pop_proto(gmcast_); gu_throw_error(ETIMEDOUT) << "failed to reach primary view"; } } pc_->set_mtu(mtu()); } void gcomm::PC::connect(const gu::URI& uri) { uri_ = uri; connect(); } void gcomm::PC::close(bool force) { if (force == false) { log_debug << "PC/EVS Proto leaving"; pc_->close(); evs_->close(); gu::datetime::Date wait_until(gu::datetime::Date::now() + linger_); do { pnet().event_loop(gu::datetime::Sec/2); } while (evs_->state() != evs::Proto::S_CLOSED && gu::datetime::Date::now() < wait_until); if (evs_->state() != evs::Proto::S_CLOSED) { evs_->shift_to(evs::Proto::S_CLOSED); } if (pc_->state() != pc::Proto::S_CLOSED) { log_warn << "PCProto didn't reach closed state"; } gmcast_->close(); } else { log_info << "Forced PC close"; } pnet().erase(&pstack_); pstack_.pop_proto(this); pstack_.pop_proto(pc_); pstack_.pop_proto(evs_); pstack_.pop_proto(gmcast_); closed_ = true; } gcomm::PC::PC(Protonet& net, const gu::URI& uri) : Transport (net, uri), gmcast_ (0), evs_ (0), pc_ (0), closed_ (true), linger_ (param( conf_, uri, Conf::PcLinger, "PT20S")), announce_timeout_(param( conf_, uri, Conf::PcAnnounceTimeout, Defaults::PcAnnounceTimeout)) { if (uri_.get_scheme() != Conf::PcScheme) { log_fatal << "invalid uri: " << uri_.to_string(); } gmcast_ = new GMCast(pnet(), uri_); const UUID& uuid(gmcast_->uuid()); if (uuid == UUID::nil()) { gu_throw_fatal << "invalid UUID: " << uuid; } evs::UserMessage evsum; evs_ = new evs::Proto(pnet().conf(), uuid, uri_, gmcast_->mtu() - 2*evsum.serial_size()); pc_ = new pc::Proto (pnet().conf(), uuid, uri_); conf_.set(Conf::PcLinger, gu::to_string(linger_)); } gcomm::PC::~PC() { if (!closed_) { try { close(); } catch (...) { } sleep(1); // half-hearted attempt to avoid race with client threads } delete gmcast_; delete evs_; delete pc_; } percona-xtradb-cluster-galera/gcomm/src/pc.hpp0000644000000000000000000000233412247075736021660 0ustar rootroot00000000000000/* * Copyright (C) 2009-2012 Codership Oy */ #include "gcomm/transport.hpp" namespace gcomm { class GMCast; namespace evs { class Proto; } // namespace gcomm namespace pc { class Proto; } class PC : public Transport { public: PC (Protonet&, const gu::URI&); ~PC(); void connect(bool start_prim = false); void connect(const gu::URI&); std::string listen_addr() const; void close(bool force = false); void handle_up(const void*, const Datagram&, const ProtoUpMeta&); int handle_down(Datagram&, const ProtoDownMeta&); const UUID& uuid() const; size_t mtu() const; private: GMCast* gmcast_; // GMCast transport evs::Proto* evs_; // EVS protocol layer pc::Proto* pc_; // PC protocol layer bool closed_; // flag for destructor // Period to wait graceful leave gu::datetime::Period linger_; gu::datetime::Period announce_timeout_; PC(const PC&); void operator=(const PC&); }; } // namespace gcomm percona-xtradb-cluster-galera/gcomm/src/pc_message.hpp0000644000000000000000000002360012247075736023363 0ustar rootroot00000000000000/* * Copyright (C) 2009-2012 Codership Oy */ #ifndef PC_MESSAGE_HPP #define PC_MESSAGE_HPP #include "gcomm/view.hpp" #include "gcomm/types.hpp" #include "gcomm/uuid.hpp" #include "gcomm/map.hpp" #include "gu_serialize.hpp" #include namespace gcomm { namespace pc { class Node; class NodeMap; class Message; class UserMessage; class StateMessage; class InstallMessage; std::ostream& operator<<(std::ostream&, const Node&); std::ostream& operator<<(std::ostream&, const Message&); bool operator==(const Message&, const Message&); } } class gcomm::pc::Node { public: enum Flags { F_PRIM = 0x1, F_WEIGHT = 0x2, F_UN = 0x4 }; Node(const bool prim = false, const bool un = false, const uint32_t last_seq = std::numeric_limits::max(), const ViewId& last_prim = ViewId(V_NON_PRIM), const int64_t to_seq = -1, const int weight = -1) : prim_ (prim ), un_ (un ), last_seq_ (last_seq ), last_prim_ (last_prim), to_seq_ (to_seq ), weight_ (weight) { } void set_prim (const bool val) { prim_ = val ; } void set_un (const bool un) { un_ = un ; } void set_last_seq (const uint32_t seq) { last_seq_ = seq ; } void set_last_prim (const ViewId& last_prim) { last_prim_ = last_prim; } void set_to_seq (const uint64_t seq) { to_seq_ = seq ; } void set_weight (const int weight) { weight_ = weight ; } bool prim() const { return prim_ ; } bool un() const { return un_ ; } uint32_t last_seq() const { return last_seq_ ; } const ViewId& last_prim() const { return last_prim_; } int64_t to_seq() const { return to_seq_ ; } int weight() const { return weight_ ; } size_t unserialize(const gu::byte_t* buf, const size_t buflen, const size_t offset) { size_t off = offset; uint32_t flags; gu_trace (off = gu::unserialize4(buf, buflen, off, flags)); prim_ = flags & F_PRIM; un_ = flags & F_UN; if (flags & F_WEIGHT) { weight_ = flags >> 24; } else { weight_ = -1; } gu_trace (off = gu::unserialize4(buf, buflen, off, last_seq_)); gu_trace (off = last_prim_.unserialize(buf, buflen, off)); gu_trace (off = gu::unserialize8(buf, buflen, off, to_seq_)); return off; } size_t serialize(gu::byte_t* buf, const size_t buflen, const size_t offset) const { size_t off = offset; uint32_t flags = 0; flags |= prim_ ? F_PRIM : 0; flags |= un_ ? F_UN : 0; if (weight_ >= 0) { flags |= F_WEIGHT; flags |= weight_ << 24; } gu_trace (off = gu::serialize4(flags, buf, buflen, off)); gu_trace (off = gu::serialize4(last_seq_, buf, buflen, off)); gu_trace (off = last_prim_.serialize(buf, buflen, off)); gu_trace (off = gu::serialize8(to_seq_, buf, buflen, off)); assert (serial_size() == (off - offset)); return off; } static size_t serial_size() { Node* node(reinterpret_cast(0)); // flags return (sizeof(uint32_t) + sizeof(node->last_seq_) + ViewId::serial_size() + sizeof(node->to_seq_)); } bool operator==(const Node& cmp) const { return (prim() == cmp.prim() && un() == cmp.un() && last_seq() == cmp.last_seq() && last_prim() == cmp.last_prim() && to_seq() == cmp.to_seq() && weight() == cmp.weight() ); } std::string to_string() const { std::ostringstream ret; ret << "prim=" << prim_ << ",un=" << un_ << ",last_seq=" << last_seq_ << ",last_prim=" << last_prim_ << ",to_seq=" << to_seq_ << ",weight=" << weight_; return ret.str(); } private: bool prim_; // Is node in prim comp bool un_; // The prim status of the node is unknown uint32_t last_seq_; // Last seen message seq from the node ViewId last_prim_; // Last known prim comp view id for the node int64_t to_seq_; // Last known TO seq for the node int weight_; // Node weight }; inline std::ostream& gcomm::pc::operator<<(std::ostream& os, const Node& n) { return (os << n.to_string()); } class gcomm::pc::NodeMap : public Map { }; class gcomm::pc::Message { public: enum Type {T_NONE, T_STATE, T_INSTALL, T_USER, T_MAX}; enum { F_CRC16 = 0x1, F_BOOTSTRAP = 0x2, F_WEIGHT_CHANGE = 0x4 }; static const char* to_string(Type t) { static const char* str[T_MAX] = { "NONE", "STATE", "INSTALL", "USER" }; if (t < T_MAX) return str[t]; return "unknown"; } Message(const int version = -1, const Type type = T_NONE, const uint32_t seq = 0, const NodeMap& node_map = NodeMap()) : version_ (version ), flags_ (0 ), type_ (type ), seq_ (seq ), crc16_ (0 ), node_map_(node_map) { } Message(const Message& msg) : version_ (msg.version_ ), flags_ (msg.flags_ ), type_ (msg.type_ ), seq_ (msg.seq_ ), crc16_ (msg.crc16_ ), node_map_(msg.node_map_) { } virtual ~Message() { } int version() const { return version_; } Type type() const { return type_; } uint32_t seq() const { return seq_; } void flags(int flags) { flags_ = flags; } int flags() const { return flags_; } void checksum(uint16_t crc16, bool flag) { crc16_ = crc16; if (flag == true) { flags_ |= F_CRC16; } else { flags_ &= ~F_CRC16; } } uint16_t checksum() const { return crc16_; } const NodeMap& node_map() const { return node_map_; } NodeMap& node_map() { return node_map_; } const Node& node(const UUID& uuid) const { return NodeMap::value(node_map_.find_checked(uuid)); } Node& node(const UUID& uuid) { return NodeMap::value(node_map_.find_checked(uuid)); } size_t unserialize(const gu::byte_t* buf, const size_t buflen, const size_t offset) { size_t off; uint32_t b; node_map_.clear(); gu_trace (off = gu::unserialize4(buf, buflen, offset, b)); version_ = b & 0x0f; flags_ = (b & 0xf0) >> 4; if (version_ != 0) gu_throw_error (EPROTONOSUPPORT) << "Unsupported protocol varsion: " << version_; type_ = static_cast((b >> 8) & 0xff); if (type_ <= T_NONE || type_ >= T_MAX) gu_throw_error (EINVAL) << "Bad type value: " << type_; crc16_ = ((b >> 16) & 0xffff); gu_trace (off = gu::unserialize4(buf, buflen, off, seq_)); if (type_ == T_STATE || type_ == T_INSTALL) { gu_trace (off = node_map_.unserialize(buf, buflen, off)); } return off; } size_t serialize(gu::byte_t* buf, const size_t buflen, const size_t offset) const { size_t off; uint32_t b; b = crc16_; b <<= 8; b |= type_ & 0xff; b <<= 8; b |= version_ & 0x0f; b |= (flags_ << 4) & 0xf0; gu_trace (off = gu::serialize4(b, buf, buflen, offset)); gu_trace (off = gu::serialize4(seq_, buf, buflen, off)); if (type_ == T_STATE || type_ == T_INSTALL) { gu_trace (off = node_map_.serialize(buf, buflen, off)); } assert (serial_size() == (off - offset)); return off; } size_t serial_size() const { // header return (sizeof(uint32_t) + sizeof(seq_) + (type_ == T_STATE || type_ == T_INSTALL ? node_map_.serial_size() : 0)); } std::string to_string() const { std::ostringstream ret; ret << "pcmsg{ type=" << to_string(type_) << ", seq=" << seq_; ret << ", flags=" << std::setw(2) << std::hex << flags_; ret << ", node_map {" << node_map() << "}"; ret << '}'; return ret.str(); } private: Message& operator=(const Message&); int version_; // Message version int flags_; // Flags Type type_; // Message type uint32_t seq_; // Message seqno uint16_t crc16_; // 16-bit crc NodeMap node_map_; // Message node map }; inline std::ostream& gcomm::pc::operator<<(std::ostream& os, const Message& m) { return (os << m.to_string()); } class gcomm::pc::StateMessage : public Message { public: StateMessage(int version) : Message(version, Message::T_STATE, 0) {} }; class gcomm::pc::InstallMessage : public Message { public: InstallMessage(int version) : Message(version, Message::T_INSTALL, 0) {} }; class gcomm::pc::UserMessage : public Message { public: UserMessage(int version, uint32_t seq) : Message(version, Message::T_USER, seq) {} }; inline bool gcomm::pc::operator==(const Message& a, const Message& b) { return (a.version() == b.version() && a.checksum() == b.checksum() && a.type() == b.type() && a.seq() == b.seq() && a.node_map() == b.node_map()); } #endif // PC_MESSAGE_HPP percona-xtradb-cluster-galera/gcomm/src/pc_proto.cpp0000644000000000000000000013446712247075736023113 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #include "pc_proto.hpp" #include "pc_message.hpp" #include "gcomm/util.hpp" #include "gu_logger.hpp" #include "gu_macros.h" #include using std::rel_ops::operator!=; using std::rel_ops::operator>; // // Helpers // class SelectPrimOp { public: SelectPrimOp(gcomm::pc::Proto::SMMap& states) : states_(states) { } void operator()(const gcomm::pc::Proto::SMMap::value_type& vt) const { const gcomm::UUID& uuid(gcomm::pc::Proto::SMMap::key(vt)); const gcomm::pc::Message& msg(gcomm::pc::Proto::SMMap::value(vt)); const gcomm::pc::NodeMap& nm(msg.node_map()); gcomm::pc::NodeMap::const_iterator nm_i(nm.find(uuid)); if (nm_i == nm.end()) { gu_throw_error(EPROTO) << "protocol error, self not found from " << uuid << " state msg node list"; } if (gcomm::pc::NodeMap::value(nm_i).prim() == true) { states_.insert(vt); } } private: gcomm::pc::Proto::SMMap& states_; }; class ToSeqCmpOp { public: bool operator()(const gcomm::pc::Proto::SMMap::value_type& a, const gcomm::pc::Proto::SMMap::value_type& b) const { const gcomm::pc::Node& astate( gcomm::pc::NodeMap::value( gcomm::pc::Proto::SMMap::value(a).node_map() .find_checked(gcomm::pc::Proto::SMMap::key(a)))); const gcomm::pc::Node& bstate( gcomm::pc::NodeMap::value( gcomm::pc::Proto::SMMap::value(b).node_map() .find_checked(gcomm::pc::Proto::SMMap::key(b)))); return (astate.to_seq() < bstate.to_seq()); } }; // Return max to seq found from states, -1 if states is empty static int64_t get_max_to_seq(const gcomm::pc::Proto::SMMap& states) { if (states.empty() == true) return -1; gcomm::pc::Proto::SMMap::const_iterator max_i( max_element(states.begin(), states.end(), ToSeqCmpOp())); const gcomm::pc::Node& state( gcomm::pc::Proto::SMMap::value(max_i).node( gcomm::pc::Proto::SMMap::key(max_i))); return state.to_seq(); } static void checksum(gcomm::pc::Message& msg, gcomm::Datagram& dg) { uint16_t crc16(gcomm::crc16(dg, 4)); msg.checksum(crc16, true); gcomm::pop_header(msg, dg); gcomm::push_header(msg, dg); } static void test_checksum(gcomm::pc::Message& msg, const gcomm::Datagram& dg, size_t offset) { uint16_t msg_crc16(msg.checksum()); uint16_t crc16(gcomm::crc16(dg, offset + 4)); if (crc16 != msg_crc16) { gu_throw_fatal << "Message checksum failed"; } } std::ostream& gcomm::pc::operator<<(std::ostream& os, const gcomm::pc::Proto& p) { os << "pc::Proto{"; os << "uuid=" << p.my_uuid_ << ","; os << "start_prim=" << p.start_prim_ << ","; os << "npvo=" << p.npvo_ << ","; os << "ignore_sb=" << p.ignore_sb_ << ","; os << "ignore_quorum=" << p.ignore_quorum_ << ","; os << "state=" << p.state_ << ","; os << "last_sent_seq=" << p.last_sent_seq_ << ","; os << "checksum=" << p.checksum_ << ","; os << "instances=\n" << p.instances_ << ","; os << "state_msgs=\n" << p.state_msgs_ << ","; os << "current_view=" << p.current_view_ << ","; os << "pc_view=" << p.pc_view_ << ","; // os << "views=" << p.views_ << ","; os << "mtu=" << p.mtu_ << "}"; return os; } // // // void gcomm::pc::Proto::send_state() { log_debug << self_id() << " sending state"; StateMessage pcs(version_); NodeMap& im(pcs.node_map()); for (NodeMap::iterator i = instances_.begin(); i != instances_.end(); ++i) { // Assume all nodes in the current view have reached current to_seq Node& local_state(NodeMap::value(i)); if (current_view_.is_member(NodeMap::key(i)) == true) { local_state.set_to_seq(to_seq()); } im.insert_unique(std::make_pair(NodeMap::key(i), local_state)); } log_debug << self_id() << " local to seq " << to_seq(); log_debug << self_id() << " sending state: " << pcs; gu::Buffer buf; serialize(pcs, buf); Datagram dg(buf); if (send_down(dg, ProtoDownMeta())) { gu_throw_fatal << "pass down failed"; } } void gcomm::pc::Proto::send_install(bool bootstrap, int weight) { gcomm_assert(bootstrap == false || weight == -1); log_debug << self_id() << " send install"; InstallMessage pci(version_); NodeMap& im(pci.node_map()); for (SMMap::const_iterator i = state_msgs_.begin(); i != state_msgs_.end(); ++i) { if (current_view_.members().find(SMMap::key(i)) != current_view_.members().end()) { gu_trace( im.insert_unique( std::make_pair( SMMap::key(i), SMMap::value(i).node((SMMap::key(i)))))); } } if (bootstrap == true) { pci.flags(pci.flags() | InstallMessage::F_BOOTSTRAP); log_debug << self_id() << " sending PC bootstrap message " << pci; } else if (weight != -1) { pci.flags(pci.flags() | InstallMessage::F_WEIGHT_CHANGE); Node& self(pci.node(uuid())); self.set_weight(weight); log_info << self_id() << " sending PC weight change message " << pci; } else { log_debug << self_id() << " sending install: " << pci; } gu::Buffer buf; serialize(pci, buf); Datagram dg(buf); int ret = send_down(dg, ProtoDownMeta()); if (ret != 0) { log_warn << self_id() << " sending install message failed: " << strerror(ret); } } void gcomm::pc::Proto::deliver_view(bool bootstrap) { View v(pc_view_.id(), bootstrap); v.add_members(current_view_.members().begin(), current_view_.members().end()); for (NodeMap::const_iterator i = instances_.begin(); i != instances_.end(); ++i) { if (current_view_.members().find(NodeMap::key(i)) == current_view_.members().end()) { v.add_partitioned(NodeMap::key(i), ""); } } ProtoUpMeta um(UUID::nil(), ViewId(), &v); log_info << v; send_up(Datagram(), um); set_stable_view(v); } void gcomm::pc::Proto::mark_non_prim() { pc_view_ = ViewId(V_NON_PRIM, current_view_.id()); for (NodeMap::iterator i = instances_.begin(); i != instances_.end(); ++i) { const UUID& uuid(NodeMap::key(i)); Node& inst(NodeMap::value(i)); if (current_view_.members().find(uuid) != current_view_.members().end()) { inst.set_prim(false); pc_view_.add_member(uuid, ""); } } set_prim(false); } void gcomm::pc::Proto::shift_to(const State s) { // State graph static const bool allowed[S_MAX][S_MAX] = { // Closed { false, false, false, false, false, true }, // States exch { true, false, true, false, true, true }, // Install { true, false, false, true, true, true }, // Prim { true, false, false, false, true, true }, // Trans { true, true, false, false, false, true }, // Non-prim { true, false, false, true, true, true } }; if (allowed[state()][s] == false) { gu_throw_fatal << "Forbidden state transtion: " << to_string(state()) << " -> " << to_string(s); } switch (s) { case S_CLOSED: break; case S_STATES_EXCH: state_msgs_.clear(); break; case S_INSTALL: break; case S_PRIM: { pc_view_ = ViewId(V_PRIM, current_view_.id()); for (NodeMap::iterator i = instances_.begin(); i != instances_.end(); ++i) { const UUID& uuid(NodeMap::key(i)); Node& inst(NodeMap::value(i)); if (current_view_.members().find(uuid) != current_view_.members().end()) { inst.set_prim(true); inst.set_last_prim(ViewId(V_PRIM, current_view_.id())); inst.set_last_seq(0); inst.set_to_seq(to_seq()); pc_view_.add_member(uuid, ""); } else { inst.set_prim(false); } } last_sent_seq_ = 0; set_prim(true); break; } case S_TRANS: break; case S_NON_PRIM: mark_non_prim(); break; default: ; } log_debug << self_id() << " shift_to: " << to_string(state()) << " -> " << to_string(s) << " prim " << prim() << " last prim " << last_prim() << " to_seq " << to_seq(); state_ = s; } void gcomm::pc::Proto::handle_first_trans(const View& view) { gcomm_assert(state() == S_NON_PRIM); gcomm_assert(view.type() == V_TRANS); if (start_prim_ == true) { if (view.members().size() > 1 || view.is_empty()) { gu_throw_fatal << "Corrupted view"; } if (NodeList::key(view.members().begin()) != uuid()) { gu_throw_fatal << "Bad first UUID: " << NodeList::key(view.members().begin()) << ", expected: " << uuid(); } set_last_prim(ViewId(V_PRIM, view.id())); set_prim(true); } current_view_ = view; shift_to(S_TRANS); } // Compute weighted sum of members in node list. If member cannot be found // from node_map its weight is assumed to be zero. static size_t weighted_sum(const gcomm::NodeList& node_list, const gcomm::pc::NodeMap& node_map) { size_t sum(0); for (gcomm::NodeList::const_iterator i(node_list.begin()); i != node_list.end(); ++i) { int weight(0); gcomm::pc::NodeMap::const_iterator node_i( node_map.find(gcomm::NodeList::key(i))); if (node_i != node_map.end()) { const gcomm::pc::Node& node(gcomm::pc::NodeMap::value(node_i)); gcomm_assert(node.weight() >= 0 && node.weight() <= 0xff); weight = node.weight(); } else { weight = 0; } sum += weight; } return sum; } // Check if all members in node_list have weight associated. This is needed // to fall back to backwards compatibility mode during upgrade (all weights are // assumed to be one). See have_quorum() and have_split_brain() below. static bool have_weights(const gcomm::NodeList& node_list, const gcomm::pc::NodeMap& node_map) { for (gcomm::NodeList::const_iterator i(node_list.begin()); i != node_list.end(); ++i) { gcomm::pc::NodeMap::const_iterator node_i( node_map.find(gcomm::NodeList::key(i))); if (node_i != node_map.end()) { const gcomm::pc::Node& node(gcomm::pc::NodeMap::value(node_i)); if (node.weight() == -1) { return false; } } } return true; } bool gcomm::pc::Proto::have_quorum(const View& view, const View& pc_view) const { if (have_weights(view.members(), instances_) && have_weights(view.left(), instances_) && have_weights(pc_view.members(), instances_)) { return (weighted_sum(view.members(), instances_) * 2 + weighted_sum(view.left(), instances_) > weighted_sum(pc_view.members(), instances_)); } else { return (view.members().size()*2 + view.left().size() > pc_view.members().size()); } } bool gcomm::pc::Proto::have_split_brain(const View& view) const { if (have_weights(view.members(), instances_) && have_weights(view.left(), instances_) && have_weights(pc_view_.members(), instances_)) { return (weighted_sum(view.members(), instances_) * 2 + weighted_sum(view.left(), instances_) == weighted_sum(pc_view_.members(), instances_)); } else { return (view.members().size()*2 + view.left().size() == pc_view_.members().size()); } } void gcomm::pc::Proto::handle_trans(const View& view) { gcomm_assert(view.id().type() == V_TRANS); gcomm_assert(view.id().uuid() == current_view_.id().uuid() && view.id().seq() == current_view_.id().seq()); log_debug << self_id() << " \n\n current view " << current_view_ << "\n\n next view " << view << "\n\n pc view " << pc_view_; if (have_quorum(view, pc_view_) == false) { if (closing_ == false && ignore_sb_ == true && have_split_brain(view)) { // configured to ignore split brain log_warn << "Ignoring possible split-brain " << "(allowed by configuration) from view:\n" << current_view_ << "\nto view:\n" << view; } else if (closing_ == false && ignore_quorum_ == true) { // configured to ignore lack of quorum log_warn << "Ignoring lack of quorum " << "(allowed by configuration) from view:\n" << current_view_ << "\nto view:\n" << view; } else { current_view_ = view; // shift_to(S_NON_PRIM); mark_non_prim(); deliver_view(); shift_to(S_TRANS); return; } } else { log_debug << self_id() << " quorum ok"; } current_view_ = view; shift_to(S_TRANS); } void gcomm::pc::Proto::handle_reg(const View& view) { gcomm_assert(view.type() == V_REG); gcomm_assert(state() == S_TRANS); if (view.is_empty() == false && view.id().seq() <= current_view_.id().seq()) { gu_throw_fatal << "Non-increasing view ids: current view " << current_view_.id() << " new view " << view.id(); } current_view_ = view; views_.push_back(current_view_); if (current_view_.is_empty() == true) { shift_to(S_NON_PRIM); deliver_view(); shift_to(S_CLOSED); } else { shift_to(S_STATES_EXCH); send_state(); } } void gcomm::pc::Proto::handle_view(const View& view) { // We accept only EVS TRANS and REG views if (view.type() != V_TRANS && view.type() != V_REG) { gu_throw_fatal << "Invalid view type"; } // Make sure that self exists in view if (view.is_empty() == false && view.is_member(uuid()) == false) { gu_throw_fatal << "Self not found from non empty view: " << view; } log_debug << self_id() << " " << view; if (view.type() == V_TRANS) { if (current_view_.type() == V_NONE) { handle_first_trans(view); } else { handle_trans(view); } } else { handle_reg(view); } } // Validate state message agains local state void gcomm::pc::Proto::validate_state_msgs() const { // #622, #638 Compute max TO seq among states from prim SMMap prim_state_msgs; std::for_each(state_msgs_.begin(), state_msgs_.end(), SelectPrimOp(prim_state_msgs)); const int64_t max_to_seq(get_max_to_seq(prim_state_msgs)); for (SMMap::const_iterator i = state_msgs_.begin(); i != state_msgs_.end(); ++i) { const UUID& msg_source_uuid(SMMap::key(i)); const Node& msg_source_state(SMMap::value(i).node(msg_source_uuid)); const NodeMap& msg_state_map(SMMap::value(i).node_map()); for (NodeMap::const_iterator si = msg_state_map.begin(); si != msg_state_map.end(); ++si) { const UUID& uuid(NodeMap::key(si)); const Node& msg_state(NodeMap::value(si)); const Node& local_state(NodeMap::value(instances_.find_checked(uuid))); if (prim() == true && msg_source_state.prim() == true && msg_state.prim() == true) { if (current_view_.is_member(uuid) == true) { // Msg source claims to come from prim view and this node // is in prim. All message prim view states must be equal // to local ones. if (msg_state.weight() == -1) { // backwards compatibility, ignore weight in state check gcomm_assert( msg_state.prim() == local_state.prim() && msg_state.last_seq() == local_state.last_seq() && msg_state.last_prim() == local_state.last_prim() && msg_state.to_seq() == local_state.to_seq()) << self_id() << " node " << uuid << " prim state message and local states not consistent:" << " msg node " << msg_state << " local state " << local_state; } else { gcomm_assert(msg_state == local_state) << self_id() << " node " << uuid << " prim state message and local states not consistent:" << " msg node " << msg_state << " local state " << local_state; } gcomm_assert(msg_state.to_seq() == max_to_seq) << self_id() << " node " << uuid << " to seq not consistent with local state:" << " max to seq " << max_to_seq << " msg state to seq " << msg_state.to_seq(); } } else if (prim() == true) { log_debug << self_id() << " node " << uuid << " from " << msg_state.last_prim() << " joining " << last_prim(); } else if (msg_state.prim() == true) { // @todo: Cross check with other state messages coming from prim log_debug << self_id() << " joining to " << msg_state.last_prim(); } } } } // @note This method is currently for sanity checking only. RTR is not // implemented yet. bool gcomm::pc::Proto::requires_rtr() const { bool ret = false; // Find maximum reported to_seq const int64_t max_to_seq(get_max_to_seq(state_msgs_)); for (SMMap::const_iterator i = state_msgs_.begin(); i != state_msgs_.end(); ++i) { NodeMap::const_iterator ii( SMMap::value(i).node_map().find_checked(SMMap::key(i))); const Node& inst = NodeMap::value(ii); const int64_t to_seq = inst.to_seq(); const ViewId last_prim = inst.last_prim(); if (to_seq != -1 && to_seq != max_to_seq && last_prim.type() != V_NON_PRIM) { log_debug << self_id() << " RTR is needed: " << to_seq << " / " << last_prim; ret = true; } } return ret; } void gcomm::pc::Proto::cleanup_instances() { gcomm_assert(state() == S_PRIM); gcomm_assert(current_view_.type() == V_REG); NodeMap::iterator i, i_next; for (i = instances_.begin(); i != instances_.end(); i = i_next) { i_next = i, ++i_next; const UUID& uuid(NodeMap::key(i)); if (current_view_.is_member(uuid) == false) { log_debug << self_id() << " cleaning up instance " << uuid; instances_.erase(i); } else { // Clear unknow status from nodes in current view here. // New PC has been installed and if paritioning happens, // we either know for sure that the other partitioned component ends // up in non-prim, or in other case we have valid PC view to // deal with in case of remerge. NodeMap::value(i).set_un(false); } } } bool gcomm::pc::Proto::is_prim() const { bool prim(false); ViewId last_prim(V_NON_PRIM); int64_t to_seq(-1); // Check if any of instances claims to come from prim view for (SMMap::const_iterator i = state_msgs_.begin(); i != state_msgs_.end(); ++i) { const Node& state(SMMap::value(i).node(SMMap::key(i))); if (state.prim() == true) { log_info << "Node " << SMMap::key(i) << " state prim"; prim = true; last_prim = state.last_prim(); to_seq = state.to_seq(); break; } } // Verify that all members are either coming from the same prim // view or from non-prim for (SMMap::const_iterator i = state_msgs_.begin(); i != state_msgs_.end(); ++i) { const Node& state(SMMap::value(i).node(SMMap::key(i))); if (state.prim() == true) { if (state.last_prim() != last_prim) { gu_throw_fatal << self_id() << " last prims not consistent"; } if (state.to_seq() != to_seq) { gu_throw_fatal << self_id() << " TO seqs not consistent"; } } else { log_debug << "Non-prim " << SMMap::key(i) <<" from " << state.last_prim() << " joining prim"; } } // No members coming from prim view, check if last known prim // view can be recovered (majority of members from last prim alive) if (prim == false) { gcomm_assert(last_prim == ViewId(V_NON_PRIM)) << last_prim << " != " << ViewId(V_NON_PRIM); // first determine if there are any nodes still in unknown state std::set un; for (NodeMap::const_iterator i(instances_.begin()); i != instances_.end(); ++i) { if (NodeMap::value(i).un() == true && current_view_.members().find(NodeMap::key(i)) == current_view_.members().end()) { un.insert(NodeMap::key(i)); } } if (un.empty() == false) { std::ostringstream oss; std::copy(un.begin(), un.end(), std::ostream_iterator(oss, " ")); log_info << "Nodes " << oss.str() << "are still in unknown state, " << "unable to rebootstrap new prim"; return false; } MultiMap last_prim_uuids; for (SMMap::const_iterator i = state_msgs_.begin(); i != state_msgs_.end(); ++i) { for (NodeMap::const_iterator j = SMMap::value(i).node_map().begin(); j != SMMap::value(i).node_map().end(); ++j) { const UUID& uuid(NodeMap::key(j)); const Node& inst(NodeMap::value(j)); if (inst.last_prim() != ViewId(V_NON_PRIM) && std::find::iterator, std::pair >( last_prim_uuids.begin(), last_prim_uuids.end(), std::make_pair(inst.last_prim(), uuid)) == last_prim_uuids.end()) { last_prim_uuids.insert(std::make_pair(inst.last_prim(), uuid)); } } } if (last_prim_uuids.empty() == true) { log_warn << "no nodes coming from prim view, prim not possible"; return false; } const ViewId greatest_view_id(last_prim_uuids.rbegin()->first); std::set greatest_view; std::pair::const_iterator, MultiMap::const_iterator> gvi = last_prim_uuids.equal_range(greatest_view_id); for (MultiMap::const_iterator i = gvi.first; i != gvi.second; ++i) { std::pair::iterator, bool> iret = greatest_view.insert( MultiMap::value(i)); gcomm_assert(iret.second == true); } log_debug << self_id() << " greatest view id " << greatest_view_id; std::set present; for (NodeList::const_iterator i = current_view_.members().begin(); i != current_view_.members().end(); ++i) { present.insert(NodeList::key(i)); } std::set intersection; set_intersection(greatest_view.begin(), greatest_view.end(), present.begin(), present.end(), inserter(intersection, intersection.begin())); log_debug << self_id() << " intersection size " << intersection.size() << " greatest view size " << greatest_view.size(); if (intersection.size() == greatest_view.size()) { log_info << "re-bootstrapping prim from partitioned components"; prim = true; } } return prim; } void gcomm::pc::Proto::handle_state(const Message& msg, const UUID& source) { gcomm_assert(msg.type() == Message::T_STATE); gcomm_assert(state() == S_STATES_EXCH); gcomm_assert(state_msgs_.size() < current_view_.members().size()); log_debug << self_id() << " handle state from " << source << " " << msg; // Early check for possibly conflicting primary components. The one // with greater view id may continue (as it probably has been around // for longer timer). However, this should be configurable policy. if (prim() == true) { const Node& si(NodeMap::value(msg.node_map().find(source))); if (si.prim() == true && si.last_prim() != last_prim()) { log_warn << self_id() << " conflicting prims: my prim: " << last_prim() << " other prim: " << si.last_prim(); if ((npvo_ == true && last_prim() < si.last_prim()) || (npvo_ == false && last_prim() > si.last_prim())) { log_warn << self_id() << " discarding other prim view: " << (npvo_ == true ? "newer" : "older" ) << " overrides"; return; } else { gu_throw_fatal << self_id() << " aborting due to conflicting prims: " << (npvo_ == true ? "newer" : "older" ) << " overrides"; } } } state_msgs_.insert_unique(std::make_pair(source, msg)); if (state_msgs_.size() == current_view_.members().size()) { // Insert states from previously unseen nodes into local state map for (SMMap::const_iterator i = state_msgs_.begin(); i != state_msgs_.end(); ++i) { const NodeMap& sm_im(SMMap::value(i).node_map()); for (NodeMap::const_iterator j = sm_im.begin(); j != sm_im.end(); ++j) { const UUID& sm_uuid(NodeMap::key(j)); const Node& sm_node(NodeMap::value(j)); NodeMap::iterator local_node_i(instances_.find(sm_uuid)); if (local_node_i == instances_.end()) { const Node& sm_state(NodeMap::value(j)); instances_.insert_unique(std::make_pair(sm_uuid, sm_state)); } else { Node& local_node(NodeMap::value(local_node_i)); if (local_node.weight() == -1 && sm_node.weight() != -1) { // backwards compatibility: override weight for // instances which have been reported by old nodes // but have weights associated anyway local_node.set_weight(sm_node.weight()); } else if (local_node.weight() != sm_node.weight() && SMMap::key(i) == NodeMap::key(local_node_i)) { log_warn << self_id() << "overriding reported weight for " << NodeMap::key(local_node_i); local_node.set_weight(sm_node.weight()); } if (sm_node.un() == true) { // set local instance status to unknown if any of the // state messages has it marked unknown local_node.set_un(true); } } } } // Validate that all state messages are consistent before proceeding gu_trace(validate_state_msgs()); if (is_prim() == true) { // @note Requires RTR does not actually have effect, but let it // be for debugging purposes until a while (void)requires_rtr(); shift_to(S_INSTALL); if (current_view_.members().find(uuid()) == current_view_.members().begin()) { send_install(false); } } else { // #571 Deliver NON-PRIM views in all cases. shift_to(S_NON_PRIM); deliver_view(); } } } void gcomm::pc::Proto::handle_install(const Message& msg, const UUID& source) { if (state() == S_PRIM) { if ((msg.flags() & Message::F_WEIGHT_CHANGE) == 0) { log_warn << "non weight changing install in S_PRIM: " << msg; } else { NodeMap::iterator local_i(instances_.find(source)); const Node& msg_n(msg.node(source)); log_info << self_id() << " changing node " << source << " weight (reg) " << NodeMap::value(local_i).weight() << " -> " << msg_n.weight(); NodeMap::value(local_i).set_weight(msg_n.weight()); if (source == uuid()) { conf_.set(gcomm::Conf::PcWeight, gu::to_string(msg_n.weight())); } } return; } else if (state() == S_TRANS) { handle_trans_install(msg, source); return; } gcomm_assert(msg.type() == Message::T_INSTALL); gcomm_assert(state() == S_INSTALL || state() == S_NON_PRIM); if ((msg.flags() & Message::F_BOOTSTRAP) == 0) { log_debug << self_id() << " handle install from " << source << " " << msg; } else { log_debug << self_id() << " handle bootstrap install from " << source << " " << msg; if (state() == S_INSTALL) { log_info << "ignoring bootstrap install in " << to_string(state()) << " state"; return; } } // Validate own state NodeMap::const_iterator mi(msg.node_map().find_checked(uuid())); const Node& m_state(NodeMap::value(mi)); if (m_state.weight() == -1) { // backwards compatibility, ignore weight in state check const Node& self_state(NodeMap::value(self_i_)); if ((m_state.prim() == self_state.prim() && m_state.last_seq() == self_state.last_seq() && m_state.last_prim() == self_state.last_prim() && m_state.to_seq() == self_state.to_seq()) == false) { gu_throw_fatal << self_id() << "Install message self state does not match, " << "message state: " << m_state << ", local state: " << NodeMap::value(self_i_); } } else { if (m_state != NodeMap::value(self_i_)) { gu_throw_fatal << self_id() << "Install message self state does not match, " << "message state: " << m_state << ", local state: " << NodeMap::value(self_i_); } } // Set TO seqno according to install message int64_t to_seq(-1); bool prim_found(false); for (mi = msg.node_map().begin(); mi != msg.node_map().end(); ++mi) { const Node& m_state = NodeMap::value(mi); // check that all TO seqs coming from prim are same if (m_state.prim() == true && to_seq != -1) { if (m_state.to_seq() != to_seq) { gu_throw_fatal << "Install message TO seqnos inconsistent"; } } if (m_state.prim() == true) { prim_found = true; to_seq = std::max(to_seq, m_state.to_seq()); } } if (prim_found == false) { // #277 // prim comp was restored from non-prims, find out max known TO seq for (mi = msg.node_map().begin(); mi != msg.node_map().end(); ++mi) { const Node& m_state = NodeMap::value(mi); to_seq = std::max(to_seq, m_state.to_seq()); } log_debug << "assigning TO seq to " << to_seq << " after restoring prim"; } log_debug << self_id() << " setting TO seq to " << to_seq; set_to_seq(to_seq); shift_to(S_PRIM); deliver_view(msg.flags() & Message::F_BOOTSTRAP); cleanup_instances(); } namespace { class ViewUUIDLT { public: bool operator()(const gcomm::NodeList::value_type& a, const gcomm::NodeList::value_type& b) const { return (a.first < b.first); } }; } // When delivering install message in trans view quorum has to be re-evaluated // as the partitioned component may have installed prim view due to higher // weight. To do this, we construct pc view that would have been installed // if install message was delivered in reg view and make quorum computation // against it. // // It is not actually known if partitioned component installed new PC, so // we mark partitioned nodes states as unknown. This is to provide deterministic // way to prevent automatic rebootstrapping of PC if some of the seen nodes // is in unknown state. void gcomm::pc::Proto::handle_trans_install(const Message& msg, const UUID& source) { gcomm_assert(msg.type() == Message::T_INSTALL); gcomm_assert(state() == S_TRANS); gcomm_assert(current_view_.type() == V_TRANS); if ((msg.flags() & Message::F_BOOTSTRAP) != 0) { log_info << "Dropping bootstrap install in TRANS state"; return; } gcomm_assert(have_quorum(current_view_, pc_view_) == true); if ((msg.flags() & Message::F_WEIGHT_CHANGE) != 0) { NodeList nl; nl.insert(current_view_.members().begin(), current_view_.members().end()); nl.insert(current_view_.left().begin(), current_view_.left().end()); if (std::includes(nl.begin(), nl.end(), pc_view_.members().begin(), pc_view_.members().end(), ViewUUIDLT()) == false) { // Weight changing install message delivered in trans view // and previous pc view has partitioned. // // Need to be very conservative: We don't know what happened to // weight change message in partitioned component, so it may not be // safe to do quorum calculation. Shift to non-prim and // wait until partitioned component comes back (or prim is // rebootstrapped). // // It would be possible to do more fine grained decisions // based on the source of the message, but to keep things simple // always go to non-prim, this is very cornerish case after all. log_info << "Weight changing trans install leads to non-prim"; mark_non_prim(); deliver_view(); for (NodeMap::const_iterator i(msg.node_map().begin()); i != msg.node_map().end(); ++i) { if (current_view_.members().find(NodeMap::key(i)) == current_view_.members().end()) { NodeMap::iterator local_i(instances_.find(NodeMap::key(i))); if (local_i == instances_.end()) { log_warn << "Node " << NodeMap::key(i) << " not found from instances"; } else { if (NodeMap::key(i) == source) { NodeMap::value(local_i).set_weight( NodeMap::value(i).weight()); if (source == uuid()) { conf_.set(gcomm::Conf::PcWeight, gu::to_string(NodeMap::value(i).weight())); } } NodeMap::value(local_i).set_un(true); } } } } else { NodeMap::iterator local_i(instances_.find(source)); const Node& msg_n(msg.node(source)); log_info << self_id() << " changing node " << source << " weight (trans) " << NodeMap::value(local_i).weight() << " -> " << msg_n.weight(); NodeMap::value(local_i).set_weight(msg_n.weight()); if (source == uuid()) { conf_.set(gcomm::Conf::PcWeight, gu::to_string(msg_n.weight())); } } } else { View new_pc_view(ViewId(V_PRIM, current_view_.id())); for (NodeMap::iterator i(instances_.begin()); i != instances_.end(); ++i) { const UUID& uuid(NodeMap::key(i)); NodeMap::const_iterator ni(msg.node_map().find(uuid)); if (ni != msg.node_map().end()) { new_pc_view.add_member(uuid); } } if (have_quorum(current_view_, new_pc_view) == false || pc_view_.type() == V_NON_PRIM) { log_info << "Trans install leads to non-prim"; mark_non_prim(); deliver_view(); for (NodeMap::const_iterator i(msg.node_map().begin()); i != msg.node_map().end(); ++i) { if (current_view_.members().find(NodeMap::key(i)) == current_view_.members().end()) { NodeMap::iterator local_i(instances_.find(NodeMap::key(i))); if (local_i == instances_.end()) { log_warn << "Node " << NodeMap::key(i) << " not found from instances"; } else { NodeMap::value(local_i).set_un(true); } } } } } } void gcomm::pc::Proto::handle_user(const Message& msg, const Datagram& dg, const ProtoUpMeta& um) { int64_t curr_to_seq(-1); if (prim() == true) { if (um.order() == O_SAFE) { set_to_seq(to_seq() + 1); curr_to_seq = to_seq(); } } else if (current_view_.members().find(um.source()) == current_view_.members().end()) { gcomm_assert(current_view_.type() == V_TRANS); // log_debug << self_id() // << " dropping message from out of view source in non-prim"; return; } if (um.order() == O_SAFE) { Node& state(NodeMap::value(instances_.find_checked(um.source()))); if (state.last_seq() + 1 != msg.seq()) { gu_throw_fatal << "gap in message sequence: source=" << um.source() << " expected_seq=" << state.last_seq() + 1 << " seq=" << msg.seq(); } state.set_last_seq(msg.seq()); } Datagram up_dg(dg, dg.offset() + msg.serial_size()); gu_trace(send_up(up_dg, ProtoUpMeta(um.source(), pc_view_.id(), 0, um.user_type(), um.order(), curr_to_seq))); } void gcomm::pc::Proto::handle_msg(const Message& msg, const Datagram& rb, const ProtoUpMeta& um) { enum Verdict { ACCEPT, DROP, FAIL }; static const Verdict verdicts[S_MAX][Message::T_MAX] = { // Msg types // NONE, STATE, INSTALL, USER { FAIL, FAIL, FAIL, FAIL }, // Closed { FAIL, ACCEPT, FAIL, FAIL }, // States exch { FAIL, FAIL, ACCEPT, FAIL }, // INSTALL { FAIL, FAIL, ACCEPT, ACCEPT }, // PRIM { FAIL, DROP, ACCEPT, ACCEPT }, // TRANS { FAIL, ACCEPT, ACCEPT, ACCEPT } // NON-PRIM }; Message::Type msg_type(msg.type()); Verdict verdict (verdicts[state()][msg.type()]); if (verdict == FAIL) { gu_throw_fatal << "Invalid input, message " << msg.to_string() << " in state " << to_string(state()); } else if (verdict == DROP) { log_warn << "Dropping input, message " << msg.to_string() << " in state " << to_string(state()); return; } switch (msg_type) { case Message::T_STATE: gu_trace(handle_state(msg, um.source())); break; case Message::T_INSTALL: gu_trace(handle_install(msg, um.source())); break; case Message::T_USER: gu_trace(handle_user(msg, rb, um)); break; default: gu_throw_fatal << "Invalid message"; } } void gcomm::pc::Proto::handle_up(const void* cid, const Datagram& rb, const ProtoUpMeta& um) { if (um.has_view() == true) { handle_view(um.view()); } else { Message msg; const gu::byte_t* b(gcomm::begin(rb)); const size_t available(gcomm::available(rb)); try { (void)msg.unserialize(b, available, 0); } catch (gu::Exception& e) { switch (e.get_errno()) { case EPROTONOSUPPORT: if (prim() == false) { gu_throw_fatal << e.what() << " terminating"; } else { log_warn << "unknown/unsupported protocol version: " << msg.version() << " dropping message"; return; } break; default: GU_TRACE(e); throw; } } if (checksum_ == true && msg.flags() & Message::F_CRC16) { test_checksum(msg, rb, rb.offset()); } try { handle_msg(msg, rb, um); } catch (gu::Exception& e) { log_error << "caught exception in PC, state dump to stderr follows:"; std::cerr << *this << std::endl; throw; } } } int gcomm::pc::Proto::handle_down(Datagram& dg, const ProtoDownMeta& dm) { if (gu_unlikely(state() != S_PRIM)) { return EAGAIN; } if (gu_unlikely(dg.len() > mtu())) { return EMSGSIZE; } uint32_t seq(dm.order() == O_SAFE ? last_sent_seq_ + 1 : last_sent_seq_); UserMessage um(version_, seq); push_header(um, dg); if (checksum_ == true) { checksum(um, dg); } int ret = send_down(dg, dm); if (ret == 0) { last_sent_seq_ = seq; } else if (ret != EAGAIN) { log_warn << "Proto::handle_down: " << strerror(ret); } pop_header(um, dg); return ret; } bool gcomm::pc::Proto::set_param(const std::string& key, const std::string& value) { if (key == gcomm::Conf::PcIgnoreSb) { ignore_sb_ = gu::from_string(value); conf_.set(gcomm::Conf::PcIgnoreSb, value); return true; } else if (key == gcomm::Conf::PcIgnoreQuorum) { ignore_quorum_ = gu::from_string(value); conf_.set(gcomm::Conf::PcIgnoreQuorum, value); return true; } else if (key == gcomm::Conf::PcBootstrap) { if (state() != S_NON_PRIM) { log_info << "ignoring '" << key << "' in state " << to_string(state()); } else { send_install(true); } return true; } else if (key == gcomm::Conf::PcWeight) { if (state() != S_PRIM) { gu_throw_error(EAGAIN) << "can't change weightm: state not S_PRIM, retry again"; } else { int w(gu::from_string(value)); if (w < 0 || w > 255) { gu_throw_error(ERANGE) << "value " << w << " for '" << key << "' out of range"; } weight_ = w; send_install(false, weight_); return true; } } else if (key == Conf::PcChecksum || key == Conf::PcAnnounceTimeout || key == Conf::PcLinger || key == Conf::PcNpvo || key == Conf::PcWaitPrim || key == Conf::PcWaitPrimTimeout) { gu_throw_error(EPERM) << "can't change value for '" << key << "' during runtime"; } return false; } percona-xtradb-cluster-galera/gcomm/src/pc_proto.hpp0000644000000000000000000001575012247075736023111 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #ifndef GCOMM_PC_PROTO_HPP #define GCOMM_PC_PROTO_HPP #include #include #include "gcomm/uuid.hpp" #include "gcomm/protolay.hpp" #include "gcomm/conf.hpp" #include "pc_message.hpp" #include "defaults.hpp" #include "gu_uri.hpp" #ifndef GCOMM_PC_MAX_VERSION #define GCOMM_PC_MAX_VERSION 0 #endif // GCOMM_PC_MAX_VERSION namespace gcomm { namespace pc { class Proto; std::ostream& operator<<(std::ostream& os, const Proto& p); } } class gcomm::pc::Proto : public Protolay { public: enum State { S_CLOSED, S_STATES_EXCH, S_INSTALL, S_PRIM, S_TRANS, S_NON_PRIM, S_MAX }; static std::string to_string(const State s) { switch (s) { case S_CLOSED: return "CLOSED"; case S_STATES_EXCH: return "STATES_EXCH"; case S_INSTALL: return "INSTALL"; case S_TRANS: return "TRANS"; case S_PRIM: return "PRIM"; case S_NON_PRIM: return "NON_PRIM"; default: gu_throw_fatal << "Invalid state"; } } Proto(gu::Config& conf, const UUID& uuid, const gu::URI& uri = gu::URI("pc://")) : Protolay(conf), version_( check_range(Conf::PcVersion, param(conf, uri, Conf::PcVersion, Defaults::PcVersion), 0, max_version_ + 1)), my_uuid_ (uuid), start_prim_ (), npvo_ (param(conf, uri, Conf::PcNpvo, Defaults::PcNpvo)), ignore_quorum_ (param(conf, uri, Conf::PcIgnoreQuorum, Defaults::PcIgnoreQuorum)), ignore_sb_ (param(conf, uri, Conf::PcIgnoreSb, gu::to_string(ignore_quorum_))), closing_ (false), state_ (S_CLOSED), last_sent_seq_ (0), checksum_ (param(conf, uri, Conf::PcChecksum, Defaults::PcChecksum)), instances_ (), self_i_ (instances_.insert_unique(std::make_pair(uuid, Node()))), state_msgs_ (), current_view_ (V_NONE), pc_view_ (V_NON_PRIM), views_ (), mtu_ (std::numeric_limits::max()), weight_ (check_range(Conf::PcWeight, param(conf, uri, Conf::PcWeight, Defaults::PcWeight), 0, 0xff)) { log_info << "PC version " << version_; set_weight(weight_); conf.set(Conf::PcVersion, gu::to_string(version_)); conf.set(Conf::PcNpvo, gu::to_string(npvo_)); conf.set(Conf::PcIgnoreQuorum, gu::to_string(ignore_quorum_)); conf.set(Conf::PcIgnoreSb, gu::to_string(ignore_sb_)); conf.set(Conf::PcChecksum, gu::to_string(checksum_)); conf.set(Conf::PcWeight, gu::to_string(weight_)); } ~Proto() { } const UUID& uuid() const { return my_uuid_; } bool prim() const { return NodeMap::value(self_i_).prim(); } void set_prim(const bool val) { NodeMap::value(self_i_).set_prim(val); } void mark_non_prim(); const ViewId& last_prim() const { return NodeMap::value(self_i_).last_prim(); } void set_last_prim(const ViewId& vid) { gcomm_assert(vid.type() == V_PRIM); NodeMap::value(self_i_).set_last_prim(vid); } uint32_t last_seq() const { return NodeMap::value(self_i_).last_seq(); } void set_last_seq(const uint32_t seq) { NodeMap::value(self_i_).set_last_seq(seq); } int64_t to_seq() const { return NodeMap::value(self_i_).to_seq(); } void set_to_seq(const int64_t seq) { NodeMap::value(self_i_).set_to_seq(seq); } void set_weight(int weight) { NodeMap::value(self_i_).set_weight(weight); } class SMMap : public Map { }; const View& current_view() const { return current_view_; } const UUID& self_id() const { return my_uuid_; } State state() const { return state_; } void shift_to (State); void send_state (); void send_install(bool bootstrap, int weight = -1); void handle_first_trans (const View&); void handle_trans (const View&); void handle_reg (const View&); void handle_msg (const Message&, const Datagram&, const ProtoUpMeta&); void handle_up (const void*, const Datagram&, const ProtoUpMeta&); int handle_down (Datagram&, const ProtoDownMeta&); void connect(bool first) { log_debug << self_id() << " start_prim " << first; start_prim_ = first; closing_ = false; shift_to(S_NON_PRIM); } void close() { closing_ = true; } void handle_view (const View&); bool set_param(const std::string& key, const std::string& val); void set_mtu(size_t mtu) { mtu_ = mtu; } size_t mtu() const { return mtu_; } private: friend std::ostream& operator<<(std::ostream& os, const Proto& p); Proto (const Proto&); Proto& operator=(const Proto&); bool requires_rtr() const; bool is_prim() const; bool have_quorum(const View&, const View&) const; bool have_split_brain(const View&) const; void validate_state_msgs() const; void cleanup_instances(); void handle_state(const Message&, const UUID&); void handle_install(const Message&, const UUID&); void handle_trans_install(const Message&, const UUID&); void handle_user(const Message&, const Datagram&, const ProtoUpMeta&); void deliver_view(bool bootstrap = false); int version_; static const int max_version_ = GCOMM_PC_MAX_VERSION; UUID const my_uuid_; // Node uuid bool start_prim_; // Is allowed to start in prim comp bool npvo_; // Newer prim view overrides bool ignore_quorum_; // Ignore lack of quorum bool ignore_sb_; // Ignore split-brain condition bool closing_; // Protocol is in closing stage State state_; // State uint32_t last_sent_seq_; // Msg seqno of last sent message bool checksum_; // Enable message checksumming NodeMap instances_; // Map of known node instances NodeMap::iterator self_i_; // Iterator pointing to self node instance SMMap state_msgs_; // Map of received state messages View current_view_; // EVS view View pc_view_; // PC view std::list views_; // List of seen views size_t mtu_; // Maximum transmission unit int weight_; // Node weight in voting }; #endif // PC_PROTO_HPP percona-xtradb-cluster-galera/gcomm/src/profile.hpp0000644000000000000000000001577112247075736022727 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ /*! * @file profile.hpp * * @brief Lightweight profiling utility. * * Profiling utility suitable for getting runtime code profile information * with minimal overhead. Macros profile_enter() and profile_leave() * can be inserted around the code and will be expanded to profiling * code if GCOMM_PROFILE is defined. * * Example usage: * @code * * Profile prof("prof"); * * void func() * { * if (is_true()) * { * profile_enter(prof); // This is line 227 * // Do something * // ... * profile_leave(prof); * } * else * { * profile_enter(prof); // This is line 250 * // Do something else * // ... * profile_leave(prof); * } * } * * // Somewhere else in your code * log_info << prof; * @endcode * */ #ifndef GCOMM_PROFILE_HPP #define GCOMM_PROFILE_HPP extern "C" { #include "gu_time.h" } #include #include namespace prof { // Forward declarations class Key; class Point; class Profile; std::ostream& operator<<(std::ostream&, const Key&); std::ostream& operator<<(std::ostream&, const Profile&); } /*! * Profile key storing human readable point description :: * and entry time. */ class prof::Key { public: Key(const char* const file, const char* const func, const int line) : file_(file), func_(func), line_(line) { } bool operator==(const Key& cmp) const { return (line_ == cmp.line_ && func_ == cmp.func_ && file_ == cmp.file_); } bool operator<(const Key& cmp) const { return (line_ < cmp.line_ || (line_ == cmp.line_ && (func_ < cmp.func_ || (func_ == cmp.func_ && file_ < cmp.file_)))); } std::string to_string() const { std::ostringstream os; os << *this; return os.str(); } private: friend class Point; friend class Profile; friend std::ostream& operator<<(std::ostream& os, const Key&); const char* const file_; const char* const func_; const int line_; }; inline std::ostream& prof::operator<<(std::ostream& os, const prof::Key& key) { return os << key.file_ << ":" << key.func_ << ":" << key.line_; } class prof::Point { public: Point(const Profile& prof, const char* file, const char* func, const int line); ~Point(); private: friend class Profile; const Profile& prof_; const Key key_; mutable long long int enter_time_calendar_; mutable long long int enter_time_thread_cputime_; }; /*! * Profile class for collecting statistics about profile points. */ class prof::Profile { struct PointStats { PointStats(long long int count = 0, long long int time_calendar = 0, long long int time_thread_cputime = 0) : count_ (count ), time_calendar_ (time_calendar ), time_thread_cputime_(time_thread_cputime) { } PointStats operator+(const PointStats& add) const { return PointStats(count_ + add.count_, time_calendar_ + add.time_calendar_, time_thread_cputime_ + add.time_thread_cputime_); } long long int count_; long long int time_calendar_; long long int time_thread_cputime_; }; public: /*! * Default constructor. * * @param name_ Name identifying the profile in ostream output. */ Profile(const std::string& name = "profile") : name_(name), start_time_calendar_(gu_time_calendar()), start_time_thread_cputime_(gu_time_thread_cputime()), points_() { } void enter(const Point& point) const { points_[point.key_].count_++; point.enter_time_calendar_ = gu_time_calendar(); point.enter_time_thread_cputime_ = gu_time_thread_cputime(); } void leave(const Point& point) const { long long int t_cal(gu_time_calendar()); long long int t_thdcpu(gu_time_thread_cputime()); points_[point.key_].time_calendar_ += (t_cal - point.enter_time_calendar_); points_[point.key_].time_thread_cputime_ += (t_thdcpu - point.enter_time_thread_cputime_); } void clear() { points_.clear(); } friend std::ostream& operator<<(std::ostream&, const Profile&); typedef std::map Map; std::string const name_; long long int const start_time_calendar_; long long int const start_time_thread_cputime_; mutable Map points_; }; inline prof::Point::Point(const Profile& prof, const char* file, const char* func, const int line) : prof_(prof), key_(file, func, line), enter_time_calendar_(), enter_time_thread_cputime_() { prof_.enter(*this); } inline prof::Point::~Point() { prof_.leave(*this); } /*! * Ostream operator for Profile class. */ inline std::ostream& prof::operator<<(std::ostream& os, const Profile& prof) { Profile::PointStats cumul; char prev_fill(os.fill()); os.fill(' '); os << "\nprofile name: " << prof.name_; os << std::left << std::fixed << std::setprecision(3); os << "\n\n"; os << std::setw(40) << "point"; os << std::setw(10) << "count"; os << std::setw(10) << "calendar"; os << std::setw(10) << "cpu"; os << "\n" << std::setfill('-') << std::setw(70) << "" << std::setfill(' ') << "\n"; for (Profile::Map::const_iterator i = prof.points_.begin(); i != prof.points_.end(); ++i) { os << std::setw(40) << std::left << i->first.to_string(); os << std::right; os << std::setw(10) << i->second.count_; os << std::setw(10) << double(i->second.time_calendar_)*1.e-9; os << std::setw(10) << double(i->second.time_thread_cputime_)*1.e-9; os << std::left; os << "\n"; cumul = cumul + i->second; } os << "\ntot count : " << cumul.count_; os << "\ntot calendar time : " << double(cumul.time_calendar_)*1.e-9; os << "\ntot thread cputime: " << double(cumul.time_thread_cputime_)*1.e-9; os << "\ntot ct since ctor : " << double(gu::datetime::Date::now().get_utc() - prof.start_time_calendar_)*1.e-9; os.fill(prev_fill); return os; } /* * Convenience macros for defining profile entry and leave points. * If GCOMM_PROFILE is undefined, these macros expand to no-op. */ #ifdef GCOMM_PROFILE #define profile_enter(__p) do { \ const prof::Point __point((__p), __FILE__, __FUNCTION__, __LINE__); \ #define profile_leave(__p) \ } while (0) #else #define profile_enter(__p) #define profile_leave(__p) #endif // GCOMM_PROFILE #endif // GCOMM_PROFILE_HPP percona-xtradb-cluster-galera/gcomm/src/protonet.cpp0000644000000000000000000000430112247075736023117 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy * * $Id$ */ #ifdef HAVE_ASIO_HPP #include "asio_protonet.hpp" #endif // HAVE_ASIO_HPP #include "gcomm/util.hpp" #include "gcomm/conf.hpp" void gcomm::Protonet::insert(Protostack* pstack) { log_debug << "insert pstack " << pstack; if (find(protos_.begin(), protos_.end(), pstack) != protos_.end()) { gu_throw_fatal; } protos_.push_back(pstack); } void gcomm::Protonet::erase(Protostack* pstack) { log_debug << "erase pstack " << pstack; std::deque::iterator i; if ((i = find(protos_.begin(), protos_.end(), pstack)) == protos_.end()) { gu_throw_fatal; } protos_.erase(i); } gu::datetime::Date gcomm::Protonet::handle_timers() { Critical crit(*this); gu::datetime::Date next_time(gu::datetime::Date::max()); { for (std::deque::iterator i = protos_.begin(); i != protos_.end(); ++i) { next_time = std::min(next_time, (*i)->handle_timers()); } } return next_time; } bool gcomm::Protonet::set_param(const std::string& key, const std::string& val) { bool ret(false); for (std::deque::iterator i(protos_.begin()); i != protos_.end(); ++i) { ret |= (*i)->set_param(key, val); } return ret; } gcomm::Protonet* gcomm::Protonet::create(gu::Config& conf) { #ifdef HAVE_ASIO_HPP static const std::string& default_backend("asio"); #endif // HAVE_ASIO_HPP const std::string backend(conf.get(Conf::ProtonetBackend, default_backend)); conf.set(Conf::ProtonetBackend, backend); const int version(conf.get(Conf::ProtonetVersion, 0)); conf.set(Conf::ProtonetVersion, version); if (version > max_version_) { gu_throw_error(EINVAL) << "invalid protonet version: " << version; } log_info << "protonet " << backend << " version " << version; #ifdef HAVE_ASIO_HPP if (backend == "asio") return new AsioProtonet(conf, version); #else #error "No protonet backends defined" #endif /* HAVE_ASIO_HPP */ gu_throw_fatal << Conf::ProtonetBackend << " '" << backend << "' not supported"; } percona-xtradb-cluster-galera/gcomm/src/protostack.cpp0000644000000000000000000000335012247075736023441 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #include "gcomm/protostack.hpp" #include "socket.hpp" #include "gcomm/util.hpp" void gcomm::Protostack::push_proto(Protolay* p) { Critical crit(*this); std::deque::iterator prev_begin(protos_.begin()); protos_.push_front(p); if (prev_begin != protos_.end()) { gcomm::connect(*prev_begin, p); } } void gcomm::Protostack::pop_proto(Protolay* p) { Critical crit(*this); assert(protos_.front() == p); if (protos_.front() != p) { log_warn << "Protolay " << p << " is not protostack front"; return; } protos_.pop_front(); if (protos_.begin() != protos_.end()) { gcomm::disconnect(*protos_.begin(), p); } } gu::datetime::Date gcomm::Protostack::handle_timers() { gu::datetime::Date ret(gu::datetime::Date::max()); Critical crit(*this); for (std::deque::reverse_iterator i = protos_.rbegin(); i != protos_.rend(); ++i) { gu::datetime::Date t((*i)->handle_timers()); if (t < ret) ret = t; } return ret; } void gcomm::Protostack::dispatch(const void* id, const Datagram& dg, const ProtoUpMeta& um) { Critical crit(*this); if (protos_.empty() == false) { protos_.back()->handle_up(id, dg, um); } } bool gcomm::Protostack::set_param(const std::string& key, const std::string& val) { bool ret(false); for (std::deque::iterator i(protos_.begin()); i != protos_.end(); ++i) { ret |= (*i)->set_param(key, val); } return ret; } percona-xtradb-cluster-galera/gcomm/src/socket.cpp0000644000000000000000000000103512247075736022536 0ustar rootroot00000000000000// // Copyright (C) 2012 Codership Oy // #include "socket.hpp" static const std::string SocketOptPrefix = "socket."; const std::string gcomm::Socket::OptNonBlocking = SocketOptPrefix + "non_blocking"; const std::string gcomm::Socket::OptIfAddr = SocketOptPrefix + "if_addr"; const std::string gcomm::Socket::OptIfLoop = SocketOptPrefix + "if_loop"; const std::string gcomm::Socket::OptCRC32 = SocketOptPrefix + "crc32"; const std::string gcomm::Socket::OptMcastTTL = SocketOptPrefix + "mcast_ttl"; percona-xtradb-cluster-galera/gcomm/src/socket.hpp0000644000000000000000000000425612247075736022553 0ustar rootroot00000000000000// // Copyright (C) 2009 Codership Oy // //! // @file socket.hpp Socket interface. // // This file defines socket interface used by gcomm. Currently socket interface // provides synchronous send() but only async_recv(). // #ifndef GCOMM_SOCKET_HPP #define GCOMM_SOCKET_HPP #include "gcomm/datagram.hpp" #include "gu_uri.hpp" namespace gcomm { typedef const void* SocketId; //!< Socket Identifier class Socket; //!< Socket interface typedef boost::shared_ptr SocketPtr; class Acceptor; //!< Acceptor interfacemat } class gcomm::Socket { public: typedef enum { S_CLOSED, S_CONNECTING, S_CONNECTED, S_FAILED, S_CLOSING } State; /*! * Symbolic option names (to specify in URI) */ static const std::string OptNonBlocking; /*! socket.non_blocking */ static const std::string OptIfAddr; /*! socket.if_addr */ static const std::string OptIfLoop; /*! socket.if_loop */ static const std::string OptCRC32; /*! socket.crc32 */ static const std::string OptMcastTTL; /*! socket.mcast_ttl */ Socket(const gu::URI& uri) : uri_(uri) { } virtual ~Socket() { } virtual void connect(const gu::URI& uri) = 0; virtual void close() = 0; virtual int send(const Datagram& dg) = 0; virtual void async_receive() = 0; virtual size_t mtu() const = 0; virtual std::string local_addr() const = 0; virtual std::string remote_addr() const = 0; virtual State state() const = 0; virtual SocketId id() const = 0; protected: const gu::URI uri_; }; class gcomm::Acceptor { public: typedef enum { S_CLOSED, S_LISTENING, S_FAILED } State; Acceptor(const gu::URI& uri) : uri_(uri) { } virtual ~Acceptor() { } virtual void listen(const gu::URI& uri) = 0; virtual std::string listen_addr() const = 0; virtual void close() = 0; virtual State state() const = 0; virtual SocketPtr accept() = 0; virtual SocketId id() const = 0; protected: const gu::URI uri_; }; #endif // GCOMM_SOCKET_HPP percona-xtradb-cluster-galera/gcomm/src/transport.cpp0000644000000000000000000000301512247075736023302 0ustar rootroot00000000000000/* * Copyright (C) 2009-2012 Codership Oy */ #include "gcomm/transport.hpp" #include "socket.hpp" #include "gmcast.hpp" #include "pc.hpp" #include "gcomm/conf.hpp" // Public methods const gcomm::UUID& gcomm::Transport::uuid() const { gu_throw_fatal << "UUID not supported by " + uri_.get_scheme(); } std::string gcomm::Transport::local_addr() const { gu_throw_fatal << "get local url not supported"; } std::string gcomm::Transport::remote_addr() const { gu_throw_fatal << "get remote url not supported"; } int gcomm::Transport::err_no() const { return error_no_; } void gcomm::Transport::listen() { gu_throw_fatal << "not supported"; } gcomm::Transport* gcomm::Transport::accept() { gu_throw_fatal << "not supported"; } // CTOR/DTOR gcomm::Transport::Transport(Protonet& pnet, const gu::URI& uri) : Protolay(pnet.conf()), pstack_(), pnet_(pnet), uri_(uri), error_no_(0) { } gcomm::Transport::~Transport() { } gcomm::Transport* gcomm::Transport::create(Protonet& pnet, const gu::URI& uri) { const std::string& scheme = uri.get_scheme(); if (scheme == Conf::GMCastScheme) { return new GMCast(pnet, uri); } else if (scheme == Conf::PcScheme) { return new PC(pnet, uri); } gu_throw_fatal << "scheme '" << uri.get_scheme() << "' not supported"; } gcomm::Transport* gcomm::Transport::create(Protonet& pnet, const std::string& uri_str) { return create(pnet, gu::URI(uri_str)); } percona-xtradb-cluster-galera/gcomm/src/uuid.cpp0000644000000000000000000000024412247075736022215 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #include "gcomm/uuid.hpp" const gcomm::UUID gcomm::UUID::uuid_nil_ = gcomm::UUID(GU_UUID_NIL); percona-xtradb-cluster-galera/gcomm/src/view.cpp0000644000000000000000000001234412247075736022225 0ustar rootroot00000000000000/* * Copyright (C) 2009-2012 Codership Oy */ #include "gcomm/view.hpp" #include "gcomm/types.hpp" #include "gcomm/util.hpp" #include "gu_logger.hpp" #include size_t gcomm::ViewId::unserialize(const gu::byte_t* buf, const size_t buflen, const size_t offset) { size_t off; gu_trace (off = uuid_.unserialize(buf, buflen, offset)); uint32_t w; gu_trace (off = gu::unserialize4(buf, buflen, off, w)); seq_ = w & 0x3fffffff; type_ = static_cast(w >> 30); return off; } size_t gcomm::ViewId::serialize(gu::byte_t* buf, const size_t buflen, const size_t offset) const { size_t off; gcomm_assert(type_ != V_NONE); gu_trace (off = uuid_.serialize(buf, buflen, offset)); uint32_t w((seq_ & 0x3fffffff) | (type_ << 30)); gu_trace (off = gu::serialize4(w, buf, buflen, off)); return off; } static std::string to_string(const gcomm::ViewType type) { switch (type) { case gcomm::V_TRANS: return "TRANS"; case gcomm::V_REG: return "REG"; case gcomm::V_NON_PRIM: return "NON_PRIM"; case gcomm::V_PRIM: return "PRIM"; default: return "UNKNOWN"; // gcomm_throw_fatal << "Invalid type value"; } } std::ostream& gcomm::operator<<(std::ostream& os, const gcomm::ViewId& vi) { return (os << "view_id(" << ::to_string(vi.type()) << "," << vi.uuid() << "," << vi.seq()) << ")"; } void gcomm::View::add_member(const UUID& pid, const std::string& name) { gu_trace((void)members_.insert_unique(std::make_pair(pid, Node()))); } void gcomm::View::add_members(NodeList::const_iterator begin, NodeList::const_iterator end) { for (NodeList::const_iterator i = begin; i != end; ++i) { gu_trace((void)members_.insert_unique( std::make_pair(NodeList::key(i), NodeList::value(i)))); } } void gcomm::View::add_joined(const UUID& pid, const std::string& name) { gu_trace((void)joined_.insert_unique(std::make_pair(pid, Node()))); } void gcomm::View::add_left(const UUID& pid, const std::string& name) { gu_trace((void)left_.insert_unique(std::make_pair(pid, Node()))); } void gcomm::View::add_partitioned(const UUID& pid, const std::string& name) { gu_trace((void)partitioned_.insert_unique(std::make_pair(pid, Node()))); } const gcomm::NodeList& gcomm::View::members() const { return members_; } const gcomm::NodeList& gcomm::View::joined() const { return joined_; } const gcomm::NodeList& gcomm::View::left() const { return left_; } const gcomm::NodeList& gcomm::View::partitioned() const { return partitioned_; } gcomm::ViewType gcomm::View::type() const { return view_id_.type(); } const gcomm::ViewId& gcomm::View::id() const { return view_id_; } const gcomm::UUID& gcomm::View::representative() const { if (members_.empty()) { return UUID::nil(); } else { return NodeList::key(members_.begin()); } } bool gcomm::View::is_empty() const { return (view_id_.uuid() == UUID::nil() && members_.size() == 0); } bool gcomm::operator==(const gcomm::View& a, const gcomm::View& b) { return (a.id() == b.id() && a.members() == b.members() && a.joined() == b.joined() && a.left() == b.left() && a.partitioned() == b.partitioned()); } size_t gcomm::View::unserialize(const gu::byte_t* buf, const size_t buflen, size_t offset) { gu_trace (offset = view_id_.unserialize (buf, buflen, offset)); gu_trace (offset = members_.unserialize (buf, buflen, offset)); gu_trace (offset = joined_.unserialize (buf, buflen, offset)); gu_trace (offset = left_.unserialize (buf, buflen, offset)); gu_trace (offset = partitioned_.unserialize(buf, buflen, offset)); return offset; } size_t gcomm::View::serialize(gu::byte_t* buf, const size_t buflen, size_t offset) const { gu_trace (offset = view_id_.serialize (buf, buflen, offset)); gu_trace (offset = members_.serialize (buf, buflen, offset)); gu_trace (offset = joined_.serialize (buf, buflen, offset)); gu_trace (offset = left_.serialize (buf, buflen, offset)); gu_trace (offset = partitioned_.serialize(buf, buflen, offset)); return offset; } size_t gcomm::View::serial_size() const { return (view_id_.serial_size() + members_.serial_size() + joined_.serial_size() + left_.serial_size() + partitioned_.serial_size()); } std::ostream& gcomm::operator<<(std::ostream& os, const gcomm::View& view) { os << "view("; if (view.is_empty() == true) { os << "(empty)"; } else { os << view.id(); os << " memb {\n"; os << view.members(); os << "} joined {\n"; os << view.joined(); os << "} left {\n"; os << view.left(); os << "} partitioned {\n"; os << view.partitioned(); os << "}"; } os << ")"; return os; } percona-xtradb-cluster-galera/gcomm/src/gcomm/common.hpp0000644000000000000000000000166212247075736023653 0ustar rootroot00000000000000/* * Copyright (C) 2012 Codership Oy */ /*! * @file common.hpp * * @brief Imports definitions from the global common.h */ #ifndef GCOMM_COMMON_HPP #define GCOMM_COMMON_HPP #if defined(HAVE_COMMON_H) #include #endif #include namespace gcomm { #if defined(HAVE_COMMON_H) static std::string const TCP_SCHEME(COMMON_TCP_SCHEME); static std::string const UDP_SCHEME(COMMON_UDP_SCHEME); static std::string const SSL_SCHEME(COMMON_SSL_SCHEME); static std::string const BASE_PORT_KEY(COMMON_BASE_PORT_KEY); static std::string const BASE_PORT_DEFAULT(COMMON_BASE_PORT_DEFAULT); #else static std::string const TCP_SCHEME("tcp"); static std::string const UDP_SCHEME("udp"); static std::string const SSL_SCHEME("ssl"); static std::string const BASE_PORT_KEY("base_port"); static std::string const BASE_PORT_DEFAULT("4567"); #endif } #endif /* GCOMM_COMMON_HPP */ percona-xtradb-cluster-galera/gcomm/src/gcomm/conf.hpp0000644000000000000000000004412412247075736023310 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ /*! * @file conf.hpp * * @brief Configuration parameters and utility templates. */ #ifndef GCOMM_CONF_HPP #define GCOMM_CONF_HPP #include "gu_config.hpp" #include "gu_uri.hpp" #include "gu_throw.hpp" namespace gcomm { /*! * Configuration parameter definitions. * * Transport definition and configuration parameters are passed to * Transport::create() in the URI form. URI scheme part defines * which transport is returned. Currently recognized are "tcp", "gmcast" * and "pc". This will change in the future. * * URI format is the following: * gcomm://[[:]][?=&=]... * The key/value pairs can be used to pass configuration parameters to * gcomm layers. * * Time periods as parameter values follow ISO8601 duration representation * (as represented in http://en.wikipedia.org/wiki/ISO_8601#Durations). * Examples: * - PT1S - one second * - PT1M30S = one minute 30 secs * - P1DT6H = one day, 6 hours * * To get subsecond resolution, second part can be represented as decimal * number, but currently it is not recommended due to bug in Period * parsing routine (rounding errors can result inaccurate boundary * value checking). */ struct Conf { static std::string const ProtonetBackend; static std::string const ProtonetVersion; /*! * @brief TCP non-blocking flag ("socket.non_blocking") * * Parameter value is boolean (passed 0 or 1) denoting whether * the socket should or should not be in non-blocking state. */ static std::string const TcpNonBlocking; /*! * @brief Use SSL sockets for communication * * Boolean describing whether underlying transport should use SSL * connections. */ static std::string const SocketUseSsl; /*! * @brief File containing CA certificates in PEM format * ("socket.ssl_verify_file") */ static std::string const SocketSslVerifyFile; /*! * @brief File containing certificate to use in PEM format * ("socket.ssl_certificate_file") */ static std::string const SocketSslCertificateFile; /*! * @brief File containing private key associated with certificate * ("socket.ssl_private_key_file") * * If private key file is protected with password, * SocketSslPasswordFile ("socket.ssl_password_file") must also be set. */ static std::string const SocketSslPrivateKeyFile; /*! * @brief File containing password used to protect private key file * ("socket.ssl_password_file") */ static std::string const SocketSslPasswordFile; /*! * @brief Cipher list for SSL connections (socket.ssl_cipher_list) */ static std::string const SocketSslCipherList; /*! * @brief Disable compression boolean (socket.ssl_disable_compression) */ static std::string const SocketSslCompression; /*! * @brief GMCast scheme for transport URI ("gmcast") */ static std::string const GMCastScheme; /*! * @brief GMCast protocol version */ static std::string const GMCastVersion; /*! * @brief GMCast group name ("gmcast.group") * * String denoting group name. Max length of string is 16. Peer nodes * accept GMCast connection only if the group names match. */ static std::string const GMCastGroup; /*! * @brief GMCast listening address ("gmcast.listen_addr") * * Listening address for GMCast. Address is currently passed in * URI format (for example tcp://192.168.3.1:4567) and it should * be passed as the last configuration parameter in order to * avoid confusion. If parameter value is undefined, GMCast * starts listening all interfaces at default port 4567. */ static std::string const GMCastListenAddr; /*! * @brief GMCast multicast address ("gmcast.mcast_addr") * * Multicast address for GMCast. By default multicast socket * is bound to the same interface as conf::GMCastListenAddr. * If multicast interface must be specified, the only way * to do it is currently via listening address configuration. */ static std::string const GMCastMCastAddr; /*! * @brief GMCast multicast port ("gmcast.mcast_port") * * Multicast port for GMCast. By default multicast uses the * same port as GMCast TCP connections. */ static std::string const GMCastMCastPort; /*! * @brief GMCast multicast TTL ("gmcast.mcast_ttl") * * This parameter controls multicast packet TTL. By default it * is set to 1 and usually it should not be changed unless * adviced so. This means that multicast is limited to single LAN * segment. */ static std::string const GMCastMCastTTL; static std::string const GMCastTimeWait; static std::string const GMCastPeerTimeout; /*! * @brief Maximum initial reconnect attempts * * Maximum initial reconnect attempts for address reported by peer. */ static std::string const GMCastMaxInitialReconnectAttempts; /*! * @brief Add or remove peer address. * * Setting value to add:://: will inject new peer * address in address list. Setting value to del:://: * will remove peer address from list (via forget procedure). */ static std::string const GMCastPeerAddr; /*! * @brief Isolate node from peers * * Setting this value to 'true' closes all conections * and will prevent forming of new connections until * value is set again to 'false'. This parameter should be * used for testing purposes only and it will not be visible * in global configuration array. */ static std::string const GMCastIsolate; /*! * @brief EVS scheme for transport URI ("evs") */ static std::string const EvsScheme; /*! * @brief EVS protocol version */ static std::string const EvsVersion; /*! * @brief EVS view forget timeout ("evs.view_forget_timeout") * * This timeout controls how long information about * known group views is maintained. This information is needed * to filter out delayed messages from previous views that are not * live anymore. Default value is 5 minutes and there is usually not * need to change it. */ static std::string const EvsViewForgetTimeout; /*! * @brief EVS suspect timeout ("evs.suspect_timeout") * * This timeout controls how long node can remain silent until * it is put under suspicion. If majority of the current group * agree that the node is under suspicion, it is discarded from * group and new group view is formed immediately. If majority * of the group does not agree about suspicion, Conf::EvsInactiveTimeout * is waited until forming of new group will be attempted. * Default value is 5 seconds. */ static std::string const EvsSuspectTimeout; /*! * @brief EVS inactive timeout ("evs.inactive_timeout") * * This timeout control how long node can remain completely silent * until it is discarded from the group. This is hard limit, unlike * Conf::EvsSuspectTimeout, and the node is discarded even if it * becomes live during the formation of the new group. Default value * is 15 seconds. */ static std::string const EvsInactiveTimeout; /*! * @brief EVS inactive check period ("evs.inactive_check_period") * * This period controls how often node liveness is checked. Default * is 1 second and there is no need to change this unless * Conf::EvsSuspectTimeout or Conf::EvsInactiveTimeout is adjusted * to smaller value. Default value is 1 second, minimum is 0.1 seconds * and maximum is Conf::EvsSuspectTimeout/2. */ static std::string const EvsInactiveCheckPeriod; static std::string const EvsInstallTimeout; /*! * @brief EVS keepalive period ("evs.keepalive_period") * * This timeout controls how often keepalive messages are * sent into network. Node liveness is determined with * these keepalives, so the value sould be significantly smaller * than Conf::EvsSuspectTimeout. Default value is 1 second, * minimum is 0.1 seconds and maximum is Conf::EvsSuspectTimeout/3. */ static std::string const EvsKeepalivePeriod; /*! * @brief EVS join retransmission period ("evs.join_retrans_period") * * This parameter controls how often join messages are retransmitted * during group formation. There is usually no need to adjust * this value. Default value is 0.3 seconds, minimum is 0.1 seconds * and maximum is Conf::EvsSuspectTimeout/3. */ static std::string const EvsJoinRetransPeriod; /*! * @brief EVS statistics reporting period ("evs.stats_report_period") * * This parameters controls how often statistics information is * printed in the log. This parameter has effect only if * statistics reporting is enabled via Conf::EvsInfoLogMask. Default * value is 1 minute. */ static std::string const EvsStatsReportPeriod; /*! * @brief EVS debug log mask ("evs.debug_log_mask") * * This mask controls what debug information is printed in the logs * if debug logging is turned on. Mask value is bitwise-or * from values gcomm::evs::Proto::DebugFlags. By default only * state information is printed. */ static std::string const EvsDebugLogMask; /*! * @brief EVS info log mask ("evs.info_log_mask") * * This mask controls what info log is printed in the logs. * Mask value is bitwise-or from values gcomm::evs::Proto::InfoFlags. */ static std::string const EvsInfoLogMask; /*! * @brief EVS send window ("evs.send_window") * * This parameter controls how many messages protocol layer is * allowed to send without getting all acknowledgements for any of them. * Default value is 32. */ static std::string const EvsSendWindow; /*! * @brief EVS user send window ("evs.user_send_window") * * Like Conf::EvsSendWindow, but for messages for which sending * is initiated by call from upper layer. Default value is 16. */ static std::string const EvsUserSendWindow; /*! * @brief EVS message aggregation mode ("evs.use_aggregate") * * This parameter controls whether EVS is allowed to aggregate * several user messages into one message. By default this option * is enabled and there should be no need to disable it unless * adviced so. */ static std::string const EvsUseAggregate; /*! * @brief Period to generate keepalives for causal messages * */ static std::string const EvsCausalKeepalivePeriod; /*! * @brief EVS maximum install timeouts ("evs.max_install_timeouts") * * This parameter controls how many install attempts are done * before declaring other nodes as inactive and trying to re-establish * group via singleton views. */ static std::string const EvsMaxInstallTimeouts; /*! * @brief PC scheme for transport URI ("pc") */ static std::string const PcScheme; /*! * @brief PC protocol version */ static std::string const PcVersion; /*! * @brief PC split-brain mode * * This parameter controls whether PC is allowed to continue * operation despite of possible split brain condition. */ static std::string const PcIgnoreSb; /*! * @brief PC quorum mode * * This parameter controls whether PC is allowed to continue * operation despite of quorum loss. */ static std::string const PcIgnoreQuorum; /*! * @brief PC message checksumming * * This parameter controls whether PC layer does message * checksumming. */ static std::string const PcChecksum; /*! * @brief PC starup announce timeout */ static std::string const PcAnnounceTimeout; /*! * @brief PC close linger timeout */ static std::string const PcLinger; /*! * @brief PC newer prim view overrides */ static std::string const PcNpvo; /*! * @brief If set during runtime bootstraps new PC */ static std::string const PcBootstrap; /*! * @brief Wait for prim comp unconditionally if set to true */ static std::string const PcWaitPrim; /*! * @brief Timeout on waiting for primary component */ static std::string const PcWaitPrimTimeout; /*! * @brief Node weight in prim comp voting */ static std::string const PcWeight; }; // Helper templates to read configuration parameters. template T _conf_param(const gu::URI& uri, const std::string& param, const T* default_value = 0, const T* min_value = 0, const T* max_value = 0) { T ret; try { ret = gu::from_string(uri.get_option(param)); } catch (gu::NotFound& e) { // cppcheck-suppress nullPointer if (default_value == 0) { gu_throw_error(EINVAL) << "param " << param << " not found from uri " << uri.to_string(); } // cppcheck-suppress nullPointer ret = *default_value; } if (min_value != 0 && *min_value > ret) { gu_throw_error(EINVAL) << "param " << param << " value " << ret << " out of range " << "min allowed " << *min_value; } if (max_value != 0 && *max_value < ret) { gu_throw_error(EINVAL) << "param " << param << " value " << ret << " out of range " << "max allowed " << *max_value; } return ret; } template T conf_param(const gu::URI& uri, const std::string& param) { return _conf_param(uri, param, 0, 0, 0); } template T conf_param_def(const gu::URI& uri, const std::string& param, const T& default_value) { return _conf_param(uri, param, &default_value); } template T conf_param_range(const gu::URI& uri, const std::string& param, const T& min_value, const T& max_value) { return _conf_param(uri, param, 0, &min_value, &max_value); } template T conf_param_def_min(const gu::URI& uri, const std::string& param, const T& default_value, const T& min_value) { return _conf_param(uri, param, &default_value, &min_value); } template T conf_param_def_max(const gu::URI& uri, const std::string& param, const T& default_value, const T& max_value) { return _conf_param(uri, param, &default_value, reinterpret_cast(0), &max_value); } template T conf_param_def_range(const gu::URI& uri, const std::string& param, const T& default_value, const T& min_value, const T& max_value) { return _conf_param(uri, param, &default_value, &min_value, &max_value); } template T param(gu::Config& conf, const gu::URI& uri, const std::string& key, const std::string& def, std::ios_base& (*f)(std::ios_base&) = std::dec) { std::string ret(def); ret = conf.get(key, ret); return gu::from_string(uri.get_option(key, ret), f); } template T check_range(const std::string& key, const T& val, const T& min, const T& max) { if (val < min || val >= max) { gu_throw_error(ERANGE) << "param '" << key << "' value " << val << " out of range [" << min << "," << max << ")"; } return val; } } // namespace gcomm #endif // GCOMM_CONF_HPP percona-xtradb-cluster-galera/gcomm/src/gcomm/datagram.hpp0000644000000000000000000002150212247075736024136 0ustar rootroot00000000000000/* * Copyright (C) 2010-2012 Codership Oy */ #ifndef GCOMM_DATAGRAM_HPP #define GCOMM_DATAGRAM_HPP #include "gu_buffer.hpp" #include "gu_serialize.hpp" #include "gu_utils.hpp" #include #include #include #include namespace gcomm { //! // @class NetHeader // // @brief Header for datagrams sent over network // // Header structure is the following (MSB first) // // | version(4) | reserved(3) | F_CRC(1) | len(24) | // | CRC(32) | // class NetHeader { public: NetHeader() : len_(), crc32_() { } NetHeader(uint32_t len, int version) : len_(len), crc32_(0) { if (len > len_mask_) gu_throw_error(EINVAL) << "msg too long " << len_; len_ |= (static_cast(version) << version_shift_); } uint32_t len() const { return (len_ & len_mask_); } void set_crc32(uint32_t crc32) { crc32_ = crc32; len_ |= F_CRC32; } bool has_crc32() const { return (len_ & F_CRC32); } uint32_t crc32() const { return crc32_; } int version() const { return ((len_ & version_mask_) >> version_shift_); } friend size_t serialize(const NetHeader& hdr, gu::byte_t* buf, size_t buflen, size_t offset); friend size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset, NetHeader& hdr); friend size_t serial_size(const NetHeader& hdr); static const size_t serial_size_ = 8; private: static const uint32_t len_mask_ = 0x00ffffff; static const uint32_t flags_mask_ = 0x0f000000; static const uint32_t flags_shift_ = 24; static const uint32_t version_mask_ = 0xf0000000; static const uint32_t version_shift_ = 28; enum { F_CRC32 = 1 << 24 }; uint32_t len_; uint32_t crc32_; }; inline size_t serialize(const NetHeader& hdr, gu::byte_t* buf, size_t buflen, size_t offset) { offset = gu::serialize4(hdr.len_, buf, buflen, offset); offset = gu::serialize4(hdr.crc32_, buf, buflen, offset); return offset; } inline size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset, NetHeader& hdr) { offset = gu::unserialize4(buf, buflen, offset, hdr.len_); offset = gu::unserialize4(buf, buflen, offset, hdr.crc32_); switch (hdr.version()) { case 0: if ((hdr.len_ & NetHeader::flags_mask_) & ~NetHeader::F_CRC32) { gu_throw_error(EPROTO) << "invalid flags " << ((hdr.len_ & NetHeader::flags_mask_) >> NetHeader::flags_shift_); } break; default: gu_throw_error(EPROTO) << "invalid protocol version " << hdr.version(); } return offset; } inline size_t serial_size(const NetHeader& hdr) { return NetHeader::serial_size_; } /*! * @brief Datagram container * * Datagram class provides consistent interface for managing * datagrams/byte buffers. */ class Datagram { public: Datagram() : header_ (), header_offset_(header_size_), payload_ (new gu::Buffer()), offset_ (0) { } /*! * @brief Construct new datagram from byte buffer * * @param[in] buf Const pointer to data buffer * @param[in] buflen Length of data buffer * * @throws std::bad_alloc */ Datagram(const gu::Buffer& buf, size_t offset = 0) : header_ (), header_offset_(header_size_), payload_ (new gu::Buffer(buf)), offset_ (offset) { assert(offset_ <= payload_->size()); } Datagram(const gu::SharedBuffer& buf, size_t offset = 0) : header_ (), header_offset_(header_size_), payload_ (buf), offset_ (offset) { assert(offset_ <= payload_->size()); } /*! * @brief Copy constructor. * * @note Only for normalized datagrams. * * @param[in] dgram Datagram to make copy from * @param[in] off * @throws std::bad_alloc */ Datagram(const Datagram& dgram, size_t off = std::numeric_limits::max()) : // header_(dgram.header_), header_offset_(dgram.header_offset_), payload_(dgram.payload_), offset_(off == std::numeric_limits::max() ? dgram.offset_ : off) { assert(offset_ <= dgram.len()); memcpy(header_ + header_offset_, dgram.header_ + dgram.header_offset(), dgram.header_len()); } /*! * @brief Destruct datagram */ ~Datagram() { } void normalize() { const gu::SharedBuffer old_payload(payload_); payload_ = gu::SharedBuffer(new gu::Buffer); payload_->reserve(header_len() + old_payload->size() - offset_); if (header_len() > offset_) { payload_->insert(payload_->end(), header_ + header_offset_ + offset_, header_ + header_size_); offset_ = 0; } else { offset_ -= header_len(); } header_offset_ = header_size_; payload_->insert(payload_->end(), old_payload->begin() + offset_, old_payload->end()); offset_ = 0; } gu::byte_t* header() { return header_; } const gu::byte_t* header() const { return header_; } size_t header_size() const { return header_size_; } size_t header_len() const { return (header_size_ - header_offset_); } size_t header_offset() const { return header_offset_; } void set_header_offset(const size_t off) { // assert(off <= header_size_); if (off > header_size_) gu_throw_fatal << "out of hdrspace"; header_offset_ = off; } const gu::Buffer& payload() const { assert(payload_ != 0); return *payload_; } gu::Buffer& payload() { assert(payload_ != 0); return *payload_; } size_t len() const { return (header_size_ - header_offset_ + payload_->size()); } size_t offset() const { return offset_; } private: friend uint16_t crc16(const Datagram&, size_t); friend uint32_t crc32(const Datagram&, size_t); static const size_t header_size_ = 128; gu::byte_t header_[header_size_]; size_t header_offset_; gu::SharedBuffer payload_; size_t offset_; }; inline uint16_t crc16(const Datagram& dg, size_t offset = 0) { boost::crc_16_type crc; assert(offset < dg.len()); gu::byte_t lenb[4]; gu::serialize4(static_cast(dg.len() - offset), lenb, sizeof(lenb), 0); crc.process_block(lenb, lenb + sizeof(lenb)); if (offset < dg.header_len()) { crc.process_block(dg.header_ + dg.header_offset_ + offset, dg.header_ + dg.header_size_); offset = 0; } else { offset -= dg.header_len(); } crc.process_block(&(*dg.payload_)[0] + offset, &(*dg.payload_)[0] + dg.payload_->size()); return crc.checksum(); } inline uint32_t crc32(const Datagram& dg, size_t offset = 0) { boost::crc_32_type crc; gu::byte_t lenb[4]; gu::serialize4(static_cast(dg.len() - offset), lenb, sizeof(lenb), 0); crc.process_block(lenb, lenb + sizeof(lenb)); if (offset < dg.header_len()) { crc.process_block(dg.header_ + dg.header_offset_ + offset, dg.header_ + dg.header_size_); offset = 0; } else { offset -= dg.header_len(); } crc.process_block(&(*dg.payload_)[0] + offset, &(*dg.payload_)[0] + dg.payload_->size()); return crc.checksum(); } } #endif // GCOMM_DATAGRAM_HPP percona-xtradb-cluster-galera/gcomm/src/gcomm/exception.hpp0000644000000000000000000000106612247075736024357 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ /*! * @file exception.hpp * * @brief GComm exception definitions. */ #ifndef GCOMM_EXCEPTION_HPP #define GCOMM_EXCEPTION_HPP #include "gu_throw.hpp" /*! * Assert macro for runtime condition checking. This should be used * for conditions that may depend on external input and are required * to validate correct protocol operation. */ #define gcomm_assert(cond_) \ if ((cond_) == false) gu_throw_fatal << #cond_ << ": " #endif // GCOMM_EXCEPTION_HPP percona-xtradb-cluster-galera/gcomm/src/gcomm/map.hpp0000644000000000000000000001703312247075736023137 0ustar rootroot00000000000000/* * Copyright (C) 2009-2012 Codership Oy */ /*! * @file map.hpp * * This file contains templates that are thin wrappers for std::map * and std::multimap with some extra functionality. */ #ifndef GCOMM_MAP_HPP #define GCOMM_MAP_HPP #include "gu_serialize.hpp" #include #include #include #include "gcomm/exception.hpp" #include "gcomm/types.hpp" namespace gcomm { template class MapBase { typedef C MapType; public: typedef typename MapType::iterator iterator; typedef typename MapType::const_iterator const_iterator; typedef typename MapType::reverse_iterator reverse_iterator; typedef typename MapType::const_reverse_iterator const_reverse_iterator; typedef typename MapType::value_type value_type; typedef typename MapType::const_reference const_reference; typedef typename MapType::key_type key_type; typedef typename MapType::mapped_type mapped_type; protected: MapType map_; public: MapBase() : map_() {} virtual ~MapBase() {} iterator begin() { return map_.begin(); } iterator end() { return map_.end(); } iterator find(const K& k) { return map_.find(k); } iterator find_checked(const K& k) { iterator ret = map_.find(k); if (ret == map_.end()) { gu_throw_fatal << "element " << k << " not found"; } return ret; } iterator lower_bound(const K& k) { return map_.lower_bound(k); } const_iterator begin() const { return map_.begin(); } const_iterator end() const { return map_.end(); } const_reverse_iterator rbegin() const { return map_.rbegin(); } const_reverse_iterator rend() const { return map_.rend(); } const_iterator find(const K& k) const { return map_.find(k); } const_iterator find_checked(const K& k) const { const_iterator ret = map_.find(k); if (ret == map_.end()) { gu_throw_fatal << "element " << k << " not found"; } return ret; } mapped_type& operator[](const key_type& k) { return map_[k]; } void erase(iterator i) { map_.erase(i); } void erase(iterator i, iterator j) { map_.erase(i, j); } void erase(const K& k) { map_.erase(k); } void clear() { map_.clear(); } size_t size() const { return map_.size(); } bool empty() const { return map_.empty(); } size_t serialize(gu::byte_t* const buf, size_t const buflen, size_t offset) const { gu_trace(offset = gu::serialize4( static_cast(size()), buf, buflen, offset)); for (const_iterator i = map_.begin(); i != map_.end(); ++i) { gu_trace(offset = key(i).serialize(buf, buflen, offset)); gu_trace(offset = value(i).serialize(buf, buflen, offset)); } return offset; } size_t unserialize(const gu::byte_t* buf, size_t const buflen, size_t offset) { uint32_t len; // Clear map in case this object is reused map_.clear(); gu_trace(offset = gu::unserialize4(buf, buflen, offset, len));; for (uint32_t i = 0; i < len; ++i) { K k; V v; gu_trace(offset = k.unserialize(buf, buflen, offset)); gu_trace(offset = v.unserialize(buf, buflen, offset)); if (map_.insert(std::make_pair(k, v)).second == false) { gu_throw_fatal << "Failed to unserialize map"; } } return offset; } size_t serial_size() const { return sizeof(uint32_t) + size()*(K::serial_size() + V::serial_size()); } bool operator==(const MapBase& other) const { return (map_ == other.map_); } bool operator!=(const MapBase& other) const { return !(map_ == other.map_); } static const K& key(const_iterator i) { return i->first; } static const K& key(iterator i) { return i->first; } static const V& value(const_iterator i) { return i->second; } static V& value(iterator i) { return i->second; } static const K& key(const value_type& vt) { return vt.first; } static V& value(value_type& vt) { return vt.second; } static const V& value(const value_type& vt) { return vt.second; } }; // @todo For some reason map key must be declared in gcomm namespace // in order this to work. Find out the reason why and fix. template std::ostream& operator<<(std::ostream& os, const std::pair& p) { return (os << "\t" << p.first << "," << p.second << "\n"); } template std::ostream& operator<<(std::ostream& os, const MapBase& map) { std::copy(map.begin(), map.end(), std::ostream_iterator >(os, "")); return os; } template > class Map : public MapBase { public: typedef typename MapBase::iterator iterator; std::pair insert(const std::pair& p) { return MapBase::map_.insert(p); } template void insert(InputIterator first, InputIterator last) { MapBase::map_.insert(first, last); } iterator insert_unique(const typename MapBase::value_type& p) { std::pair ret = MapBase::map_.insert(p); if (false == ret.second) { gu_throw_fatal << "duplicate entry " << "key=" << MapBase::key(p) << " " << "value=" << MapBase::value(p) << " " << "map=" << *this; } return ret.first; } }; template > class MultiMap : public MapBase { public: typedef typename MapBase::iterator iterator; typedef typename MapBase::const_iterator const_iterator; typedef typename MapBase::value_type value_type; typedef typename MapBase::const_reference const_reference; iterator insert(const std::pair& p) { return MapBase::map_.insert(p); } iterator insert(iterator position, const value_type& vt) { return MapBase::map_.insert(position, vt); } std::pair equal_range(const K& k) const { return MapBase::map_.equal_range(k); } }; } #endif /* GCOMM_MAP_HPP */ percona-xtradb-cluster-galera/gcomm/src/gcomm/order.hpp0000644000000000000000000000272412247075736023476 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy * * $Id$ */ /*! * @file order.hpp * * @brief Message order type enumeration. */ #ifndef GCOMM_ORDER_HPP #define GCOMM_ORDER_HPP namespace gcomm { /*! * @brief Message order type enumeration. */ enum Order { /*! Message will not be delivered, for protocol use only. */ O_DROP = 0, /*! Message delivery is unreliable, for protocol use only. */ O_UNRELIABLE = 1, /*! Message will be delivered in source fifo order. */ O_FIFO = 2, /*! * Message will be delivered in same order on all nodes * if it is delivered. */ O_AGREED = 3, /*! * Message will be delivered in safe order, it is guaranteed * that all the nodes in group have received the message. */ O_SAFE = 4, /*! * Message will be delivered only locally and delivery will fulfill the * following property: * * Let M_c be message tagged with O_LOCAL_CAUSAL ordering requirement. * Any message M_a which is delivered on any node so that delivery * has causal precedence on generating M_c will be delivered locally * before M_c. * * Note that the causality is guaranteed only with respect to * already delivered messages. */ O_LOCAL_CAUSAL = 8 }; } #endif // GCOMM_ORDER_HPP percona-xtradb-cluster-galera/gcomm/src/gcomm/protolay.hpp0000644000000000000000000002224512247075736024234 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ /*! * @file protolay.hpp * * @brief Protocol layer interface definitions. * * Protocol layer interface allows construction of protocol stacks * with consistent interface to send messages upwards or downwards in * stack. */ #ifndef GCOMM_PROTOLAY_HPP #define GCOMM_PROTOLAY_HPP #include "gcomm/view.hpp" #include "gcomm/exception.hpp" #include "gcomm/order.hpp" #include "gcomm/datagram.hpp" #include "gu_logger.hpp" #include "gu_datetime.hpp" #include "gu_config.hpp" #include #include #include // Declarations namespace gcomm { /*! * @class ProtoUpMeta * * Container for metadata passed upwards in protocol stack. */ class ProtoUpMeta; std::ostream& operator<<(std::ostream&, const ProtoUpMeta&); /*! * @class ProtoDownMeta * * Container for metadata passed downwards in protocol stack. */ class ProtoDownMeta; /*! * @class Protolay * * Protocol layer interface. */ class Protolay; /*! * @class Toplay * * Protolay that is on the top of the protocol stack. */ class Toplay; /*! * @class Bottomlay * * Protolay that is on the bottom of the protocol stack. */ class Bottomlay; void connect(Protolay*, Protolay*); void disconnect(Protolay*, Protolay*); } /* message context to pass up with the data buffer? */ class gcomm::ProtoUpMeta { public: ProtoUpMeta(const int err_no) : source_(), source_view_id_(), user_type_(), order_(), to_seq_(), err_no_(err_no), view_(0) { } ProtoUpMeta(const UUID source = UUID::nil(), const ViewId source_view_id = ViewId(), const View* view = 0, const uint8_t user_type = 0xff, const Order order = O_DROP, const int64_t to_seq = -1, const int err_no = 0) : source_ (source ), source_view_id_ (source_view_id ), user_type_ (user_type ), order_ (order ), to_seq_ (to_seq ), err_no_ (err_no ), view_ (view != 0 ? new View(*view) : 0) { } ProtoUpMeta(const ProtoUpMeta& um) : source_ (um.source_ ), source_view_id_ (um.source_view_id_ ), user_type_ (um.user_type_ ), order_ (um.order_ ), to_seq_ (um.to_seq_ ), err_no_ (um.err_no_ ), view_ (um.view_ ? new View(*um.view_) : 0) { } ~ProtoUpMeta() { delete view_; } const UUID& source() const { return source_; } const ViewId& source_view_id() const { return source_view_id_; } uint8_t user_type() const { return user_type_; } Order order() const { return order_; } int64_t to_seq() const { return to_seq_; } int err_no() const { return err_no_; } bool has_view() const { return view_ != 0; } const View& view() const { return *view_; } private: ProtoUpMeta& operator=(const ProtoUpMeta&); UUID const source_; ViewId const source_view_id_; uint8_t const user_type_; Order const order_; int64_t const to_seq_; int const err_no_; View* const view_; }; inline std::ostream& gcomm::operator<<(std::ostream& os, const ProtoUpMeta& um) { os << "proto_up_meta: { "; if (not (um.source() == UUID::nil())) { os << "source=" << um.source() << ","; } if (um.source_view_id().type() != V_NONE) { os << "source_view_id=" << um.source_view_id() << ","; } os << "user_type=" << static_cast(um.user_type()) << ","; os << "to_seq=" << um.to_seq() << ","; if (um.has_view() == true) { os << "view=" << um.view(); } os << "}"; return os; } /* message context to pass down? */ class gcomm::ProtoDownMeta { public: ProtoDownMeta(const uint8_t user_type = 0xff, const Order order = O_SAFE, const UUID& uuid = UUID::nil()) : user_type_ (user_type), order_ (order), source_ (uuid) { } uint8_t user_type() const { return user_type_; } Order order() const { return order_; } const UUID& source() const { return source_; } private: const uint8_t user_type_; const Order order_; const UUID source_; }; class gcomm::Protolay { typedef std::list CtxList; CtxList up_context_; CtxList down_context_; Protolay (const Protolay&); Protolay& operator=(const Protolay&); protected: gu::Config& conf_; Protolay(gu::Config& conf) : up_context_(0), down_context_(0), conf_(conf) { } public: virtual ~Protolay() {} virtual void connect(bool) { } virtual void close() { } virtual void close(const UUID& uuid) { } /* apparently handles data from upper layer. what is return value? */ virtual int handle_down (Datagram&, const ProtoDownMeta&) = 0; virtual void handle_up (const void*, const Datagram&, const ProtoUpMeta&) = 0; void set_up_context(Protolay *up) { if (std::find(up_context_.begin(), up_context_.end(), up) != up_context_.end()) { gu_throw_fatal << "up context already exists"; } up_context_.push_back(up); } void set_down_context(Protolay *down) { if (std::find(down_context_.begin(), down_context_.end(), down) != down_context_.end()) { gu_throw_fatal << "down context already exists"; } down_context_.push_back(down); } void unset_up_context(Protolay* up) { CtxList::iterator i; if ((i = std::find(up_context_.begin(), up_context_.end(), up)) == up_context_.end()) { gu_throw_fatal << "up context does not exist"; } up_context_.erase(i); } void unset_down_context(Protolay* down) { CtxList::iterator i; if ((i = std::find(down_context_.begin(), down_context_.end(), down)) == down_context_.end()) { gu_throw_fatal << "down context does not exist"; } down_context_.erase(i); } /* apparently passed data buffer to the upper layer */ void send_up(const Datagram& dg, const ProtoUpMeta& up_meta) { if (up_context_.empty() == true) { gu_throw_fatal << this << " up context(s) not set"; } CtxList::iterator i, i_next; for (i = up_context_.begin(); i != up_context_.end(); i = i_next) { i_next = i, ++i_next; (*i)->handle_up(this, dg, up_meta); } } /* apparently passes data buffer to lower layer, what is return value? */ int send_down(Datagram& dg, const ProtoDownMeta& down_meta) { if (down_context_.empty() == true) { log_warn << this << " down context(s) not set"; return ENOTCONN; } int ret = 0; for (CtxList::iterator i = down_context_.begin(); i != down_context_.end(); ++i) { const size_t hdr_offset(dg.header_offset()); int err = (*i)->handle_down(dg, down_meta); // Verify that lower layer rolls back any modifications to // header if (hdr_offset != dg.header_offset()) { gu_throw_fatal; } if (err != 0) { ret = err; } } return ret; } virtual void handle_stable_view(const View& view) { } void set_stable_view(const View& view) { for (CtxList::iterator i(down_context_.begin()); i != down_context_.end(); ++i) { (*i)->handle_stable_view(view); } } virtual gu::datetime::Date handle_timers() { return gu::datetime::Date::max(); } virtual bool set_param(const std::string& key, const std::string& val) { return false; } const Protolay* id() const { return this; } }; class gcomm::Toplay : public Protolay { public: Toplay(gu::Config& conf) : Protolay(conf) { } private: int handle_down(Datagram& dg, const ProtoDownMeta& dm) { gu_throw_fatal << "Toplay handle_down() called"; } }; class gcomm::Bottomlay : public Protolay { public: Bottomlay(gu::Config& conf) : Protolay(conf) { } private: void handle_up(const void* id, const Datagram&, const ProtoUpMeta& um) { gu_throw_fatal << "Bottomlay handle_up() called"; } }; inline void gcomm::connect(Protolay* down, Protolay* up) { down->set_up_context(up); up->set_down_context(down); } inline void gcomm::disconnect(Protolay* down, Protolay* up) { down->unset_up_context(up); up->unset_down_context(down); } #endif /* GCOMM_PROTOLAY_HPP */ percona-xtradb-cluster-galera/gcomm/src/gcomm/protonet.hpp0000644000000000000000000000522712247075736024236 0ustar rootroot00000000000000// // Copyright (C) 2009 Codership Oy // //! // @file protonet.hpp // // This file defines protonet interface used by gcomm. // #ifndef GCOMM_PROTONET_HPP #define GCOMM_PROTONET_HPP #include "gu_uri.hpp" #include "gu_datetime.hpp" #include "protostack.hpp" #include "gu_config.hpp" #include "socket.hpp" #include #ifndef GCOMM_PROTONET_MAX_VERSION #define GCOMM_PROTONET_MAX_VERSION 0 #endif // GCOMM_PROTONET_MAX_VERSION namespace gcomm { // Forward declarations class Protonet; } //! // Abstract Protonet interface class // class gcomm::Protonet { public: Protonet(gu::Config& conf, const std::string& type, int version) : protos_ (), version_(version), conf_ (conf), type_ (type) { } virtual ~Protonet() { } //! // Insert Protostack to be handled by Protonet // // @param pstack Pointer to Protostack // void insert(Protostack* pstack); //! // Erase Protostack from Protonet to stop dispatching events // to Protostack // // @param pstack Pointer to Protostack // void erase(Protostack* pstack); //! // Create new Socket // // @param uri URI to specify Socket type // // @return Socket // virtual gcomm::SocketPtr socket(const gu::URI& uri) = 0; //! // Create new Acceptor // // @param uri URI to specify Acceptor type // // @return Acceptor // virtual Acceptor* acceptor(const gu::URI& uri) = 0; //! // Dispatch events until period p has passed or event // loop is interrupted. // // @param p Period to run event_loop(), negative value means forever // virtual void event_loop(const gu::datetime::Period& p) = 0; //! // Iterate over Protostacks and handle timers // // @return Time of next known timer expiration // gu::datetime::Date handle_timers(); //! // Interrupt event loop // virtual void interrupt() = 0; //! // Enter Protonet critical section // virtual void enter() = 0; //! // Leave Protonet critical section // virtual void leave() = 0; bool set_param(const std::string& key, const std::string& val); gu::Config& conf() { return conf_; } //! // Factory method for creating Protonets // static Protonet* create(gu::Config& conf); const std::string& type() const { return type_; } virtual size_t mtu() const = 0; protected: std::deque protos_; int version_; static const int max_version_ = GCOMM_PROTONET_MAX_VERSION; gu::Config& conf_; private: std::string type_; }; #endif // GCOMM_PROTONET_HPP percona-xtradb-cluster-galera/gcomm/src/gcomm/protostack.hpp0000644000000000000000000000154212247075736024551 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #ifndef GCOMM_PROTOSTACK_HPP #define GCOMM_PROTOSTACK_HPP #include "gcomm/protolay.hpp" #include "gu_lock.hpp" #include namespace gcomm { class Socket; class Acceptor; class Protostack; class Protonet; class BoostProtonet; } class gcomm::Protostack { public: Protostack() : protos_(), mutex_() { } void push_proto(Protolay* p); void pop_proto(Protolay* p); gu::datetime::Date handle_timers(); void dispatch(const void* id, const Datagram& dg, const ProtoUpMeta& um); bool set_param(const std::string&, const std::string&); void enter() { mutex_.lock(); } void leave() { mutex_.unlock(); } private: friend class Protonet; std::deque protos_; gu::Mutex mutex_; }; #endif // GCOMM_PROTOSTACK_HPP percona-xtradb-cluster-galera/gcomm/src/gcomm/transport.hpp0000644000000000000000000000505112247075736024413 0ustar rootroot00000000000000/* * Copyright (C) 2009-2012 Codership Oy */ /*! * @file transport.hpp * * @brief Transport interface. */ #ifndef _GCOMM_TRANSPORT_HPP_ #define _GCOMM_TRANSPORT_HPP_ #include "gcomm/uuid.hpp" #include "gcomm/protolay.hpp" #include "gcomm/protostack.hpp" #include "gcomm/protonet.hpp" #include "gu_uri.hpp" namespace gcomm { /*! * @class Transport * * @brief Transport interface */ class Transport; } /*! * */ class gcomm::Transport : public Protolay { public: virtual ~Transport(); virtual size_t mtu() const = 0; virtual const UUID& uuid() const = 0; virtual std::string local_addr() const; virtual std::string remote_addr() const; int err_no() const; virtual void connect(bool start_prim) { gu_throw_fatal << "connect(start_prim) not supported"; } virtual void connect() // if not overloaded, will default to connect(bool) { connect(false); } virtual void connect(const gu::URI& uri) { gu_throw_fatal << "connect(URI) not supported"; } virtual void close(bool force = false) = 0; virtual void close(const UUID& uuid) { gu_throw_error(ENOTSUP) << "close(UUID) not supported by " << uri_.get_scheme(); } virtual void listen(); virtual std::string listen_addr() const { gu_throw_fatal << "not supported"; } virtual Transport* accept(); virtual void handle_accept(Transport*) { gu_throw_error(ENOTSUP) << "handle_accept() not supported by" << uri_.get_scheme(); } virtual void handle_connect() { gu_throw_error(ENOTSUP) << "handle_connect() not supported by" << uri_.get_scheme(); } virtual int handle_down(Datagram&, const ProtoDownMeta&) = 0; virtual void handle_up (const void*, const Datagram&, const ProtoUpMeta&) = 0; virtual void handle_stable_view(const View& view) { } Protostack& pstack() { return pstack_; } Protonet& pnet() { return pnet_; } static Transport* create(Protonet&, const std::string&); static Transport* create(Protonet&, const gu::URI&); protected: Transport (Protonet&, const gu::URI&); Protostack pstack_; Protonet& pnet_; gu::URI uri_; int error_no_; private: Transport (const Transport&); Transport& operator=(const Transport&); }; #endif // _GCOMM_TRANSPORT_HPP_ percona-xtradb-cluster-galera/gcomm/src/gcomm/types.hpp0000644000000000000000000000431012247075736023520 0ustar rootroot00000000000000/* * Copyright (C) 2009-2012 Codership Oy */ #ifndef _GCOMM_TYPES_HPP_ #define _GCOMM_TYPES_HPP_ #include "gcomm/exception.hpp" #include "gu_byteswap.hpp" #include "gu_buffer.hpp" #include #include #include namespace gcomm { template class String { public: String(const std::string& str = "") : str_(str) { if (str_.size() > str_size_) { gu_throw_error(EMSGSIZE); } } virtual ~String() { } size_t serialize(gu::byte_t* buf, size_t buflen, size_t offset) const { if (buflen < offset + str_size_) { gu_throw_error (EMSGSIZE) << str_size_ << " > " << (buflen-offset); } std::string ser_str(str_); ser_str.resize(str_size_, '\0'); (void)std::copy(ser_str.data(), ser_str.data() + ser_str.size(), buf + offset); return offset + str_size_; } size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset) { if (buflen < offset + str_size_) { gu_throw_error (EMSGSIZE) << str_size_ << " > " << (buflen-offset); } str_.assign(reinterpret_cast(buf) + offset, str_size_); const size_t tc(str_.find_first_of('\0')); if (tc != std::string::npos) { str_.resize(tc); } return offset + str_size_; } static size_t serial_size() { return str_size_; } const std::string& to_string() const { return str_; } bool operator==(const String& cmp) const { return (str_ == cmp.str_); } private: static const size_t str_size_ = SZ ; std::string str_; /* Human readable name if any */ }; template inline std::ostream& operator<<(std::ostream& os, const String& str) { return (os << str.to_string()); } } // namespace gcomm #endif /* _GCOMM_TYPES_HPP_ */ percona-xtradb-cluster-galera/gcomm/src/gcomm/util.hpp0000644000000000000000000000520212247075736023332 0ustar rootroot00000000000000/* * Copyright (C) 2012 Codership Oy */ #ifndef _GCOMM_UTIL_HPP_ #define _GCOMM_UTIL_HPP_ #include "gcomm/datagram.hpp" #include "gu_logger.hpp" #include "gu_throw.hpp" #include namespace gcomm { inline std::string uri_string (const std::string& scheme, const std::string& addr, const std::string& port = std::string("")) { if (port.length() > 0) return (scheme + "://" + addr + ':' + port); else return (scheme + "://" + addr); } inline bool host_is_any (const std::string& host) { return (host.length() == 0 || host == "0.0.0.0" || host.find ("::/128") <= 1); } template size_t serialize(const C& c, gu::Buffer& buf) { const size_t prev_size(buf.size()); buf.resize(buf.size() + c.serial_size()); size_t ret; gu_trace(ret = c.serialize(&buf[0] + prev_size, buf.size(), prev_size)); assert(ret == prev_size + c.serial_size()); return ret; } template size_t unserialize(const gu::Buffer& buf, size_t offset, C& c) { size_t ret; gu_trace(ret = c.unserialize(buf, buf.size(), offset)); return ret; } template void push_header(const M& msg, Datagram& dg) { if (dg.header_offset() < msg.serial_size()) { gu_throw_fatal; } msg.serialize(dg.header(), dg.header_size(), dg.header_offset() - msg.serial_size()); dg.set_header_offset(dg.header_offset() - msg.serial_size()); } template void pop_header(const M& msg, Datagram& dg) { assert(dg.header_size() >= dg.header_offset() + msg.serial_size()); dg.set_header_offset(dg.header_offset() + msg.serial_size()); } inline const gu::byte_t* begin(const Datagram& dg) { return (dg.offset() < dg.header_len() ? dg.header() + dg.header_offset() + dg.offset() : &dg.payload()[0] + (dg.offset() - dg.header_len())); } inline size_t available(const Datagram& dg) { return (dg.offset() < dg.header_len() ? dg.header_len() - dg.offset() : dg.payload().size() - (dg.offset() - dg.header_len())); } template class Critical { public: Critical(M& monitor) : monitor_(monitor) { monitor_.enter(); } ~Critical() { monitor_.leave(); } private: M& monitor_; }; } // namespace gcomm #endif // _GCOMM_UTIL_HPP_ percona-xtradb-cluster-galera/gcomm/src/gcomm/uuid.hpp0000644000000000000000000000704012247075736023325 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #ifndef _GCOMM_UUID_HPP_ #define _GCOMM_UUID_HPP_ #include "gcomm/exception.hpp" #include "gcomm/types.hpp" #include "gu_utils.hpp" #include "gu_assert.hpp" #include "gu_byteswap.h" #include "gu_uuid.h" #include #include namespace gcomm { class UUID; std::ostream& operator<<(std::ostream&, const UUID&); } class gcomm::UUID { public: UUID() : uuid_(GU_UUID_NIL) {} UUID(const void* node, const size_t node_len) : uuid_() { gu_uuid_generate(&uuid_, node, node_len); } UUID(const int32_t idx) : uuid_() { assert(idx > 0); uuid_ = GU_UUID_NIL; memcpy(&uuid_, &idx, sizeof(idx)); } static const UUID& nil() { return uuid_nil_; } size_t unserialize(const gu::byte_t* buf, const size_t buflen, const size_t offset) { if (buflen < offset + sizeof(gu_uuid_t)) gu_throw_error (EMSGSIZE) << sizeof(gu_uuid_t) << " > " << (buflen - offset); memcpy(&uuid_, buf + offset, sizeof(gu_uuid_t)); return offset + sizeof(gu_uuid_t); } size_t serialize(gu::byte_t* buf, const size_t buflen, const size_t offset) const { if (buflen < offset + sizeof(gu_uuid_t)) gu_throw_error (EMSGSIZE) << sizeof(gu_uuid_t) << " > " << (buflen - offset); memcpy(buf + offset, &uuid_, sizeof(gu_uuid_t)); return offset + sizeof(gu_uuid_t); } static size_t serial_size() { return sizeof(gu_uuid_t); } const gu_uuid_t* uuid_ptr() const { return &uuid_; } bool operator<(const UUID& cmp) const { return (gu_uuid_compare(&uuid_, &cmp.uuid_) < 0); } bool operator==(const UUID& cmp) const { return (gu_uuid_compare(&uuid_, &cmp.uuid_) == 0); } bool older(const UUID& cmp) const { return (gu_uuid_older(&uuid_, &cmp.uuid_) > 0); } std::ostream& to_stream(std::ostream& os) const { static const char buf[37] = { 0, }; const uint32_t* i = reinterpret_cast(uuid_.data); if (i[0] != 0 && memcmp(i + 1, buf, sizeof(uuid_) - sizeof(*i)) == 0) { // if all of UUID is contained in the first 4 bytes os << i[0]; // should this be converted to certain endianness? } else { const uint16_t* s = reinterpret_cast(uuid_.data); std::ios_base::fmtflags saved = os.flags(); os << std::hex << std::setfill('0') << std::setw(8) << gu_be32(i[0]) << '-' << std::setfill('0') << std::setw(4) << gu_be16(s[2]) << '-' << std::setfill('0') << std::setw(4) << gu_be16(s[3]) << '-' << std::setfill('0') << std::setw(4) << gu_be16(s[4]) << '-' << std::setfill('0') << std::setw(4) << gu_be16(s[5]) << std::setfill('0') << std::setw(8) << gu_be32(i[3]); os.flags(saved); } return os; } // Prefer the above function over this one std::string _str() const { std::ostringstream os; to_stream(os); return os.str(); } private: gu_uuid_t uuid_; static const UUID uuid_nil_; UUID(gu_uuid_t uuid) : uuid_(uuid) {} }; inline std::ostream& gcomm::operator<<(std::ostream& os, const UUID& uuid) { return uuid.to_stream (os); } #endif // _GCOMM_UUID_HPP_ percona-xtradb-cluster-galera/gcomm/src/gcomm/view.hpp0000644000000000000000000001233012247075736023327 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ /*! * @file Group view class (used in the ProtoUpMeta (protolay.hpp) */ #ifndef _GCOMM_VIEW_HPP_ #define _GCOMM_VIEW_HPP_ #include "gcomm/uuid.hpp" #include "gcomm/types.hpp" #include "gcomm/map.hpp" namespace gcomm { typedef enum { V_NONE = -1, V_REG = 0, V_TRANS = 1, V_NON_PRIM = 2, V_PRIM = 3 } ViewType; class ViewId { public: ViewId(const ViewType type = V_NONE, const UUID& uuid = UUID::nil(), const uint32_t seq = 0) : type_(type), uuid_(uuid), seq_ (seq) { } ViewId(const ViewType type, const ViewId& vi) : type_(type), uuid_(vi.uuid()), seq_ (vi.seq()) { } virtual ~ViewId() { } ViewType type() const { return type_; } const UUID& uuid() const { return uuid_; } uint32_t seq() const { return seq_; } size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset); size_t serialize(gu::byte_t* buf, size_t buflen, size_t offset) const; static size_t serial_size() { return UUID::serial_size() + sizeof(reinterpret_cast(0)->seq_); } bool operator<(const ViewId& cmp) const { // View ordering: // 1) view seq less than // 2) uuid newer than // 3) type less than return (seq_ < cmp.seq_ || (seq_ == cmp.seq_ && (cmp.uuid_.older(uuid_) || (uuid_ == cmp.uuid_ && type_ < cmp.type_) ) ) ); } bool operator==(const ViewId& cmp) const { return (seq_ == cmp.seq_ && type_ == cmp.type_ && uuid_ == cmp.uuid_); } bool operator!=(const ViewId& cmp) const { return !(*this == cmp); } private: ViewType type_; UUID uuid_; // uniquely identifies the sequence of group views (?) uint32_t seq_; // position in the sequence (?) }; std::ostream& operator<<(std::ostream&, const ViewId&); class Node : public String<16> { public: Node(const std::string& name = "") : String<16>(name) { } bool operator==(const Node& cmp) const { return true; } }; inline std::ostream& operator<<(std::ostream& os, const Node& n) { return (os << ""); } class NodeList : public gcomm::Map { }; class View { public: View() : bootstrap_ (false), view_id_ (V_NONE), members_ (), joined_ (), left_ (), partitioned_ () { } View(const ViewId& view_id, bool bootstrap = false) : bootstrap_ (bootstrap), view_id_ (view_id), members_ (), joined_ (), left_ (), partitioned_ () { } ~View() {} void add_member (const UUID& pid, const std::string& name = ""); void add_members (NodeList::const_iterator begin, NodeList::const_iterator end); void add_joined (const UUID& pid, const std::string& name); void add_left (const UUID& pid, const std::string& name); void add_partitioned (const UUID& pid, const std::string& name); const NodeList& members () const; const NodeList& joined () const; const NodeList& left () const; const NodeList& partitioned () const; NodeList& members() { return members_; } bool is_member(const UUID& uuid) const { return members_.find(uuid) != members_.end(); } bool is_joining(const UUID& uuid) const { return joined_.find(uuid) != joined_.end(); } bool is_leaving(const UUID& uuid) const { return left_.find(uuid) != left_.end(); } bool is_partitioning(const UUID& uuid) const { return partitioned_.find(uuid) != partitioned_.end(); } ViewType type () const; const ViewId& id () const; const UUID& representative () const; bool is_empty() const; bool is_bootstrap() const { return bootstrap_; } size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset); size_t serialize(gu::byte_t* buf, size_t buflen, size_t offset) const; size_t serial_size() const; private: bool bootstrap_; // Flag indicating if view was bootstrapped ViewId view_id_; // View identifier NodeList members_; // List of members in view NodeList joined_; // List of newly joined members in view NodeList left_; // Fracefully left members from previous view NodeList partitioned_; // Partitioned members from previous view }; bool operator==(const gcomm::View&, const gcomm::View&); std::ostream& operator<<(std::ostream&, const View&); } // namespace gcomm #endif // _GCOMM_VIEW_HPP_ percona-xtradb-cluster-galera/gcomm/test/SConscript0000644000000000000000000000152112247075736022744 0ustar rootroot00000000000000 Import('check_env') env = check_env.Clone() env.Prepend(LIBS=File('#/galerautils/src/libgalerautils.a')) env.Prepend(LIBS=File('#/galerautils/src/libgalerautils++.a')) env.Prepend(LIBS=File('#/gcomm/src/libgcomm.a')) gcomm_check = env.Program(target = 'check_gcomm', source = Split(''' check_gcomm.cpp check_trace.cpp check_types.cpp check_util.cpp check_gmcast.cpp check_evs2.cpp check_pc.cpp ''')) env.Test("gcomm_check.passed", gcomm_check) Clean(gcomm_check, '#/check_gcomm.log') ssl_test = env.Program(target = 'ssl_test', source = ['ssl_test.cpp']) percona-xtradb-cluster-galera/gcomm/test/check_evs2.cpp0000644000000000000000000013763612247075736023473 0ustar rootroot00000000000000/* * Copyright (C) 2009-2012 Codership Oy * * $Id$ */ /*! * @file Unit tests for refactored EVS */ #include "evs_proto.hpp" #include "evs_input_map2.hpp" #include "evs_message2.hpp" #include "evs_seqno.hpp" #include "check_gcomm.hpp" #include "check_templ.hpp" #include "check_trace.hpp" #include "gcomm/conf.hpp" #include #include #include #include "check.h" using namespace std; using namespace std::rel_ops; using namespace gu; using namespace gu::datetime; using namespace gcomm; using namespace gcomm::evs; void init_rand() { unsigned int seed(static_cast(time(0))); log_info << "rand seed " << seed; srand(seed); } void init_rand(unsigned int seed) { log_info << "rand seed " << seed; srand(seed); } START_TEST(test_range) { log_info << "START"; Range r(3, 6); check_serialization(r, 2 * sizeof(seqno_t), Range()); } END_TEST START_TEST(test_message) { log_info << "START"; UUID uuid1(0, 0); ViewId view_id(V_TRANS, uuid1, 4567); seqno_t seq(478), aru_seq(456), seq_range(7); UserMessage um(0, uuid1, view_id, seq, aru_seq, seq_range, O_SAFE, 75433, 0xab, Message::F_SOURCE); fail_unless(um.serial_size() % 4 == 0); check_serialization(um, um.serial_size(), UserMessage()); AggregateMessage am(0xab, 17457, 0x79); check_serialization(am, 4, AggregateMessage()); DelegateMessage dm(0, uuid1, view_id); dm.set_source(uuid1); check_serialization(dm, dm.serial_size(), DelegateMessage()); MessageNodeList node_list; node_list.insert(make_pair(uuid1, MessageNode())); node_list.insert(make_pair(UUID(2), MessageNode(true, false, 1, ViewId(V_REG), 5, Range(7, 8)))); JoinMessage jm(0, uuid1, view_id, 8, 5, 27, node_list); jm.set_source(uuid1); check_serialization(jm, jm.serial_size(), JoinMessage()); InstallMessage im(0, uuid1, view_id, ViewId(V_REG, view_id.uuid(), view_id.seq()), 8, 5, 27, node_list); im.set_source(uuid1); check_serialization(im, im.serial_size(), InstallMessage()); LeaveMessage lm(0, uuid1, view_id, 45, 88, 3456); lm.set_source(uuid1); check_serialization(lm, lm.serial_size(), LeaveMessage()); } END_TEST START_TEST(test_input_map_insert) { log_info << "START"; UUID uuid1(1), uuid2(2); InputMap im; ViewId view(V_REG, uuid1, 0); try { im.insert(0, UserMessage(0, uuid1, view, 0)); fail(""); } catch (...) { } im.reset(1); im.insert(0, UserMessage(0, uuid1, view, 0)); im.clear(); im.reset(2); for (seqno_t s = 0; s < 10; ++s) { im.insert(0, UserMessage(0, uuid1, view, s)); im.insert(1, UserMessage(0, uuid2, view, s)); } for (seqno_t s = 0; s < 10; ++s) { InputMap::iterator i = im.find(0, s); fail_if(i == im.end()); fail_unless(InputMapMsgIndex::value(i).msg().source() == uuid1); fail_unless(InputMapMsgIndex::value(i).msg().seq() == s); i = im.find(1, s); fail_if(i == im.end()); fail_unless(InputMapMsgIndex::value(i).msg().source() == uuid2); fail_unless(InputMapMsgIndex::value(i).msg().seq() == s); } } END_TEST START_TEST(test_input_map_find) { log_info << "START"; InputMap im; UUID uuid1(1); ViewId view(V_REG, uuid1, 0); im.reset(1); im.insert(0, UserMessage(0, uuid1, view, 0)); fail_if(im.find(0, 0) == im.end()); im.insert(0, UserMessage(0, uuid1, view, 2)); im.insert(0, UserMessage(0, uuid1, view, 4)); im.insert(0, UserMessage(0, uuid1, view, 7)); fail_if(im.find(0, 2) == im.end()); fail_if(im.find(0, 4) == im.end()); fail_if(im.find(0, 7) == im.end()); fail_unless(im.find(0, 3) == im.end()); fail_unless(im.find(0, 5) == im.end()); fail_unless(im.find(0, 6) == im.end()); fail_unless(im.find(0, 8) == im.end()); } END_TEST START_TEST(test_input_map_safety) { log_info << "START"; InputMap im; UUID uuid1(1); size_t index1(0); ViewId view(V_REG, uuid1, 0); im.reset(1); im.insert(index1, UserMessage(0, uuid1, view, 0)); fail_unless(im.aru_seq() == 0); im.insert(index1, UserMessage(0, uuid1, view, 1)); fail_unless(im.aru_seq() == 1); im.insert(index1, UserMessage(0, uuid1, view, 2)); fail_unless(im.aru_seq() == 2); im.insert(index1, UserMessage(0, uuid1, view, 3)); fail_unless(im.aru_seq() == 3); im.insert(index1, UserMessage(0, uuid1, view, 5)); fail_unless(im.aru_seq() == 3); im.insert(index1, UserMessage(0, uuid1, view, 4)); fail_unless(im.aru_seq() == 5); InputMap::iterator i = im.find(index1, 0); fail_unless(im.is_fifo(i) == true); fail_unless(im.is_agreed(i) == true); fail_if(im.is_safe(i) == true); im.set_safe_seq(index1, 0); fail_unless(im.is_safe(i) == true); im.set_safe_seq(index1, 5); i = im.find(index1, 5); fail_unless(im.is_safe(i) == true); im.insert(index1, UserMessage(0, uuid1, view, 7)); im.set_safe_seq(index1, im.aru_seq()); i = im.find(index1, 7); fail_if(im.is_safe(i) == true); } END_TEST START_TEST(test_input_map_erase) { log_info << "START"; InputMap im; size_t index1(0); UUID uuid1(1); ViewId view(V_REG, uuid1, 1); im.reset(1); for (seqno_t s = 0; s < 10; ++s) { im.insert(index1, UserMessage(0, uuid1, view, s)); } for (seqno_t s = 0; s < 10; ++s) { InputMap::iterator i = im.find(index1, s); fail_unless(i != im.end()); im.erase(i); i = im.find(index1, s); fail_unless(i == im.end()); (void)im.recover(index1, s); } im.set_safe_seq(index1, 9); try { im.recover(index1, 9); fail(""); } catch (...) { } } END_TEST START_TEST(test_input_map_overwrap) { log_info << "START"; InputMap im; const size_t n_nodes(5); ViewId view(V_REG, UUID(1), 1); vector uuids; for (size_t n = 0; n < n_nodes; ++n) { uuids.push_back(UUID(static_cast(n + 1))); } im.reset(n_nodes); Date start(Date::now()); size_t cnt(0); seqno_t last_safe(-1); for (seqno_t seq = 0; seq < 100000; ++seq) { for (size_t i = 0; i < n_nodes; ++i) { UserMessage um(0, uuids[i], view, seq); (void)im.insert(i, um); if ((seq + 5) % 10 == 0) { last_safe = um.seq() - 3; im.set_safe_seq(i, last_safe); for (InputMap::iterator ii = im.begin(); ii != im.end() && im.is_safe(ii) == true; ii = im.begin()) { im.erase(ii); } } cnt++; } gcomm_assert(im.aru_seq() == seq); gcomm_assert(im.safe_seq() == last_safe); } Date stop(Date::now()); double div(double(stop.get_utc() - start.get_utc())/gu::datetime::Sec); log_info << "input map msg rate " << double(cnt)/div; } END_TEST class InputMapInserter { public: InputMapInserter(InputMap& im) : im_(im) { } void operator()(const pair& p) const { im_.insert(p.first, p.second); } private: InputMap& im_; }; START_TEST(test_input_map_random_insert) { log_info << "START"; init_rand(); seqno_t window(1024); seqno_t n_seqnos(1024); size_t n_uuids(4); vector uuids(n_uuids); vector > msgs(static_cast(n_uuids*n_seqnos)); ViewId view_id(V_REG, UUID(1), 1); InputMap im; for (size_t i = 0; i < n_uuids; ++i) { uuids[i] = (static_cast(i + 1)); } im.reset(n_uuids, window); for (seqno_t j = 0; j < n_seqnos; ++j) { for (size_t i = 0; i < n_uuids; ++i) { msgs[static_cast(j*n_uuids) + i] = make_pair(i, UserMessage(0, uuids[i], view_id, j)); } } vector > random_msgs(msgs); random_shuffle(random_msgs.begin(), random_msgs.end()); for_each(random_msgs.begin(), random_msgs.end(), InputMapInserter(im)); size_t n = 0; for (InputMap::iterator i = im.begin(); i != im.end(); ++i) { const InputMapMsg& msg(InputMapMsgIndex::value(i)); fail_unless(msg.msg() == msgs[n].second); fail_if(im.is_safe(i) == true); ++n; } fail_unless(im.aru_seq() == n_seqnos - 1); fail_unless(im.safe_seq() == -1); for (size_t i = 0; i < n_uuids; ++i) { fail_unless(im.range(i) == Range(n_seqnos, n_seqnos - 1)); im.set_safe_seq(i, n_seqnos - 1); } fail_unless(im.safe_seq() == n_seqnos - 1); } END_TEST static Datagram* get_msg(DummyTransport* tp, Message* msg, bool release = true) { Datagram* rb = tp->out(); if (rb != 0) { gu_trace(Proto::unserialize_message(tp->uuid(), *rb, msg)); if (release == true) { delete rb; } } return rb; } static void single_join(DummyTransport* t, Proto* p) { Message jm, im, gm; // Initial state is joining p->shift_to(Proto::S_JOINING); // Send join must produce emitted join message p->send_join(); Datagram* rb = get_msg(t, &jm); fail_unless(rb != 0); fail_unless(jm.type() == Message::T_JOIN); // Install message is emitted at the end of JOIN handling // 'cause this is the only instance and is always consistent // with itself rb = get_msg(t, &im); fail_unless(rb != 0); fail_unless(im.type() == Message::T_INSTALL); // Handling INSTALL message emits three gap messages, // one for receiving install message (commit gap), one for // shift to install and one for shift to operational rb = get_msg(t, &gm); fail_unless(rb != 0); fail_unless(gm.type() == Message::T_GAP); fail_unless((gm.flags() & Message::F_COMMIT) != 0); rb = get_msg(t, &gm); fail_unless(rb != 0); fail_unless(gm.type() == Message::T_GAP); fail_unless((gm.flags() & Message::F_COMMIT) == 0); rb = get_msg(t, &gm); fail_unless(rb != 0); fail_unless(gm.type() == Message::T_GAP); fail_unless((gm.flags() & Message::F_COMMIT) == 0); // State must have evolved JOIN -> S_GATHER -> S_INSTALL -> S_OPERATIONAL fail_unless(p->state() == Proto::S_OPERATIONAL); // Handle join message again, must stay in S_OPERATIONAL, must not // emit anything p->handle_msg(jm); rb = get_msg(t, &gm); fail_unless(rb == 0); fail_unless(p->state() == Proto::S_OPERATIONAL); } class DummyUser : public Toplay { public: DummyUser(gu::Config& conf) : Toplay(conf) { } void handle_up(const void*, const Datagram&, const ProtoUpMeta&) { } private: }; START_TEST(test_proto_single_join) { log_info << "START"; gu::Config conf; UUID uuid(1); DummyTransport t(uuid); DummyUser u(conf); Proto p(conf, uuid); gcomm::connect(&t, &p); gcomm::connect(&p, &u); single_join(&t, &p); } END_TEST static void double_join(DummyTransport* t1, Proto* p1, DummyTransport* t2, Proto* p2) { Message jm; Message im; Message gm; Message gm2; Message msg; Datagram* rb; // Initial states check p2->shift_to(Proto::S_JOINING); fail_unless(p1->state() == Proto::S_OPERATIONAL); fail_unless(p2->state() == Proto::S_JOINING); // Send join message, don't self handle immediately // Expected output: one join message p2->send_join(false); fail_unless(p2->state() == Proto::S_JOINING); rb = get_msg(t2, &jm); fail_unless(rb != 0); fail_unless(jm.type() == Message::T_JOIN); rb = get_msg(t2, &msg); fail_unless(rb == 0); // Handle node 2's join on node 1 // Expected output: shift to S_GATHER and one join message p1->handle_msg(jm); fail_unless(p1->state() == Proto::S_GATHER); rb = get_msg(t1, &jm); fail_unless(rb != 0); fail_unless(jm.type() == Message::T_JOIN); rb = get_msg(t1, &msg); fail_unless(rb == 0); // Handle node 1's join on node 2 // Expected output: shift to S_GATHER and one join message p2->handle_msg(jm); fail_unless(p2->state() == Proto::S_GATHER); rb = get_msg(t2, &jm); fail_unless(rb != 0); fail_unless(jm.type() == Message::T_JOIN); rb = get_msg(t2, &msg); fail_unless(rb == 0); // Handle node 2's join on node 1 // Expected output: Install and commit gap messages, state stays in S_GATHER p1->handle_msg(jm); fail_unless(p1->state() == Proto::S_GATHER); rb = get_msg(t1, &im); fail_unless(rb != 0); fail_unless(im.type() == Message::T_INSTALL); rb = get_msg(t1, &gm); fail_unless(rb != 0); fail_unless(gm.type() == Message::T_GAP); fail_unless((gm.flags() & Message::F_COMMIT) != 0); rb = get_msg(t1, &msg); fail_unless(rb == 0); // Handle install message on node 2 // Expected output: commit gap message and state stays in S_RECOVERY p2->handle_msg(im); fail_unless(p2->state() == Proto::S_GATHER); rb = get_msg(t2, &gm2); fail_unless(rb != 0); fail_unless(gm2.type() == Message::T_GAP); fail_unless((gm2.flags() & Message::F_COMMIT) != 0); rb = get_msg(t2, &msg); fail_unless(rb == 0); // Handle gap messages // Expected output: Both nodes shift to S_INSTALL, // both send gap messages p1->handle_msg(gm2); fail_unless(p1->state() == Proto::S_INSTALL); Message gm12; rb = get_msg(t1, &gm12); fail_unless(rb != 0); fail_unless(gm12.type() == Message::T_GAP); fail_unless((gm12.flags() & Message::F_COMMIT) == 0); rb = get_msg(t1, &msg); fail_unless(rb == 0); p2->handle_msg(gm); fail_unless(p2->state() == Proto::S_INSTALL); Message gm22; rb = get_msg(t2, &gm22); fail_unless(rb != 0); fail_unless(gm22.type() == Message::T_GAP); fail_unless((gm22.flags() & Message::F_COMMIT) == 0); rb = get_msg(t2, &msg); fail_unless(rb == 0); // Handle final gap messages, expected output shift to operational // and gap message p1->handle_msg(gm22); fail_unless(p1->state() == Proto::S_OPERATIONAL); rb = get_msg(t1, &msg); fail_unless(rb != 0); fail_unless(msg.type() == Message::T_GAP); fail_unless((msg.flags() & Message::F_COMMIT) == 0); rb = get_msg(t1, &msg); fail_unless(rb == 0); p2->handle_msg(gm12); fail_unless(p2->state() == Proto::S_OPERATIONAL); rb = get_msg(t2, &msg); fail_unless(rb != 0); fail_unless(msg.type() == Message::T_GAP); fail_unless((msg.flags() & Message::F_COMMIT) == 0); rb = get_msg(t2, &msg); fail_unless(rb == 0); } START_TEST(test_proto_double_join) { log_info << "START"; gu::Config conf; UUID uuid1(1), uuid2(2); DummyTransport t1(uuid1), t2(uuid2); DummyUser u1(conf), u2(conf); Proto p1(conf, uuid1), p2(conf, uuid2); gcomm::connect(&t1, &p1); gcomm::connect(&p1, &u1); gcomm::connect(&t2, &p2); gcomm::connect(&p2, &u2); single_join(&t1, &p1); double_join(&t1, &p1, &t2, &p2); } END_TEST static gu::Config gu_conf; static DummyNode* create_dummy_node(size_t idx, const string& suspect_timeout = "PT1H", const string& inactive_timeout = "PT1H", const string& retrans_period = "PT20M") { // reset conf to avoid stale config in case of nofork gu_conf = gu::Config(); string conf = "evs://?" + Conf::EvsViewForgetTimeout + "=PT1H&" + Conf::EvsInactiveCheckPeriod + "=" + to_string(Period(suspect_timeout)/3) + "&" + Conf::EvsSuspectTimeout + "=" + suspect_timeout + "&" + Conf::EvsInactiveTimeout + "=" + inactive_timeout + "&" + Conf::EvsKeepalivePeriod + "=" + retrans_period + "&" + Conf::EvsJoinRetransPeriod + "=" + retrans_period + "&" + Conf::EvsInfoLogMask + "=0x7"; if (::getenv("EVS_DEBUG_MASK") != 0) { conf += "&" + Conf::EvsDebugLogMask + "=" + ::getenv("EVS_DEBUG_MASK"); } list protos; try { UUID uuid(static_cast(idx)); protos.push_back(new DummyTransport(uuid, false)); protos.push_back(new Proto(gu_conf, uuid, conf)); return new DummyNode(gu_conf, idx, protos); } catch (...) { for_each(protos.begin(), protos.end(), DeleteObject()); throw; } } namespace { gcomm::evs::Proto* evs_from_dummy(DummyNode* dn) { return reinterpret_cast(dn->protos().back()); } } static void join_node(PropagationMatrix* p, DummyNode* n, bool first = false) { gu_trace(p->insert_tp(n)); gu_trace(n->connect(first)); } static void send_n(DummyNode* node, const size_t n) { for (size_t i = 0; i < n; ++i) { gu_trace(node->send()); } } static void set_cvi(vector& nvec, size_t i_begin, size_t i_end, size_t seq) { for (size_t i = i_begin; i <= i_end; ++i) { nvec[i]->set_cvi(ViewId(V_REG, nvec[i_begin]->uuid(), static_cast(seq))); } } template class ViewSeq { public: ViewSeq() { } bool operator()(const C& a, const C& b) const { return (a->trace().current_view_trace().view().id().seq() < b->trace().current_view_trace().view().id().seq()); } }; static uint32_t get_max_view_seq(const std::vector& dnv, size_t i, size_t j) { if (i == dnv.size()) return static_cast(-1); return (*std::max_element(dnv.begin() + i, dnv.begin() + j, ViewSeq()))->trace().current_view_trace().view().id().seq(); } START_TEST(test_proto_join_n) { log_info << "START (join_n)"; init_rand(); const size_t n_nodes(4); PropagationMatrix prop; vector dn; for (size_t i = 1; i <= n_nodes; ++i) { gu_trace(dn.push_back(create_dummy_node(i))); } uint32_t max_view_seq(0); for (size_t i = 0; i < n_nodes; ++i) { gu_trace(join_node(&prop, dn[i], i == 0 ? true : false)); set_cvi(dn, 0, i, max_view_seq + 1); gu_trace(prop.propagate_until_cvi(false)); max_view_seq = get_max_view_seq(dn, 0, i); } gu_trace(check_trace(dn)); for_each(dn.begin(), dn.end(), DeleteObject()); } END_TEST START_TEST(test_proto_join_n_w_user_msg) { gu_conf_self_tstamp_on(); log_info << "START (join_n_w_user_msg)"; init_rand(); const size_t n_nodes(4); PropagationMatrix prop; vector dn; // @todo This test should terminate without these timeouts const string suspect_timeout("PT1H"); const string inactive_timeout("PT1H"); const string retrans_period("PT0.1S"); for (size_t i = 1; i <= n_nodes; ++i) { gu_trace(dn.push_back( create_dummy_node(i, suspect_timeout, inactive_timeout, retrans_period))); } uint32_t max_view_seq(0); for (size_t i = 0; i < n_nodes; ++i) { gu_trace(join_node(&prop, dn[i], i == 0 ? true : false)); set_cvi(dn, 0, i, max_view_seq + 1); gu_trace(prop.propagate_until_cvi(true)); for (size_t j = 0; j <= i; ++j) { gu_trace(send_n(dn[j], 5 + ::rand() % 4)); } gu_trace(prop.propagate_until_empty()); for (size_t j = 0; j <= i; ++j) { gu_trace(send_n(dn[j], 5 + ::rand() % 4)); } max_view_seq = get_max_view_seq(dn, 0, i); } gu_trace(check_trace(dn)); for_each(dn.begin(), dn.end(), DeleteObject()); } END_TEST START_TEST(test_proto_join_n_lossy) { gu_conf_self_tstamp_on(); log_info << "START (join_n_lossy)"; init_rand(); const size_t n_nodes(4); PropagationMatrix prop; vector dn; const string suspect_timeout("PT1H"); const string inactive_timeout("PT1H"); const string retrans_period("PT0.1S"); for (size_t i = 1; i <= n_nodes; ++i) { gu_trace(dn.push_back( create_dummy_node(i, suspect_timeout, inactive_timeout, retrans_period))); } uint32_t max_view_seq(0); for (size_t i = 0; i < n_nodes; ++i) { gu_trace(join_node(&prop, dn[i], i == 0 ? true : false)); set_cvi(dn, 0, i, max_view_seq + 1); for (size_t j = 1; j < i + 1; ++j) { prop.set_loss(i + 1, j, 0.9); prop.set_loss(j, i + 1, 0.9); } gu_trace(prop.propagate_until_cvi(true)); max_view_seq = get_max_view_seq(dn, 0, i); } gu_trace(check_trace(dn)); for_each(dn.begin(), dn.end(), DeleteObject()); } END_TEST START_TEST(test_proto_join_n_lossy_w_user_msg) { gu_conf_self_tstamp_on(); log_info << "START (join_n_lossy_w_user_msg)"; init_rand(); const size_t n_nodes(4); PropagationMatrix prop; vector dn; const string suspect_timeout("PT1H"); const string inactive_timeout("PT1H"); const string retrans_period("PT0.1S"); for (size_t i = 1; i <= n_nodes; ++i) { gu_trace(dn.push_back( create_dummy_node(i, suspect_timeout, inactive_timeout, retrans_period))); } uint32_t max_view_seq(0); for (size_t i = 0; i < n_nodes; ++i) { gu_trace(join_node(&prop, dn[i], i == 0 ? true : false)); set_cvi(dn, 0, i, max_view_seq + 1); for (size_t j = 1; j < i + 1; ++j) { prop.set_loss(i + 1, j, 0.9); prop.set_loss(j, i + 1, 0.9); } gu_trace(prop.propagate_until_cvi(true)); for (size_t j = 0; j < i; ++j) { gu_trace(send_n(dn[j], 5 + ::rand() % 4)); } max_view_seq = get_max_view_seq(dn, 0, i); } gu_trace(check_trace(dn)); for_each(dn.begin(), dn.end(), DeleteObject()); } END_TEST START_TEST(test_proto_leave_n) { gu_conf_self_tstamp_on(); log_info << "START (leave_n)"; init_rand(); const size_t n_nodes(4); PropagationMatrix prop; vector dn; for (size_t i = 1; i <= n_nodes; ++i) { gu_trace(dn.push_back(create_dummy_node(i))); } for (size_t i = 0; i < n_nodes; ++i) { gu_trace(join_node(&prop, dn[i], i == 0 ? true : false)); set_cvi(dn, 0, i, i + 1); gu_trace(prop.propagate_until_cvi(true)); } uint32_t max_view_seq(get_max_view_seq(dn, 0, n_nodes)); for (size_t i = 0; i < n_nodes; ++i) { dn[i]->close(); dn[i]->set_cvi(V_REG); set_cvi(dn, i + 1, n_nodes - 1, max_view_seq + 1); gu_trace(prop.propagate_until_cvi(true)); max_view_seq = get_max_view_seq(dn, i + 1, n_nodes); } gu_trace(check_trace(dn)); for_each(dn.begin(), dn.end(), DeleteObject()); } END_TEST START_TEST(test_proto_leave_n_w_user_msg) { gu_conf_self_tstamp_on(); log_info << "START (leave_n_w_user_msg)"; init_rand(); const size_t n_nodes(4); PropagationMatrix prop; vector dn; const string suspect_timeout("PT1H"); const string inactive_timeout("PT1H"); const string retrans_period("PT0.1S"); for (size_t i = 1; i <= n_nodes; ++i) { gu_trace(dn.push_back( create_dummy_node(i, suspect_timeout, inactive_timeout, retrans_period))); } for (size_t i = 0; i < n_nodes; ++i) { gu_trace(join_node(&prop, dn[i], i == 0 ? true : false)); set_cvi(dn, 0, i, i + 1); gu_trace(prop.propagate_until_cvi(false)); } uint32_t max_view_seq(get_max_view_seq(dn, 0, n_nodes)); for (size_t i = 0; i < n_nodes; ++i) { for (size_t j = i; j < n_nodes; ++j) { gu_trace(send_n(dn[j], 5 + ::rand() % 4)); } dn[i]->close(); dn[i]->set_cvi(V_REG); set_cvi(dn, i + 1, n_nodes - 1, max_view_seq + 1); gu_trace(prop.propagate_until_cvi(true)); max_view_seq = get_max_view_seq(dn, i + 1, n_nodes); } gu_trace(check_trace(dn)); for_each(dn.begin(), dn.end(), DeleteObject()); } END_TEST START_TEST(test_proto_leave_n_lossy) { gu_conf_self_tstamp_on(); log_info << "START (leave_n_lossy)"; init_rand(); const size_t n_nodes(4); PropagationMatrix prop; vector dn; const string suspect_timeout("PT0.5S"); const string inactive_timeout("PT1S"); const string retrans_period("PT0.1S"); for (size_t i = 1; i <= n_nodes; ++i) { gu_trace(dn.push_back( create_dummy_node(i, suspect_timeout, inactive_timeout, retrans_period))); } for (size_t i = 0; i < n_nodes; ++i) { gu_trace(join_node(&prop, dn[i], i == 0 ? true : false)); set_cvi(dn, 0, i, i + 1); gu_trace(prop.propagate_until_cvi(false)); } uint32_t max_view_seq(get_max_view_seq(dn, 0, n_nodes)); for (size_t i = 0; i < n_nodes; ++i) { for (size_t j = 1; j < i + 1; ++j) { prop.set_loss(i + 1, j, 0.9); prop.set_loss(j, i + 1, 0.9); } } for (size_t i = 0; i < n_nodes; ++i) { dn[i]->set_cvi(V_REG); set_cvi(dn, i + 1, n_nodes - 1, max_view_seq + 1); dn[i]->close(); gu_trace(prop.propagate_until_cvi(true)); max_view_seq = get_max_view_seq(dn, i + 1, n_nodes); } gu_trace(check_trace(dn)); for_each(dn.begin(), dn.end(), DeleteObject()); } END_TEST START_TEST(test_proto_leave_n_lossy_w_user_msg) { gu_conf_self_tstamp_on(); log_info << "START (leave_n_lossy_w_user_msg)"; init_rand(); const size_t n_nodes(4); PropagationMatrix prop; vector dn; const string suspect_timeout("PT0.5S"); const string inactive_timeout("PT1S"); const string retrans_period("PT0.1S"); for (size_t i = 1; i <= n_nodes; ++i) { gu_trace(dn.push_back( create_dummy_node(i, suspect_timeout, inactive_timeout, retrans_period))); } for (size_t i = 0; i < n_nodes; ++i) { gu_trace(join_node(&prop, dn[i], i == 0 ? true : false)); set_cvi(dn, 0, i, i + 1); gu_trace(prop.propagate_until_cvi(false)); } for (size_t i = 0; i < n_nodes; ++i) { for (size_t j = 1; j < i + 1; ++j) { prop.set_loss(i + 1, j, 0.9); prop.set_loss(j, i + 1, 0.9); } } uint32_t max_view_seq(get_max_view_seq(dn, 0, n_nodes)); for (size_t i = 0; i < n_nodes; ++i) { for (size_t j = i; j < n_nodes; ++j) { gu_trace(send_n(dn[j], 5 + ::rand() % 4)); } dn[i]->set_cvi(V_REG); set_cvi(dn, i + 1, n_nodes - 1, max_view_seq + 1); dn[i]->close(); gu_trace(prop.propagate_until_cvi(true)); max_view_seq = get_max_view_seq(dn, i + 1, n_nodes); } gu_trace(check_trace(dn)); for_each(dn.begin(), dn.end(), DeleteObject()); } END_TEST // Generic test code for split/merge cases static void test_proto_split_merge_gen(const size_t n_nodes, const bool send_msgs, const double loss) { PropagationMatrix prop; vector dn; const string suspect_timeout("PT1.2S"); const string inactive_timeout("PT1.2S"); const string retrans_period("PT0.1S"); for (size_t i = 1; i <= n_nodes; ++i) { gu_trace(dn.push_back( create_dummy_node(i, suspect_timeout, inactive_timeout, retrans_period))); } for (size_t i = 0; i < n_nodes; ++i) { gu_trace(join_node(&prop, dn[i], i == 0 ? true : false)); set_cvi(dn, 0, i, i + 1); gu_trace(prop.propagate_until_cvi(false)); } for (size_t i = 0; i < n_nodes; ++i) { for (size_t j = 1; j < i + 1; ++j) { prop.set_loss(i + 1, j, loss); prop.set_loss(j, i + 1, loss); } } vector split; for (size_t i = 0; i < n_nodes; ++i) { split.push_back(static_cast(i + 1)); } uint32_t max_view_seq(get_max_view_seq(dn, 0, n_nodes)); for (size_t i = 1; i < n_nodes; ++i) { if (send_msgs == true) { for (size_t k = 0; k < 5; ++k) { for (size_t j = 0; j < n_nodes; ++j) { gu_trace(send_n(dn[j], 1 + j)); } gu_trace(prop.propagate_n(7)); } } log_info << "split " << i; for (size_t j = 0; j < i; ++j) { for (size_t k = i; k < n_nodes; ++k) { gu_trace(prop.set_loss(split[j], split[k], 0.)); gu_trace(prop.set_loss(split[k], split[j], 0.)); } } set_cvi(dn, 0, i - 1, max_view_seq + 1); set_cvi(dn, i, n_nodes - 1, max_view_seq + 1); if (send_msgs == true) { for (size_t j = 0; j < n_nodes; ++j) { gu_trace(send_n(dn[j], 5 + rand() % 4)); } } gu_trace(prop.propagate_until_cvi(true)); max_view_seq = get_max_view_seq(dn, 0, n_nodes); log_info << "merge " << i; for (size_t j = 0; j < i; ++j) { for (size_t k = i; k < n_nodes; ++k) { gu_trace(prop.set_loss(split[j], split[k], loss)); gu_trace(prop.set_loss(split[k], split[j], loss)); } } set_cvi(dn, 0, n_nodes - 1, max_view_seq + 1); if (send_msgs == true) { for (size_t j = 0; j < n_nodes; ++j) { gu_trace(send_n(dn[j], 5 + rand() % 4)); } } gu_trace(prop.propagate_until_cvi(true)); max_view_seq = get_max_view_seq(dn, 0, n_nodes); } gu_trace(prop.propagate_until_empty()); gu_trace(check_trace(dn)); for_each(dn.begin(), dn.end(), DeleteObject()); } START_TEST(test_proto_split_merge) { gu_conf_self_tstamp_on(); log_info << "START (split_merge)"; init_rand(); test_proto_split_merge_gen(4, false, 1.); } END_TEST START_TEST(test_proto_split_merge_lossy) { gu_conf_self_tstamp_on(); log_info << "START (split_merge_lossy)"; init_rand(); test_proto_split_merge_gen(4, false, .9); } END_TEST START_TEST(test_proto_split_merge_w_user_msg) { gu_conf_self_tstamp_on(); log_info << "START (split_merge_w_user_msg)"; init_rand(); test_proto_split_merge_gen(4, true, 1.); } END_TEST START_TEST(test_proto_split_merge_lossy_w_user_msg) { gu_conf_self_tstamp_on(); log_info << "START (split_merge_lossy_w_user_msg)"; init_rand(); test_proto_split_merge_gen(4, true, .9); } END_TEST START_TEST(test_proto_stop_cont) { log_info << "START"; init_rand(); const size_t n_nodes(4); PropagationMatrix prop; vector dn; const string suspect_timeout("PT0.31S"); const string inactive_timeout("PT0.31S"); const string retrans_period("PT0.1S"); for (size_t i = 1; i <= n_nodes; ++i) { gu_trace(dn.push_back( create_dummy_node(i, suspect_timeout, inactive_timeout, retrans_period))); } for (size_t i = 0; i < n_nodes; ++i) { gu_trace(join_node(&prop, dn[i], i == 0 ? true : false)); set_cvi(dn, 0, i, i + 1); gu_trace(prop.propagate_until_cvi(false)); } uint32_t view_seq = n_nodes + 1; for (size_t i = 0; i < n_nodes; ++i) { for (size_t j = 0; j < n_nodes; ++j) { if (j != i) { dn[j]->close(dn[i]->uuid()); } } set_cvi(dn, 0, n_nodes - 1, view_seq + 1); gu_trace(prop.propagate_until_cvi(true)); view_seq += 2; } gu_trace(check_trace(dn)); for_each(dn.begin(), dn.end(), DeleteObject()); } END_TEST START_TEST(test_proto_arbitrate) { log_info << "START"; const size_t n_nodes(3); PropagationMatrix prop; vector dn; const string suspect_timeout("PT0.5S"); const string inactive_timeout("PT0.5S"); const string retrans_period("PT0.1S"); for (size_t i = 1; i <= n_nodes; ++i) { gu_trace(dn.push_back( create_dummy_node(i, suspect_timeout, inactive_timeout, retrans_period))); } for (size_t i = 0; i < n_nodes; ++i) { gu_trace(join_node(&prop, dn[i], i == 0 ? true : false)); set_cvi(dn, 0, i, i + 1); gu_trace(prop.propagate_until_cvi(false)); } uint32_t view_seq = n_nodes + 1; dn[0]->close(dn[1]->uuid()); dn[1]->close(dn[0]->uuid()); dn[0]->set_cvi(ViewId(V_REG, dn[0]->uuid(), view_seq)); dn[2]->set_cvi(ViewId(V_REG, dn[0]->uuid(), view_seq)); dn[1]->set_cvi(ViewId(V_REG, dn[1]->uuid(), view_seq)); gu_trace(prop.propagate_until_cvi(true)); dn[0]->set_cvi(ViewId(V_REG, dn[0]->uuid(), view_seq + 1)); dn[1]->set_cvi(ViewId(V_REG, dn[0]->uuid(), view_seq + 1)); dn[2]->set_cvi(ViewId(V_REG, dn[0]->uuid(), view_seq + 1)); gu_trace(prop.propagate_until_cvi(true)); gu_trace(check_trace(dn)); for_each(dn.begin(), dn.end(), DeleteObject()); } END_TEST START_TEST(test_proto_split_two) { log_info << "START"; const size_t n_nodes(2); PropagationMatrix prop; vector dn; const string suspect_timeout("PT0.31S"); const string inactive_timeout("PT0.31S"); const string retrans_period("PT0.1S"); for (size_t i = 1; i <= n_nodes; ++i) { gu_trace(dn.push_back( create_dummy_node(i, suspect_timeout, inactive_timeout, retrans_period))); } for (size_t i = 0; i < n_nodes; ++i) { gu_trace(join_node(&prop, dn[i], i == 0 ? true : false)); set_cvi(dn, 0, i, i + 1); gu_trace(prop.propagate_until_cvi(false)); } uint32_t view_seq = n_nodes + 1; dn[0]->close(dn[1]->uuid()); dn[1]->close(dn[0]->uuid()); dn[0]->set_cvi(ViewId(V_REG, dn[0]->uuid(), view_seq)); dn[1]->set_cvi(ViewId(V_REG, dn[1]->uuid(), view_seq)); gu_trace(prop.propagate_until_cvi(true)); dn[0]->set_cvi(ViewId(V_REG, dn[0]->uuid(), view_seq + 1)); dn[1]->set_cvi(ViewId(V_REG, dn[0]->uuid(), view_seq + 1)); gu_trace(prop.propagate_until_cvi(true)); gu_trace(check_trace(dn)); for_each(dn.begin(), dn.end(), DeleteObject()); } END_TEST START_TEST(test_aggreg) { log_info << "START"; const size_t n_nodes(2); PropagationMatrix prop; vector dn; const string suspect_timeout("PT0.31S"); const string inactive_timeout("PT0.31S"); const string retrans_period("PT0.1S"); for (size_t i = 1; i <= n_nodes; ++i) { gu_trace(dn.push_back( create_dummy_node(i, suspect_timeout, inactive_timeout, retrans_period))); } for (size_t i = 0; i < n_nodes; ++i) { gu_trace(join_node(&prop, dn[i], i == 0 ? true : false)); set_cvi(dn, 0, i, i + 1); gu_trace(prop.propagate_until_cvi(false)); } for (size_t i = 0; i < n_nodes; ++i) { gu_trace(send_n(dn[i], 8)); } gu_trace(prop.propagate_until_empty()); gu_trace(check_trace(dn)); for_each(dn.begin(), dn.end(), DeleteObject()); } END_TEST START_TEST(test_trac_538) { gu_conf_self_tstamp_on(); log_info << "START (test_trac_538)"; init_rand(); const size_t n_nodes(5); PropagationMatrix prop; vector dn; const string suspect_timeout("PT0.5S"); const string inactive_timeout("PT1S"); const string retrans_period("PT0.1S"); for (size_t i = 1; i <= n_nodes; ++i) { gu_trace(dn.push_back( create_dummy_node(i, suspect_timeout, inactive_timeout, retrans_period))); } for (size_t i = 0; i < n_nodes - 1; ++i) { gu_trace(join_node(&prop, dn[i], i == 0 ? true : false)); set_cvi(dn, 0, i, i + 1); gu_trace(prop.propagate_until_cvi(false)); } uint32_t max_view_seq(get_max_view_seq(dn, 0, n_nodes - 1)); gu_trace(join_node(&prop, dn[n_nodes - 1], false)); for (size_t i = 1; i <= n_nodes; ++i) { if (i != n_nodes - 1) { prop.set_loss(i, n_nodes - 1, 0); prop.set_loss(n_nodes - 1, i, 0); } } set_cvi(dn, 0, n_nodes - 1, max_view_seq + 1); dn[n_nodes - 2]->set_cvi(ViewId(V_REG, n_nodes - 1, max_view_seq + 1)); gu_trace(prop.propagate_until_cvi(true)); gu_trace(check_trace(dn)); for_each(dn.begin(), dn.end(), DeleteObject()); } END_TEST START_TEST(test_trac_552) { gu_conf_self_tstamp_on(); log_info << "START (trac_552)"; init_rand(); const size_t n_nodes(3); PropagationMatrix prop; vector dn; const string suspect_timeout("PT0.5S"); const string inactive_timeout("PT1S"); const string retrans_period("PT0.1S"); for (size_t i = 1; i <= n_nodes; ++i) { gu_trace(dn.push_back( create_dummy_node(i, suspect_timeout, inactive_timeout, retrans_period))); } for (size_t i = 0; i < n_nodes; ++i) { gu_trace(join_node(&prop, dn[i], i == 0 ? true : false)); set_cvi(dn, 0, i, i + 1); gu_trace(prop.propagate_until_cvi(false)); } for (size_t i = 0; i < n_nodes; ++i) { for (size_t j = 1; j < i + 1; ++j) { prop.set_loss(i + 1, j, 0.9); prop.set_loss(j, i + 1, 0.9); } } uint32_t max_view_seq(get_max_view_seq(dn, 0, n_nodes)); for (size_t j = 0; j < n_nodes; ++j) { gu_trace(send_n(dn[j], 5 + ::rand() % 4)); } dn[0]->set_cvi(V_REG); dn[1]->set_cvi(V_REG); set_cvi(dn, 2, n_nodes - 1, max_view_seq + 1); dn[0]->close(); dn[1]->close(); gu_trace(prop.propagate_until_cvi(true)); gu_trace(check_trace(dn)); for_each(dn.begin(), dn.end(), DeleteObject()); } END_TEST START_TEST(test_trac_607) { gu_conf_self_tstamp_on(); log_info << "START (trac_607)"; const size_t n_nodes(3); PropagationMatrix prop; vector dn; const string suspect_timeout("PT0.5S"); const string inactive_timeout("PT1S"); const string retrans_period("PT0.1S"); for (size_t i = 1; i <= n_nodes; ++i) { gu_trace(dn.push_back( create_dummy_node(i, suspect_timeout, inactive_timeout, retrans_period))); } for (size_t i = 0; i < n_nodes; ++i) { gu_trace(join_node(&prop, dn[i], i == 0 ? true : false)); set_cvi(dn, 0, i, i + 1); gu_trace(prop.propagate_until_cvi(false)); } uint32_t max_view_seq(get_max_view_seq(dn, 0, n_nodes)); dn[0]->set_cvi(V_REG); dn[0]->close(); while (evs_from_dummy(dn[1])->state() != Proto::S_INSTALL) { prop.propagate_n(1); } // this used to cause exception: // Forbidden state transition: INSTALL -> LEAVING (FATAL) dn[1]->close(); // expected behavior: // dn[1], dn[2] reach S_OPERATIONAL and then dn[1] leaves gracefully set_cvi(dn, 1, n_nodes - 1, max_view_seq + 1); gu_trace(prop.propagate_until_cvi(true)); max_view_seq = get_max_view_seq(dn, 0, n_nodes); dn[1]->set_cvi(V_REG); set_cvi(dn, 2, 2, max_view_seq + 1); gu_trace(prop.propagate_until_cvi(true)); gu_trace(check_trace(dn)); for_each(dn.begin(), dn.end(), DeleteObject()); } END_TEST START_TEST(test_trac_724) { gu_conf_self_tstamp_on(); log_info << "START (trac_724)"; init_rand(); const size_t n_nodes(2); PropagationMatrix prop; vector dn; const string suspect_timeout("PT0.5S"); const string inactive_timeout("PT1S"); const string retrans_period("PT0.1S"); for (size_t i = 1; i <= n_nodes; ++i) { gu_trace(dn.push_back( create_dummy_node(i, suspect_timeout, inactive_timeout, retrans_period))); } for (size_t i = 0; i < n_nodes; ++i) { gu_trace(join_node(&prop, dn[i], i == 0 ? true : false)); set_cvi(dn, 0, i, i + 1); gu_trace(prop.propagate_until_cvi(false)); } // Slightly asymmetric settings and evs.use_aggregate=false to // allow completion window to grow over 0xff. Proto* evs0(evs_from_dummy(dn[0])); bool ret(evs0->set_param("evs.use_aggregate", "false")); fail_unless(ret == true); ret = evs0->set_param("evs.send_window", "1024"); fail_unless(ret == true); ret = evs0->set_param("evs.user_send_window", "515"); Proto* evs1(evs_from_dummy(dn[1])); ret = evs1->set_param("evs.use_aggregate", "false"); fail_unless(ret == true); ret = evs1->set_param("evs.send_window", "1024"); fail_unless(ret == true); ret = evs1->set_param("evs.user_send_window", "512"); prop.set_loss(1, 2, 0.); for (size_t i(0); i < 256; ++i) { dn[0]->send(); dn[0]->send(); dn[1]->send(); gu_trace(prop.propagate_until_empty()); } dn[0]->send(); prop.set_loss(1, 2, 1.); dn[0]->send(); gu_trace(prop.propagate_until_empty()); gu_trace(check_trace(dn)); for_each(dn.begin(), dn.end(), DeleteObject()); } END_TEST START_TEST(test_trac_760) { gu_conf_self_tstamp_on(); log_info << "START (trac_760)"; init_rand(); const size_t n_nodes(3); PropagationMatrix prop; vector dn; const string suspect_timeout("PT0.5S"); const string inactive_timeout("PT1S"); const string retrans_period("PT0.1S"); for (size_t i = 1; i <= n_nodes; ++i) { gu_trace(dn.push_back( create_dummy_node(i, suspect_timeout, inactive_timeout, retrans_period))); } for (size_t i = 0; i < n_nodes; ++i) { gu_trace(join_node(&prop, dn[i], i == 0 ? true : false)); set_cvi(dn, 0, i, i + 1); gu_trace(prop.propagate_until_cvi(false)); } for (size_t i = 0; i < n_nodes; ++i) { gu_trace(send_n(dn[i], 2)); } gu_trace(prop.propagate_until_empty()); uint32_t max_view_seq(get_max_view_seq(dn, 0, n_nodes)); gu_trace(send_n(dn[0], 1)); gu_trace(send_n(dn[1], 1)); // gu_trace(send_n(dn[2], 1)); set_cvi(dn, 0, 1, max_view_seq + 1); dn[2]->set_cvi(V_REG); dn[2]->close(); Proto* evs0(evs_from_dummy(dn[0])); Proto* evs1(evs_from_dummy(dn[1])); while (evs1->state() != Proto::S_GATHER && evs0->state() != Proto::S_GATHER) { gu_trace(prop.propagate_n(1)); } dn[1]->close(); gu_trace(prop.propagate_until_cvi(true)); gu_trace(check_trace(dn)); for_each(dn.begin(), dn.end(), DeleteObject()); } END_TEST Suite* evs2_suite() { Suite* s = suite_create("gcomm::evs"); TCase* tc; bool skip(false); if (skip == false) { tc = tcase_create("test_range"); tcase_add_test(tc, test_range); suite_add_tcase(s, tc); tc = tcase_create("test_message"); tcase_add_test(tc, test_message); suite_add_tcase(s, tc); tc = tcase_create("test_input_map_insert"); tcase_add_test(tc, test_input_map_insert); suite_add_tcase(s, tc); tc = tcase_create("test_input_map_find"); tcase_add_test(tc, test_input_map_find); suite_add_tcase(s, tc); tc = tcase_create("test_input_map_safety"); tcase_add_test(tc, test_input_map_safety); suite_add_tcase(s, tc); tc = tcase_create("test_input_map_erase"); tcase_add_test(tc, test_input_map_erase); suite_add_tcase(s, tc); tc = tcase_create("test_input_map_overwrap"); tcase_add_test(tc, test_input_map_overwrap); tcase_set_timeout(tc, 15); suite_add_tcase(s, tc); tc = tcase_create("test_input_map_random_insert"); tcase_add_test(tc, test_input_map_random_insert); suite_add_tcase(s, tc); tc = tcase_create("test_proto_single_join"); tcase_add_test(tc, test_proto_single_join); suite_add_tcase(s, tc); tc = tcase_create("test_proto_double_join"); tcase_add_test(tc, test_proto_double_join); suite_add_tcase(s, tc); tc = tcase_create("test_proto_join_n"); tcase_add_test(tc, test_proto_join_n); suite_add_tcase(s, tc); tc = tcase_create("test_proto_join_n_w_user_msg"); tcase_add_test(tc, test_proto_join_n_w_user_msg); suite_add_tcase(s, tc); tc = tcase_create("test_proto_join_n_lossy"); tcase_add_test(tc, test_proto_join_n_lossy); suite_add_tcase(s, tc); tc = tcase_create("test_proto_join_n_lossy_w_user_msg"); tcase_add_test(tc, test_proto_join_n_lossy_w_user_msg); suite_add_tcase(s, tc); tc = tcase_create("test_proto_leave_n"); tcase_add_test(tc, test_proto_leave_n); tcase_set_timeout(tc, 20); suite_add_tcase(s, tc); tc = tcase_create("test_proto_leave_n_w_user_msg"); tcase_add_test(tc, test_proto_leave_n_w_user_msg); tcase_set_timeout(tc, 20); suite_add_tcase(s, tc); tc = tcase_create("test_proto_leave_n_lossy"); tcase_add_test(tc, test_proto_leave_n_lossy); tcase_set_timeout(tc, 25); suite_add_tcase(s, tc); tc = tcase_create("test_proto_leave_n_lossy_w_user_msg"); tcase_add_test(tc, test_proto_leave_n_lossy_w_user_msg); tcase_set_timeout(tc, 25); suite_add_tcase(s, tc); tc = tcase_create("test_proto_split_merge"); tcase_add_test(tc, test_proto_split_merge); tcase_set_timeout(tc, 20); suite_add_tcase(s, tc); tc = tcase_create("test_proto_split_merge_lossy"); tcase_add_test(tc, test_proto_split_merge_lossy); tcase_set_timeout(tc, 20); suite_add_tcase(s, tc); tc = tcase_create("test_proto_split_merge_w_user_msg"); tcase_add_test(tc, test_proto_split_merge_w_user_msg); tcase_set_timeout(tc, 60); suite_add_tcase(s, tc); tc = tcase_create("test_proto_split_merge_lossy_w_user_msg"); tcase_add_test(tc, test_proto_split_merge_lossy_w_user_msg); tcase_set_timeout(tc, 60); suite_add_tcase(s, tc); tc = tcase_create("test_proto_stop_cont"); tcase_add_test(tc, test_proto_stop_cont); tcase_set_timeout(tc, 10); suite_add_tcase(s, tc); tc = tcase_create("test_proto_split_two"); tcase_add_test(tc, test_proto_split_two); suite_add_tcase(s, tc); tc = tcase_create("test_aggreg"); tcase_add_test(tc, test_aggreg); suite_add_tcase(s, tc); tc = tcase_create("test_proto_arbitrate"); tcase_add_test(tc, test_proto_arbitrate); suite_add_tcase(s, tc); tc = tcase_create("test_trac_538"); tcase_add_test(tc, test_trac_538); tcase_set_timeout(tc, 15); suite_add_tcase(s, tc); tc = tcase_create("test_trac_552"); tcase_add_test(tc, test_trac_552); tcase_set_timeout(tc, 15); suite_add_tcase(s, tc); tc = tcase_create("test_trac_607"); tcase_add_test(tc, test_trac_607); tcase_set_timeout(tc, 15); suite_add_tcase(s, tc); tc = tcase_create("test_trac_724"); tcase_add_test(tc, test_trac_724); tcase_set_timeout(tc, 15); suite_add_tcase(s, tc); } tc = tcase_create("test_trac_760"); tcase_add_test(tc, test_trac_760); tcase_set_timeout(tc, 15); suite_add_tcase(s, tc); return s; } percona-xtradb-cluster-galera/gcomm/test/check_gcomm.cpp0000644000000000000000000000414612247075736023703 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #include "check_gcomm.hpp" #include "gu_string.hpp" #include "gu_exception.hpp" #include "gu_logger.hpp" #include #include #include #include #include #include #include // * suits = 0; if (argc > 1 && !strcmp(argv[1],"nofork")) { srunner_set_fork_status(sr, CK_NOFORK); } else if (argc > 1 && strcmp(argv[1], "nolog") == 0) { /* no log redirection */} else { // running in the background, loggin' to file FILE* log_file = fopen ("check_gcomm.log", "w"); if (!log_file) return EXIT_FAILURE; gu_conf_set_log_file (log_file); // redirect occasional stderr there as well if (dup2(fileno(log_file), 2) < 0) { perror("dup2() failed: "); return EXIT_FAILURE; } } if (::getenv("CHECK_GCOMM_DEBUG")) { gu_log_max_level = GU_LOG_DEBUG; //gu::Logger::enable_debug(true); } log_info << "check_gcomm, start tests"; if (::getenv("CHECK_GCOMM_SUITES")) { suits = new vector(gu::strsplit(::getenv("CHECK_GCOMM_SUITES"), ',')); } for (size_t i = 0; suites[i].suite != 0; ++i) { if (suits == 0 || find(suits->begin(), suits->end(), suites[i].name) != suits->end()) { srunner_add_suite(sr, suites[i].suite()); } } delete suits; suits = 0; srunner_run_all(sr, CK_NORMAL); log_info << "check_gcomm, run all tests"; int n_fail = srunner_ntests_failed(sr); srunner_free(sr); return n_fail == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } percona-xtradb-cluster-galera/gcomm/test/check_gcomm.hpp0000644000000000000000000000145412247075736023707 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #ifndef CHECK_GCOMM_HPP #define CHECK_GCOMM_HPP struct Suite; /* Tests for various common types */ Suite* types_suite(); /* Tests for utilities */ Suite* util_suite(); /* Tests for logger */ Suite* logger_suite(); /* Tests for message buffer implementations */ Suite* buffer_suite(); /* Tests for event loop */ Suite* event_suite(); /* Tests for concurrency handling (mutex, cond, thread, etc.)*/ Suite* concurrent_suite(); /* Tests for TCP transport */ Suite* tcp_suite(); /* Tests for GMcast transport */ Suite* gmcast_suite(); /* Tests for EVS transport */ Suite* evs_suite(); /* Better evs suite */ Suite* evs2_suite(); /* Tests for VS trasport */ Suite* vs_suite(); /* Tests for PC transport */ Suite* pc_suite(); #endif // CHECK_GCOMM_HPP percona-xtradb-cluster-galera/gcomm/test/check_gmcast.cpp0000644000000000000000000002401712247075736024056 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #include "check_gcomm.hpp" #include "gcomm/protostack.hpp" #include "gmcast.hpp" #include "gmcast_message.hpp" using namespace std; using namespace gcomm; using namespace gcomm::gmcast; using namespace gu; using namespace gu::datetime; #include // Note: Not all tests are run by default as they require default port to be // used or listen port to be known beforehand. static bool run_all_tests(false); // Note: Multicast test(s) not run by default. static bool test_multicast(false); string mcast_param("gmcast.mcast_addr=239.192.0.11&gmcast.mcast_port=4567"); START_TEST(test_gmcast_multicast) { string uri1("gmcast://?gmcast.group=test&gmcast.mcast_addr=239.192.0.11"); gu::Config conf; auto_ptr pnet(Protonet::create(conf)); Transport* gm1(Transport::create(*pnet, uri1)); gm1->connect(); gm1->close(); delete gm1; } END_TEST START_TEST(test_gmcast_w_user_messages) { class User : public Toplay { Transport* tp_; size_t recvd_; Protostack pstack_; explicit User(const User&); void operator=(User&); public: User(Protonet& pnet, const std::string& listen_addr, const std::string& remote_addr) : Toplay(pnet.conf()), tp_(0), recvd_(0), pstack_() { string uri("gmcast://"); uri += remote_addr; // != 0 ? remote_addr : ""; uri += "?"; uri += "tcp.non_blocking=1"; uri += "&"; uri += "gmcast.group=testgrp"; uri += "&gmcast.time_wait=PT0.5S"; if (test_multicast == true) { uri += "&" + mcast_param; } uri += "&gmcast.listen_addr=tcp://"; uri += listen_addr; tp_ = Transport::create(pnet, uri); } ~User() { delete tp_; } void start(const std::string& peer = "") { if (peer == "") { tp_->connect(); } else { tp_->connect(peer); } pstack_.push_proto(tp_); pstack_.push_proto(this); } void stop() { pstack_.pop_proto(this); pstack_.pop_proto(tp_); tp_->close(); } void handle_timer() { byte_t buf[16]; memset(buf, 0xa5, sizeof(buf)); Datagram dg(Buffer(buf, buf + sizeof(buf))); send_down(dg, ProtoDownMeta()); } void handle_up(const void* cid, const Datagram& rb, const ProtoUpMeta& um) { if (rb.len() < rb.offset() + 16) { gu_throw_fatal << "offset error"; } char buf[16]; memset(buf, 0xa5, sizeof(buf)); // cppcheck-suppress uninitstring if (memcmp(buf, &rb.payload()[0] + rb.offset(), 16) != 0) { gu_throw_fatal << "content mismatch"; } recvd_++; } size_t recvd() const { return recvd_; } void set_recvd(size_t val) { recvd_ = val; } Protostack& pstack() { return pstack_; } std::string listen_addr() const { return tp_->listen_addr(); } }; log_info << "START"; gu::Config conf; auto_ptr pnet(Protonet::create(conf)); User u1(*pnet, "127.0.0.1:0", ""); pnet->insert(&u1.pstack()); log_info << "u1 start"; u1.start(); pnet->event_loop(Sec/10); fail_unless(u1.recvd() == 0); log_info << "u2 start"; User u2(*pnet, "127.0.0.1:0", u1.listen_addr().erase(0, strlen("tcp://"))); pnet->insert(&u2.pstack()); u2.start(); while (u1.recvd() <= 50 || u2.recvd() <= 50) { u1.handle_timer(); u2.handle_timer(); pnet->event_loop(Sec/10); } log_info << "u3 start"; User u3(*pnet, "127.0.0.1:0", u2.listen_addr().erase(0, strlen("tcp://"))); pnet->insert(&u3.pstack()); u3.start(); while (u3.recvd() <= 50) { u1.handle_timer(); u2.handle_timer(); pnet->event_loop(Sec/10); } log_info << "u4 start"; User u4(*pnet, "127.0.0.1:0", u2.listen_addr().erase(0, strlen("tcp://"))); pnet->insert(&u4.pstack()); u4.start(); while (u4.recvd() <= 50) { u1.handle_timer(); u2.handle_timer(); pnet->event_loop(Sec/10); } log_info << "u1 stop"; u1.stop(); pnet->erase(&u1.pstack()); pnet->event_loop(3*Sec); log_info << "u1 start"; pnet->insert(&u1.pstack()); u1.start(u2.listen_addr()); u1.set_recvd(0); u2.set_recvd(0); u3.set_recvd(0); u4.set_recvd(0); for (size_t i(0); i < 30; ++i) { u1.handle_timer(); u2.handle_timer(); pnet->event_loop(Sec/10); } fail_unless(u1.recvd() != 0); fail_unless(u2.recvd() != 0); fail_unless(u3.recvd() != 0); fail_unless(u4.recvd() != 0); pnet->erase(&u4.pstack()); pnet->erase(&u3.pstack()); pnet->erase(&u2.pstack()); pnet->erase(&u1.pstack()); u1.stop(); u2.stop(); u3.stop(); u4.stop(); pnet->event_loop(0); } END_TEST // not run by default, hard coded port START_TEST(test_gmcast_auto_addr) { log_info << "START"; gu::Config conf; auto_ptr pnet(Protonet::create(conf)); Transport* tp1 = Transport::create(*pnet, "gmcast://?gmcast.group=test"); Transport* tp2 = Transport::create(*pnet, "gmcast://127.0.0.1:4567?gmcast.group=test&gmcast.listen_addr=tcp://127.0.0.1:10002"); pnet->insert(&tp1->pstack()); pnet->insert(&tp2->pstack()); tp1->connect(); tp2->connect(); pnet->event_loop(Sec); pnet->erase(&tp2->pstack()); pnet->erase(&tp1->pstack()); tp1->close(); tp2->close(); delete tp1; delete tp2; pnet->event_loop(0); } END_TEST START_TEST(test_gmcast_forget) { gu_conf_self_tstamp_on(); log_info << "START"; gu::Config conf; auto_ptr pnet(Protonet::create(conf)); Transport* tp1 = Transport::create(*pnet, "gmcast://?gmcast.group=test&gmcast.listen_addr=tcp://127.0.0.1:0"); pnet->insert(&tp1->pstack()); tp1->connect(); Transport* tp2 = Transport::create(*pnet, std::string("gmcast://") + tp1->listen_addr().erase( 0, strlen("tcp://")) + "?gmcast.group=test&gmcast.listen_addr=tcp://127.0.0.1:0"); Transport* tp3 = Transport::create(*pnet, std::string("gmcast://") + tp1->listen_addr().erase( 0, strlen("tcp://")) + "?gmcast.group=test&gmcast.listen_addr=tcp://127.0.0.1:0"); pnet->insert(&tp2->pstack()); pnet->insert(&tp3->pstack()); tp2->connect(); tp3->connect(); pnet->event_loop(Sec); UUID uuid1 = tp1->uuid(); tp1->close(); tp2->close(uuid1); tp3->close(uuid1); pnet->event_loop(10*Sec); tp1->connect(); // @todo Implement this using User class above and verify that // tp2 and tp3 communicate with each other but now with tp1 log_info << "####"; pnet->event_loop(Sec); pnet->erase(&tp3->pstack()); pnet->erase(&tp2->pstack()); pnet->erase(&tp1->pstack()); tp1->close(); tp2->close(); tp3->close(); delete tp1; delete tp2; delete tp3; pnet->event_loop(0); } END_TEST // not run by default, hard coded port START_TEST(test_trac_380) { gu_conf_self_tstamp_on(); log_info << "START"; gu::Config conf; std::auto_ptr pnet(gcomm::Protonet::create(conf)); // caused either assertion or exception gcomm::Transport* tp1(gcomm::Transport::create( *pnet, "gmcast://127.0.0.1:4567?" "gmcast.group=test")); pnet->insert(&tp1->pstack()); tp1->connect(); try { pnet->event_loop(Sec); } catch (gu::Exception& e) { fail_unless(e.get_errno() == EINVAL, "unexpected errno: %d, cause %s", e.get_errno(), e.what()); } pnet->erase(&tp1->pstack()); tp1->close(); delete tp1; pnet->event_loop(0); try { tp1 = gcomm::Transport::create( *pnet, "gmcast://127.0.0.1:4567?" "gmcast.group=test&" "gmcast.listen_addr=tcp://127.0.0.1:4567"); } catch (gu::Exception& e) { fail_unless(e.get_errno() == EINVAL, "unexpected errno: %d, cause %s", e.get_errno(), e.what()); } pnet->event_loop(0); } END_TEST Suite* gmcast_suite() { Suite* s = suite_create("gmcast"); TCase* tc; if (test_multicast == true) { tc = tcase_create("test_gmcast_multicast"); tcase_add_test(tc, test_gmcast_multicast); suite_add_tcase(s, tc); } tc = tcase_create("test_gmcast_w_user_messages"); tcase_add_test(tc, test_gmcast_w_user_messages); tcase_set_timeout(tc, 20); suite_add_tcase(s, tc); if (run_all_tests == true) { // not run by default, hard coded port tc = tcase_create("test_gmcast_auto_addr"); tcase_add_test(tc, test_gmcast_auto_addr); suite_add_tcase(s, tc); } tc = tcase_create("test_gmcast_forget"); tcase_add_test(tc, test_gmcast_forget); tcase_set_timeout(tc, 20); suite_add_tcase(s, tc); if (run_all_tests == true) { // not run by default, hard coded port tc = tcase_create("test_trac_380"); tcase_add_test(tc, test_trac_380); suite_add_tcase(s, tc); } return s; } percona-xtradb-cluster-galera/gcomm/test/check_pc.cpp0000644000000000000000000025672112247075736023213 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #include "check_gcomm.hpp" #include "pc_message.hpp" #include "pc_proto.hpp" #include "evs_proto.hpp" #include "check_templ.hpp" #include "check_trace.hpp" #include "gcomm/conf.hpp" #include "gu_errno.h" #include #include #include #include using namespace std; using namespace std::rel_ops; using namespace gu; using namespace gu::datetime; using namespace gcomm; using namespace gcomm::pc; START_TEST(test_pc_messages) { StateMessage pcs(0); pc::NodeMap& sim(pcs.node_map()); sim.insert(std::make_pair(UUID(0,0), pc::Node(true, false, 6, ViewId(V_PRIM, UUID(0, 0), 9), 42, -1))); sim.insert(std::make_pair(UUID(0,0), pc::Node(false, true, 88, ViewId(V_PRIM, UUID(0, 0), 3), 472, 0))); sim.insert(std::make_pair(UUID(0,0), pc::Node(true, false, 78, ViewId(V_PRIM, UUID(0, 0), 87), 52, 1))); size_t expt_size = 4 // hdr + 4 // seq + 4 + 3*(UUID::serial_size() + sizeof(uint32_t) + 4 + 20 + 8); // NodeMap check_serialization(pcs, expt_size, StateMessage(-1)); InstallMessage pci(0); pc::NodeMap& iim = pci.node_map(); iim.insert(std::make_pair(UUID(0,0), pc::Node(true, true, 6, ViewId(V_PRIM, UUID(0, 0), 9), 42, -1))); iim.insert(std::make_pair(UUID(0,0), pc::Node(false, false, 88, ViewId(V_NON_PRIM, UUID(0, 0), 3), 472, 0))); iim.insert(std::make_pair(UUID(0,0), pc::Node(true, false, 78, ViewId(V_PRIM, UUID(0, 0), 87), 52, 1))); iim.insert(std::make_pair(UUID(0,0), pc::Node(false, true, 457, ViewId(V_NON_PRIM, UUID(0, 0), 37), 56, 0xff))); expt_size = 4 // hdr + 4 // seq + 4 + 4*(UUID::serial_size() + sizeof(uint32_t) + 4 + 20 + 8); // NodeMap check_serialization(pci, expt_size, InstallMessage(-1)); UserMessage pcu(0, 7); pcu.checksum(0xfefe, true); expt_size = 4 + 4; check_serialization(pcu, expt_size, UserMessage(-1, -1U)); fail_unless(pcu.serial_size() % 4 == 0); } END_TEST class PCUser : public Toplay { public: PCUser(gu::Config& conf, const UUID& uuid, DummyTransport *tp, Proto* pc) : Toplay(conf), views_(), uuid_(uuid), tp_(tp), pc_(pc) { gcomm::connect(tp_, pc_); gcomm::connect(pc_, this); } const UUID& uuid() const { return uuid_; } DummyTransport* tp() { return tp_; } Proto* pc() { return pc_; } void handle_up(const void* cid, const Datagram& rb, const ProtoUpMeta& um) { if (um.has_view() == true) { const View& view(um.view()); log_info << view; fail_unless(view.type() == V_PRIM || view.type() == V_NON_PRIM); views_.push_back(View(view)); } } void send() { byte_t pl[4] = {1, 2, 3, 4}; Buffer buf(pl, pl + sizeof(pl)); Datagram dg(buf); fail_unless(send_down(dg, ProtoDownMeta()) == 0); } private: PCUser(const PCUser&); void operator=(const PCUser&); list views_; UUID uuid_; DummyTransport* tp_; Proto* pc_; }; void get_msg(Datagram* rb, Message* msg, bool release = true) { assert(msg != 0); if (rb == 0) { log_info << "get_msg: (null)"; } else { // assert(rb->get_header().size() == 0 && rb->get_offset() == 0); const byte_t* begin(gcomm::begin(*rb)); const size_t available(gcomm::available(*rb)); fail_unless(msg->unserialize(begin, available, 0) != 0); log_info << "get_msg: " << msg->to_string(); if (release) delete rb; } } void single_boot(PCUser* pu1) { ProtoUpMeta sum1(pu1->uuid()); View vt0(ViewId(V_TRANS, pu1->uuid(), 0)); vt0.add_member(pu1->uuid(), "n1"); ProtoUpMeta um1(UUID::nil(), ViewId(), &vt0); pu1->pc()->connect(true); // pu1->pc()->shift_to(Proto::S_JOINING); pu1->pc()->handle_up(0, Datagram(), um1); fail_unless(pu1->pc()->state() == Proto::S_TRANS); View vr1(ViewId(V_REG, pu1->uuid(), 1)); vr1.add_member(pu1->uuid(), "n1"); ProtoUpMeta um2(UUID::nil(), ViewId(), &vr1); pu1->pc()->handle_up(0, Datagram(), um2); fail_unless(pu1->pc()->state() == Proto::S_STATES_EXCH); Datagram* rb = pu1->tp()->out(); fail_unless(rb != 0); Message sm1; get_msg(rb, &sm1); fail_unless(sm1.type() == Message::T_STATE); fail_unless(sm1.node_map().size() == 1); { const pc::Node& pi1 = pc::NodeMap::value(sm1.node_map().begin()); fail_unless(pi1.prim() == true); fail_unless(pi1.last_prim() == ViewId(V_PRIM, pu1->uuid(), 0)); } pu1->pc()->handle_msg(sm1, Datagram(), sum1); fail_unless(pu1->pc()->state() == Proto::S_INSTALL); rb = pu1->tp()->out(); fail_unless(rb != 0); Message im1; get_msg(rb, &im1); fail_unless(im1.type() == Message::T_INSTALL); fail_unless(im1.node_map().size() == 1); { const pc::Node& pi1 = pc::NodeMap::value(im1.node_map().begin()); fail_unless(pi1.prim() == true); fail_unless(pi1.last_prim() == ViewId(V_PRIM, pu1->uuid(), 0)); } pu1->pc()->handle_msg(im1, Datagram(), sum1); fail_unless(pu1->pc()->state() == Proto::S_PRIM); } START_TEST(test_pc_view_changes_single) { log_info << "START (test_pc_view_changes_single)"; gu::Config conf; UUID uuid1(0, 0); Proto pc1(conf, uuid1); DummyTransport tp1; PCUser pu1(conf, uuid1, &tp1, &pc1); single_boot(&pu1); } END_TEST static void double_boot(PCUser* pu1, PCUser* pu2) { ProtoUpMeta pum1(pu1->uuid()); ProtoUpMeta pum2(pu2->uuid()); View t11(ViewId(V_TRANS, pu1->pc()->current_view().id())); t11.add_member(pu1->uuid(), "n1"); pu1->pc()->handle_view(t11); fail_unless(pu1->pc()->state() == Proto::S_TRANS); View t12(ViewId(V_TRANS, pu2->uuid(), 0)); t12.add_member(pu2->uuid(), "n2"); // pu2->pc()->shift_to(Proto::S_JOINING); pu2->pc()->connect(false); pu2->pc()->handle_view(t12); fail_unless(pu2->pc()->state() == Proto::S_TRANS); View r1(ViewId(V_REG, pu1->uuid(), pu1->pc()->current_view().id().seq() + 1)); r1.add_member(pu1->uuid(), "n1"); r1.add_member(pu2->uuid(), "n2"); pu1->pc()->handle_view(r1); fail_unless(pu1->pc()->state() == Proto::S_STATES_EXCH); pu2->pc()->handle_view(r1); fail_unless(pu2->pc()->state() == Proto::S_STATES_EXCH); Datagram* rb = pu1->tp()->out(); fail_unless(rb != 0); Message sm1; get_msg(rb, &sm1); fail_unless(sm1.type() == Message::T_STATE); rb = pu2->tp()->out(); fail_unless(rb != 0); Message sm2; get_msg(rb, &sm2); fail_unless(sm2.type() == Message::T_STATE); rb = pu1->tp()->out(); fail_unless(rb == 0); rb = pu2->tp()->out(); fail_unless(rb == 0); pu1->pc()->handle_msg(sm1, Datagram(), pum1); rb = pu1->tp()->out(); fail_unless(rb == 0); fail_unless(pu1->pc()->state() == Proto::S_STATES_EXCH); pu1->pc()->handle_msg(sm2, Datagram(), pum2); fail_unless(pu1->pc()->state() == Proto::S_INSTALL); pu2->pc()->handle_msg(sm1, Datagram(), pum1); rb = pu2->tp()->out(); fail_unless(rb == 0); fail_unless(pu2->pc()->state() == Proto::S_STATES_EXCH); pu2->pc()->handle_msg(sm2, Datagram(), pum2); fail_unless(pu2->pc()->state() == Proto::S_INSTALL); Message im1; UUID imsrc; if (pu1->uuid() < pu2->uuid()) { rb = pu1->tp()->out(); imsrc = pu1->uuid(); } else { rb = pu2->tp()->out(); imsrc = pu2->uuid(); } fail_unless(rb != 0); get_msg(rb, &im1); fail_unless(im1.type() == Message::T_INSTALL); fail_unless(pu1->tp()->out() == 0); fail_unless(pu2->tp()->out() == 0); ProtoUpMeta ipum(imsrc); pu1->pc()->handle_msg(im1, Datagram(), ipum); fail_unless(pu1->pc()->state() == Proto::S_PRIM); pu2->pc()->handle_msg(im1, Datagram(), ipum); fail_unless(pu2->pc()->state() == Proto::S_PRIM); } // Form PC for three instances. static void triple_boot(PCUser* pu1, PCUser* pu2, PCUser* pu3) { fail_unless(pu1->uuid() < pu2->uuid() && pu2->uuid() < pu3->uuid()); // trans views { View tr12(ViewId(V_TRANS, pu1->pc()->current_view().id())); tr12.add_member(pu1->uuid(), ""); tr12.add_member(pu2->uuid(), ""); ProtoUpMeta trum12(UUID::nil(), ViewId(), &tr12); pu1->pc()->handle_up(0, Datagram(), trum12); pu2->pc()->handle_up(0, Datagram(), trum12); fail_unless(pu1->pc()->state() == Proto::S_TRANS); fail_unless(pu2->pc()->state() == Proto::S_TRANS); pu3->pc()->connect(false); View tr3(ViewId(V_TRANS, pu3->uuid(), 0)); tr3.add_member(pu3->uuid(), ""); ProtoUpMeta trum3(UUID::nil(), ViewId(), &tr3); pu3->pc()->handle_up(0, Datagram(), trum3); fail_unless(pu3->pc()->state() == Proto::S_TRANS); } // reg view { View reg( ViewId(V_REG, pu1->uuid(), pu1->pc()->current_view().id().seq() + 1)); reg.add_member(pu1->uuid(), ""); reg.add_member(pu2->uuid(), ""); reg.add_member(pu3->uuid(), ""); ProtoUpMeta pum(UUID::nil(), ViewId(), ®); pu1->pc()->handle_up(0, Datagram(), pum); pu2->pc()->handle_up(0, Datagram(), pum); pu3->pc()->handle_up(0, Datagram(), pum); fail_unless(pu1->pc()->state() == Proto::S_STATES_EXCH); fail_unless(pu2->pc()->state() == Proto::S_STATES_EXCH); fail_unless(pu3->pc()->state() == Proto::S_STATES_EXCH); } // states exch { Datagram* dg(pu1->tp()->out()); fail_unless(dg != 0); pu1->pc()->handle_up(0, *dg, ProtoUpMeta(pu1->uuid())); pu2->pc()->handle_up(0, *dg, ProtoUpMeta(pu1->uuid())); pu3->pc()->handle_up(0, *dg, ProtoUpMeta(pu1->uuid())); delete dg; dg = pu2->tp()->out(); fail_unless(dg != 0); pu1->pc()->handle_up(0, *dg, ProtoUpMeta(pu2->uuid())); pu2->pc()->handle_up(0, *dg, ProtoUpMeta(pu2->uuid())); pu3->pc()->handle_up(0, *dg, ProtoUpMeta(pu2->uuid())); delete dg; dg = pu3->tp()->out(); fail_unless(dg != 0); pu1->pc()->handle_up(0, *dg, ProtoUpMeta(pu3->uuid())); pu2->pc()->handle_up(0, *dg, ProtoUpMeta(pu3->uuid())); pu3->pc()->handle_up(0, *dg, ProtoUpMeta(pu3->uuid())); delete dg; fail_unless(pu1->pc()->state() == Proto::S_INSTALL); fail_unless(pu2->pc()->state() == Proto::S_INSTALL); fail_unless(pu3->pc()->state() == Proto::S_INSTALL); } // install { Datagram* dg(pu1->tp()->out()); fail_unless(dg != 0); pu1->pc()->handle_up(0, *dg, ProtoUpMeta(pu1->uuid())); pu2->pc()->handle_up(0, *dg, ProtoUpMeta(pu1->uuid())); pu3->pc()->handle_up(0, *dg, ProtoUpMeta(pu1->uuid())); delete dg; fail_unless(pu1->pc()->state() == Proto::S_PRIM); fail_unless(pu2->pc()->state() == Proto::S_PRIM); fail_unless(pu3->pc()->state() == Proto::S_PRIM); } } START_TEST(test_pc_view_changes_double) { log_info << "START (test_pc_view_changes_double)"; gu::Config conf; UUID uuid1(1); ProtoUpMeta pum1(uuid1); Proto pc1(conf, uuid1); DummyTransport tp1; PCUser pu1(conf, uuid1, &tp1, &pc1); single_boot(&pu1); UUID uuid2(2); ProtoUpMeta pum2(uuid2); Proto pc2(conf, uuid2); DummyTransport tp2; PCUser pu2(conf, uuid2, &tp2, &pc2); double_boot(&pu1, &pu2); Datagram* rb; View tnp(ViewId(V_TRANS, pu1.pc()->current_view().id())); tnp.add_member(uuid1, "n1"); pu1.pc()->handle_view(tnp); fail_unless(pu1.pc()->state() == Proto::S_TRANS); View reg(ViewId(V_REG, uuid1, pu1.pc()->current_view().id().seq() + 1)); reg.add_member(uuid1, "n1"); pu1.pc()->handle_view(reg); fail_unless(pu1.pc()->state() == Proto::S_STATES_EXCH); rb = pu1.tp()->out(); fail_unless(rb != 0); pu1.pc()->handle_up(0, *rb, ProtoUpMeta(uuid1)); fail_unless(pu1.pc()->state() == Proto::S_NON_PRIM); delete rb; View tpv2(ViewId(V_TRANS, pu2.pc()->current_view().id())); tpv2.add_member(uuid2, "n2"); tpv2.add_left(uuid1, "n1"); pu2.pc()->handle_view(tpv2); fail_unless(pu2.pc()->state() == Proto::S_TRANS); fail_unless(pu2.tp()->out() == 0); View rp2(ViewId(V_REG, uuid2, pu1.pc()->current_view().id().seq() + 1)); rp2.add_member(uuid2, "n2"); rp2.add_left(uuid1, "n1"); pu2.pc()->handle_view(rp2); fail_unless(pu2.pc()->state() == Proto::S_STATES_EXCH); rb = pu2.tp()->out(); fail_unless(rb != 0); Message sm2; get_msg(rb, &sm2); fail_unless(sm2.type() == Message::T_STATE); fail_unless(pu2.tp()->out() == 0); pu2.pc()->handle_msg(sm2, Datagram(), pum2); fail_unless(pu2.pc()->state() == Proto::S_INSTALL); rb = pu2.tp()->out(); fail_unless(rb != 0); Message im2; get_msg(rb, &im2); fail_unless(im2.type() == Message::T_INSTALL); pu2.pc()->handle_msg(im2, Datagram(), pum2); fail_unless(pu2.pc()->state() == Proto::S_PRIM); } END_TEST /* Test that UUID ordering does not matter when starting nodes */ START_TEST(test_pc_view_changes_reverse) { log_info << "START (test_pc_view_changes_reverse)"; gu::Config conf; UUID uuid1(1); ProtoUpMeta pum1(uuid1); Proto pc1(conf, uuid1); DummyTransport tp1; PCUser pu1(conf, uuid1, &tp1, &pc1); UUID uuid2(2); ProtoUpMeta pum2(uuid2); Proto pc2(conf, uuid2); DummyTransport tp2; PCUser pu2(conf, uuid2, &tp2, &pc2); single_boot(&pu2); double_boot(&pu2, &pu1); } END_TEST START_TEST(test_pc_state1) { log_info << "START (test_pc_state1)"; gu::Config conf; UUID uuid1(1); ProtoUpMeta pum1(uuid1); Proto pc1(conf, uuid1); DummyTransport tp1; PCUser pu1(conf, uuid1, &tp1, &pc1); single_boot(&pu1); UUID uuid2(2); ProtoUpMeta pum2(uuid2); Proto pc2(conf, uuid2); DummyTransport tp2; PCUser pu2(conf, uuid2, &tp2, &pc2); // n1: PRIM -> TRANS -> STATES_EXCH -> RTR -> PRIM // n2: JOINING -> STATES_EXCH -> RTR -> PRIM double_boot(&pu1, &pu2); fail_unless(pu1.pc()->state() == Proto::S_PRIM); fail_unless(pu2.pc()->state() == Proto::S_PRIM); // PRIM -> TRANS -> STATES_EXCH -> RTR -> TRANS -> STATES_EXCH -> RTR -> PRIM View tr1(ViewId(V_TRANS, pu1.pc()->current_view().id())); tr1.add_member(uuid1, "n1"); tr1.add_member(uuid2, "n2"); pu1.pc()->handle_view(tr1); pu2.pc()->handle_view(tr1); fail_unless(pu1.pc()->state() == Proto::S_TRANS); fail_unless(pu2.pc()->state() == Proto::S_TRANS); fail_unless(pu1.tp()->out() == 0); fail_unless(pu2.tp()->out() == 0); View reg2(ViewId(V_REG, uuid1, pu1.pc()->current_view().id().seq() + 1)); reg2.add_member(uuid1, "n1"); reg2.add_member(uuid2, "n2"); pu1.pc()->handle_view(reg2); pu2.pc()->handle_view(reg2); fail_unless(pu1.pc()->state() == Proto::S_STATES_EXCH); fail_unless(pu2.pc()->state() == Proto::S_STATES_EXCH); Message msg; get_msg(pu1.tp()->out(), &msg); pu1.pc()->handle_msg(msg, Datagram(), pum1); pu2.pc()->handle_msg(msg, Datagram(), pum1); fail_unless(pu1.pc()->state() == Proto::S_STATES_EXCH); fail_unless(pu2.pc()->state() == Proto::S_STATES_EXCH); get_msg(pu2.tp()->out(), &msg); pu1.pc()->handle_msg(msg, Datagram(), pum2); pu2.pc()->handle_msg(msg, Datagram(), pum2); fail_unless(pu1.pc()->state() == Proto::S_INSTALL); fail_unless(pu2.pc()->state() == Proto::S_INSTALL); View tr2(ViewId(V_TRANS, pu1.pc()->current_view().id())); tr2.add_member(uuid1, "n1"); tr2.add_member(uuid2, "n2"); pu1.pc()->handle_view(tr2); pu2.pc()->handle_view(tr2); fail_unless(pu1.pc()->state() == Proto::S_TRANS); fail_unless(pu2.pc()->state() == Proto::S_TRANS); Message im; if (uuid1 < uuid2) { get_msg(pu1.tp()->out(), &im); pu1.pc()->handle_msg(im, Datagram(), pum1); pu2.pc()->handle_msg(im, Datagram(), pum1); } else { get_msg(pu2.tp()->out(), &im); pu1.pc()->handle_msg(im, Datagram(), pum2); pu2.pc()->handle_msg(im, Datagram(), pum2); } fail_unless(pu1.pc()->state() == Proto::S_TRANS); fail_unless(pu2.pc()->state() == Proto::S_TRANS); View reg3(ViewId(V_REG, uuid1, pu1.pc()->current_view().id().seq() + 1)); reg3.add_member(uuid1, "n1"); reg3.add_member(uuid2, "n2"); pu1.pc()->handle_view(reg3); pu2.pc()->handle_view(reg3); fail_unless(pu1.pc()->state() == Proto::S_STATES_EXCH); fail_unless(pu2.pc()->state() == Proto::S_STATES_EXCH); get_msg(pu1.tp()->out(), &msg); pu1.pc()->handle_msg(msg, Datagram(), pum1); pu2.pc()->handle_msg(msg, Datagram(), pum1); fail_unless(pu1.pc()->state() == Proto::S_STATES_EXCH); fail_unless(pu2.pc()->state() == Proto::S_STATES_EXCH); get_msg(pu2.tp()->out(), &msg); pu1.pc()->handle_msg(msg, Datagram(), pum2); pu2.pc()->handle_msg(msg, Datagram(), pum2); fail_unless(pu1.pc()->state() == Proto::S_INSTALL); fail_unless(pu2.pc()->state() == Proto::S_INSTALL); if (uuid1 < uuid2) { get_msg(pu1.tp()->out(), &im); pu1.pc()->handle_msg(im, Datagram(), pum1); pu2.pc()->handle_msg(im, Datagram(), pum1); } else { get_msg(pu2.tp()->out(), &im); pu1.pc()->handle_msg(im, Datagram(), pum2); pu2.pc()->handle_msg(im, Datagram(), pum2); } fail_unless(pu1.pc()->state() == Proto::S_PRIM); fail_unless(pu2.pc()->state() == Proto::S_PRIM); } END_TEST START_TEST(test_pc_state2) { log_info << "START (test_pc_state2)"; gu::Config conf; UUID uuid1(1); ProtoUpMeta pum1(uuid1); Proto pc1(conf, uuid1); DummyTransport tp1; PCUser pu1(conf, uuid1, &tp1, &pc1); single_boot(&pu1); UUID uuid2(2); ProtoUpMeta pum2(uuid2); Proto pc2(conf, uuid2); DummyTransport tp2; PCUser pu2(conf, uuid2, &tp2, &pc2); // n1: PRIM -> TRANS -> STATES_EXCH -> RTR -> PRIM // n2: JOINING -> STATES_EXCH -> RTR -> PRIM double_boot(&pu1, &pu2); fail_unless(pu1.pc()->state() == Proto::S_PRIM); fail_unless(pu2.pc()->state() == Proto::S_PRIM); // PRIM -> TRANS -> STATES_EXCH -> TRANS -> STATES_EXCH -> RTR -> PRIM View tr1(ViewId(V_TRANS, pu1.pc()->current_view().id())); tr1.add_member(uuid1, "n1"); tr1.add_member(uuid2, "n2"); pu1.pc()->handle_view(tr1); pu2.pc()->handle_view(tr1); fail_unless(pu1.pc()->state() == Proto::S_TRANS); fail_unless(pu2.pc()->state() == Proto::S_TRANS); fail_unless(pu1.tp()->out() == 0); fail_unless(pu2.tp()->out() == 0); View reg2(ViewId(V_REG, uuid1, pu1.pc()->current_view().id().seq() + 1)); reg2.add_member(uuid1, "n1"); reg2.add_member(uuid2, "n2"); pu1.pc()->handle_view(reg2); pu2.pc()->handle_view(reg2); fail_unless(pu1.pc()->state() == Proto::S_STATES_EXCH); fail_unless(pu2.pc()->state() == Proto::S_STATES_EXCH); View tr2(ViewId(V_TRANS, pu1.pc()->current_view().id())); tr2.add_member(uuid1, "n1"); tr2.add_member(uuid2, "n2"); pu1.pc()->handle_view(tr2); pu2.pc()->handle_view(tr2); fail_unless(pu1.pc()->state() == Proto::S_TRANS); fail_unless(pu2.pc()->state() == Proto::S_TRANS); Message msg; get_msg(pu1.tp()->out(), &msg); pu1.pc()->handle_msg(msg, Datagram(), pum1); pu2.pc()->handle_msg(msg, Datagram(), pum1); fail_unless(pu1.pc()->state() == Proto::S_TRANS); fail_unless(pu2.pc()->state() == Proto::S_TRANS); get_msg(pu2.tp()->out(), &msg); pu1.pc()->handle_msg(msg, Datagram(), pum2); pu2.pc()->handle_msg(msg, Datagram(), pum2); fail_unless(pu1.pc()->state() == Proto::S_TRANS); fail_unless(pu2.pc()->state() == Proto::S_TRANS); View reg3(ViewId(V_REG, uuid1, pu1.pc()->current_view().id().seq() + 1)); reg3.add_member(uuid1, "n1"); reg3.add_member(uuid2, "n2"); pu1.pc()->handle_view(reg3); pu2.pc()->handle_view(reg3); fail_unless(pu1.pc()->state() == Proto::S_STATES_EXCH); fail_unless(pu2.pc()->state() == Proto::S_STATES_EXCH); get_msg(pu1.tp()->out(), &msg); pu1.pc()->handle_msg(msg, Datagram(), pum1); pu2.pc()->handle_msg(msg, Datagram(), pum1); fail_unless(pu1.pc()->state() == Proto::S_STATES_EXCH); fail_unless(pu2.pc()->state() == Proto::S_STATES_EXCH); get_msg(pu2.tp()->out(), &msg); pu1.pc()->handle_msg(msg, Datagram(), pum2); pu2.pc()->handle_msg(msg, Datagram(), pum2); fail_unless(pu1.pc()->state() == Proto::S_INSTALL); fail_unless(pu2.pc()->state() == Proto::S_INSTALL); Message im; if (uuid1 < uuid2) { get_msg(pu1.tp()->out(), &im); pu1.pc()->handle_msg(im, Datagram(), pum1); pu2.pc()->handle_msg(im, Datagram(), pum1); } else { get_msg(pu2.tp()->out(), &im); pu1.pc()->handle_msg(im, Datagram(), pum2); pu2.pc()->handle_msg(im, Datagram(), pum2); } fail_unless(pu1.pc()->state() == Proto::S_PRIM); fail_unless(pu2.pc()->state() == Proto::S_PRIM); } END_TEST START_TEST(test_pc_state3) { log_info << "START (test_pc_state3)"; gu::Config conf; UUID uuid1(1); ProtoUpMeta pum1(uuid1); Proto pc1(conf, uuid1); DummyTransport tp1; PCUser pu1(conf, uuid1, &tp1, &pc1); single_boot(&pu1); UUID uuid2(2); ProtoUpMeta pum2(uuid2); Proto pc2(conf, uuid2); DummyTransport tp2; PCUser pu2(conf, uuid2, &tp2, &pc2); // n1: PRIM -> TRANS -> STATES_EXCH -> RTR -> PRIM // n2: JOINING -> STATES_EXCH -> RTR -> PRIM double_boot(&pu1, &pu2); fail_unless(pu1.pc()->state() == Proto::S_PRIM); fail_unless(pu2.pc()->state() == Proto::S_PRIM); // PRIM -> NON_PRIM -> STATES_EXCH -> RTR -> NON_PRIM -> STATES_EXCH -> ... // -> NON_PRIM -> STATES_EXCH -> RTR -> NON_PRIM View tr11(ViewId(V_TRANS, pu1.pc()->current_view().id())); tr11.add_member(uuid1, "n1"); pu1.pc()->handle_view(tr11); View tr12(ViewId(V_TRANS, pu1.pc()->current_view().id())); tr12.add_member(uuid2, "n2"); pu2.pc()->handle_view(tr12); fail_unless(pu1.pc()->state() == Proto::S_TRANS); fail_unless(pu2.pc()->state() == Proto::S_TRANS); fail_unless(pu1.tp()->out() == 0); fail_unless(pu2.tp()->out() == 0); View reg21(ViewId(V_REG, uuid1, pu1.pc()->current_view().id().seq() + 1)); reg21.add_member(uuid1, "n1"); pu1.pc()->handle_view(reg21); fail_unless(pu1.pc()->state() == Proto::S_STATES_EXCH); View reg22(ViewId(V_REG, uuid2, pu2.pc()->current_view().id().seq() + 1)); reg22.add_member(uuid2, "n2"); pu2.pc()->handle_view(reg22); fail_unless(pu2.pc()->state() == Proto::S_STATES_EXCH); Message msg; get_msg(pu1.tp()->out(), &msg); pu1.pc()->handle_msg(msg, Datagram(), pum1); get_msg(pu2.tp()->out(), &msg); pu2.pc()->handle_msg(msg, Datagram(), pum2); fail_unless(pu1.pc()->state() == Proto::S_NON_PRIM); fail_unless(pu2.pc()->state() == Proto::S_NON_PRIM); View tr21(ViewId(V_TRANS, pu1.pc()->current_view().id())); tr21.add_member(uuid1, "n1"); pu1.pc()->handle_view(tr21); View tr22(ViewId(V_TRANS, pu2.pc()->current_view().id())); tr22.add_member(uuid2, "n2"); pu2.pc()->handle_view(tr22); fail_unless(pu1.pc()->state() == Proto::S_TRANS); fail_unless(pu2.pc()->state() == Proto::S_TRANS); fail_unless(pu1.tp()->out() == 0); fail_unless(pu2.tp()->out() == 0); View reg3(ViewId(V_REG, uuid1, pu1.pc()->current_view().id().seq() + 1)); reg3.add_member(uuid1, "n1"); reg3.add_member(uuid2, "n2"); pu1.pc()->handle_view(reg3); pu2.pc()->handle_view(reg3); fail_unless(pu1.pc()->state() == Proto::S_STATES_EXCH); fail_unless(pu2.pc()->state() == Proto::S_STATES_EXCH); get_msg(pu1.tp()->out(), &msg); pu1.pc()->handle_msg(msg, Datagram(), pum1); pu2.pc()->handle_msg(msg, Datagram(), pum1); fail_unless(pu1.pc()->state() == Proto::S_STATES_EXCH); fail_unless(pu2.pc()->state() == Proto::S_STATES_EXCH); get_msg(pu2.tp()->out(), &msg); pu1.pc()->handle_msg(msg, Datagram(), pum2); pu2.pc()->handle_msg(msg, Datagram(), pum2); fail_unless(pu1.pc()->state() == Proto::S_INSTALL); fail_unless(pu2.pc()->state() == Proto::S_INSTALL); Message im; if (uuid1 < uuid2) { get_msg(pu1.tp()->out(), &im); pu1.pc()->handle_msg(im, Datagram(), pum1); pu2.pc()->handle_msg(im, Datagram(), pum1); } else { get_msg(pu2.tp()->out(), &im); pu1.pc()->handle_msg(im, Datagram(), pum2); pu2.pc()->handle_msg(im, Datagram(), pum2); } fail_unless(pu1.pc()->state() == Proto::S_PRIM); fail_unless(pu2.pc()->state() == Proto::S_PRIM); } END_TEST START_TEST(test_pc_conflicting_prims) { log_info << "START (test_pc_conflicting_prims)"; gu::Config conf; UUID uuid1(1); ProtoUpMeta pum1(uuid1); Proto pc1(conf, uuid1); DummyTransport tp1; PCUser pu1(conf, uuid1, &tp1, &pc1); single_boot(&pu1); UUID uuid2(2); ProtoUpMeta pum2(uuid2); Proto pc2(conf, uuid2); DummyTransport tp2; PCUser pu2(conf, uuid2, &tp2, &pc2); single_boot(&pu2); View tr1(ViewId(V_TRANS, pu1.pc()->current_view().id())); tr1.add_member(uuid1); pu1.pc()->handle_view(tr1); View tr2(ViewId(V_TRANS, pu2.pc()->current_view().id())); tr2.add_member(uuid2); pu2.pc()->handle_view(tr2); View reg(ViewId(V_REG, uuid1, tr1.id().seq() + 1)); reg.add_member(uuid1); reg.add_member(uuid2); pu1.pc()->handle_view(reg); pu2.pc()->handle_view(reg); Message msg1, msg2; /* First node must discard msg2 and stay in states exch waiting for * trans view */ get_msg(pu1.tp()->out(), &msg1); get_msg(pu2.tp()->out(), &msg2); fail_unless(pu1.pc()->state() == Proto::S_STATES_EXCH); pu1.pc()->handle_msg(msg1, Datagram(), pum1); pu1.pc()->handle_msg(msg2, Datagram(), pum2); /* Second node must abort */ try { pu2.pc()->handle_msg(msg1, Datagram(), pum1); fail("not aborted"); } catch (Exception& e) { log_info << e.what(); } fail_unless(pu1.tp()->out() == 0); View tr3(ViewId(V_TRANS, reg.id())); tr3.add_member(uuid1); pu1.pc()->handle_view(tr3); View reg3(ViewId(V_REG, uuid1, tr3.id().seq() + 1)); reg3.add_member(uuid1); pu1.pc()->handle_view(reg3); get_msg(pu1.tp()->out(), &msg1); pu1.pc()->handle_msg(msg1, Datagram(), pum1); get_msg(pu1.tp()->out(), &msg1); pu1.pc()->handle_msg(msg1, Datagram(), pum1); fail_unless(pu1.pc()->state() == Proto::S_PRIM); } END_TEST START_TEST(test_pc_conflicting_prims_npvo) { log_info << "START (test_pc_conflicting_npvo)"; gu::Config conf; UUID uuid1(1); ProtoUpMeta pum1(uuid1); Proto pc1(conf, uuid1, URI("pc://?pc.npvo=true")); DummyTransport tp1; PCUser pu1(conf, uuid1, &tp1, &pc1); single_boot(&pu1); UUID uuid2(2); ProtoUpMeta pum2(uuid2); Proto pc2(conf, uuid2, URI("pc://?pc.npvo=true")); DummyTransport tp2; PCUser pu2(conf, uuid2, &tp2, &pc2); single_boot(&pu2); View tr1(ViewId(V_TRANS, pu1.pc()->current_view().id())); tr1.add_member(uuid1); pu1.pc()->handle_view(tr1); View tr2(ViewId(V_TRANS, pu2.pc()->current_view().id())); tr2.add_member(uuid2); pu2.pc()->handle_view(tr2); View reg(ViewId(V_REG, uuid1, tr1.id().seq() + 1)); reg.add_member(uuid1); reg.add_member(uuid2); pu1.pc()->handle_view(reg); pu2.pc()->handle_view(reg); Message msg1, msg2; /* First node must discard msg2 and stay in states exch waiting for * trans view */ get_msg(pu1.tp()->out(), &msg1); get_msg(pu2.tp()->out(), &msg2); fail_unless(pu1.pc()->state() == Proto::S_STATES_EXCH); pu1.pc()->handle_msg(msg1, Datagram(), pum1); pu2.pc()->handle_msg(msg1, Datagram(), pum1); /* First node must abort */ try { pu1.pc()->handle_msg(msg2, Datagram(), pum2); fail("not aborted"); } catch (Exception& e) { log_info << e.what(); } fail_unless(pu2.tp()->out() == 0); View tr3(ViewId(V_TRANS, reg.id())); tr3.add_member(uuid2); pu2.pc()->handle_view(tr3); View reg3(ViewId(V_REG, uuid2, tr3.id().seq() + 1)); reg3.add_member(uuid2); pu2.pc()->handle_view(reg3); get_msg(pu2.tp()->out(), &msg2); pu2.pc()->handle_msg(msg2, Datagram(), pum2); get_msg(pu2.tp()->out(), &msg2); pu2.pc()->handle_msg(msg2, Datagram(), pum2); fail_unless(pu2.pc()->state() == Proto::S_PRIM); } END_TEST static void join_node(PropagationMatrix* p, DummyNode* n, bool first) { log_info << first; gu_trace(p->insert_tp(n)); gu_trace(n->connect(first)); } static void send_n(DummyNode* node, const size_t n) { for (size_t i = 0; i < n; ++i) { gu_trace(node->send()); } } static void set_cvi(vector& nvec, size_t i_begin, size_t i_end, size_t seq, ViewType type) { for (size_t i = i_begin; i <= i_end; ++i) { nvec[i]->set_cvi(ViewId(type, type == V_NON_PRIM ? nvec[0]->uuid() : nvec[i_begin]->uuid(), static_cast(type == V_NON_PRIM ? seq - 1 : seq))); } } static gu::Config gu_conf; static DummyNode* create_dummy_node(size_t idx, const string& inactive_timeout = "PT1H", const string& retrans_period = "PT1H", int weight = 1) { const string conf = "evs://?" + Conf::EvsViewForgetTimeout + "=PT1H&" + Conf::EvsInactiveCheckPeriod + "=" + to_string(Period(inactive_timeout)/3) + "&" + Conf::EvsInactiveTimeout + "=" + inactive_timeout + "&" + Conf::EvsKeepalivePeriod + "=" + retrans_period + "&" + Conf::EvsJoinRetransPeriod + "=" + retrans_period + "&" + Conf::EvsInstallTimeout + "=" + inactive_timeout + "&" + Conf::PcWeight + "=" + gu::to_string(weight); list protos; try { UUID uuid(static_cast(idx)); protos.push_back(new DummyTransport(uuid, false)); protos.push_back(new evs::Proto(gu_conf, uuid, conf)); protos.push_back(new Proto(gu_conf, uuid, conf)); return new DummyNode(gu_conf, idx, protos); } catch (...) { for_each(protos.begin(), protos.end(), DeleteObject()); throw; } } static ViewType view_type(const size_t i_begin, const size_t i_end, const size_t n_nodes) { return (((i_end - i_begin + 1)*2 > n_nodes) ? V_PRIM : V_NON_PRIM); } START_TEST(test_pc_split_merge) { log_info << "START (test_pc_split_merge)"; size_t n_nodes(5); vector dn; PropagationMatrix prop; const string inactive_timeout("PT0.7S"); const string retrans_period("PT0.1S"); uint32_t view_seq = 0; for (size_t i = 0; i < n_nodes; ++i) { dn.push_back(create_dummy_node(i + 1, inactive_timeout, retrans_period)); gu_trace(join_node(&prop, dn[i], i == 0)); set_cvi(dn, 0, i, ++view_seq, V_PRIM); gu_trace(prop.propagate_until_cvi(false)); } for (size_t i = 1; i < n_nodes; ++i) { for (size_t j = 0; j < i; ++j) { for (size_t k = i; k < n_nodes; ++k) { prop.split(j + 1, k + 1); } } ++view_seq; log_info << "split " << i << " view seq " << view_seq; set_cvi(dn, 0, i - 1, view_seq, view_type(0, i - 1, n_nodes)); set_cvi(dn, i, n_nodes - 1, view_seq, view_type(i, n_nodes - 1, n_nodes)); gu_trace(prop.propagate_until_cvi(true)); for (size_t j = 0; j < i; ++j) { for (size_t k = i; k < n_nodes; ++k) { prop.merge(j + 1, k + 1); } } ++view_seq; log_info << "merge " << i << " view seq " << view_seq; set_cvi(dn, 0, n_nodes - 1, view_seq, V_PRIM); gu_trace(prop.propagate_until_cvi(true)); } check_trace(dn); for_each(dn.begin(), dn.end(), DeleteObject()); } END_TEST START_TEST(test_pc_split_merge_w_user_msg) { log_info << "START (test_pc_split_merge_w_user_msg)"; size_t n_nodes(5); vector dn; PropagationMatrix prop; const string inactive_timeout("PT0.7S"); const string retrans_period("PT0.1S"); uint32_t view_seq = 0; for (size_t i = 0; i < n_nodes; ++i) { dn.push_back(create_dummy_node(i + 1, inactive_timeout, retrans_period)); gu_trace(join_node(&prop, dn[i], i == 0)); set_cvi(dn, 0, i, ++view_seq, V_PRIM); gu_trace(prop.propagate_until_cvi(false)); } for (size_t i = 1; i < n_nodes; ++i) { for (size_t j = 0; j < n_nodes; ++j) { send_n(dn[j], ::rand() % 5); } for (size_t j = 0; j < i; ++j) { for (size_t k = i; k < n_nodes; ++k) { prop.split(j + 1, k + 1); } } ++view_seq; log_info << "split " << i << " view seq " << view_seq; set_cvi(dn, 0, i - 1, view_seq, view_type(0, i - 1, n_nodes)); set_cvi(dn, i, n_nodes - 1, view_seq, view_type(i, n_nodes - 1, n_nodes)); gu_trace(prop.propagate_until_cvi(true)); for (size_t j = 0; j < n_nodes; ++j) { send_n(dn[j], ::rand() % 5); } for (size_t j = 0; j < i; ++j) { for (size_t k = i; k < n_nodes; ++k) { prop.merge(j + 1, k + 1); } } ++view_seq; log_info << "merge " << i << " view seq " << view_seq; set_cvi(dn, 0, n_nodes - 1, view_seq, V_PRIM); gu_trace(prop.propagate_until_cvi(true)); } check_trace(dn); for_each(dn.begin(), dn.end(), DeleteObject()); } END_TEST START_TEST(test_pc_complete_split_merge) { log_info << "START (test_pc_complete_split_merge)"; size_t n_nodes(5); vector dn; PropagationMatrix prop; const string inactive_timeout("PT0.31S"); const string retrans_period("PT0.1S"); uint32_t view_seq = 0; for (size_t i = 0; i < n_nodes; ++i) { dn.push_back(create_dummy_node(i + 1, inactive_timeout, retrans_period)); log_info << "i " << i; gu_trace(join_node(&prop, dn[i], i == 0)); set_cvi(dn, 0, i, ++view_seq, V_PRIM); gu_trace(prop.propagate_until_cvi(false)); } for (size_t i = 0; i < 5; ++i) { for (size_t j = 0; j < n_nodes; ++j) { send_n(dn[j], ::rand() % 5); } prop.propagate_n(9 + ::rand() % 5); for (size_t j = 0; j < n_nodes; ++j) { for (size_t k = 0; k < n_nodes; ++k) { if (j != k) { prop.split(j + 1, k + 1); } } } ++view_seq; log_info << "split " << i << " view seq " << view_seq; set_cvi(dn, 0, n_nodes - 1, view_seq, V_NON_PRIM); gu_trace(prop.propagate_until_cvi(true)); for (size_t j = 0; j < n_nodes; ++j) { for (size_t k = 0; k < n_nodes; ++k) { if (j != k) { prop.merge(j + 1, k + 1); } } } ++view_seq; log_info << "merge " << i << " view seq " << view_seq; set_cvi(dn, 0, n_nodes - 1, view_seq, V_PRIM); gu_trace(prop.propagate_until_cvi(true)); } check_trace(dn); for_each(dn.begin(), dn.end(), DeleteObject()); } END_TEST class PCUser2 : public Toplay { Transport* tp_; bool sending_; uint8_t my_type_; bool send_; Period send_period_; Date next_send_; PCUser2(const PCUser2&); void operator=(const PCUser2); public: PCUser2(Protonet& net, const string& uri, const bool send = true) : Toplay(net.conf()), tp_(Transport::create(net, uri)), sending_(false), my_type_(static_cast(1 + ::rand()%4)), send_(send), send_period_("PT0.05S"), next_send_(Date::max()) { } ~PCUser2() { delete tp_; } void start() { gcomm::connect(tp_, this); tp_->connect(); gcomm::disconnect(tp_, this); tp_->pstack().push_proto(this); } void stop() { sending_ = false; tp_->pstack().pop_proto(this); gcomm::connect(tp_, this); tp_->close(); gcomm::disconnect(tp_, this); } void handle_up(const void* cid, const Datagram& rb, const ProtoUpMeta& um) { if (um.has_view()) { const View& view(um.view()); log_info << view; if (view.type() == V_PRIM && send_ == true) { sending_ = true; next_send_ = Date::now() + send_period_; } } else { // log_debug << "received message: " << um.get_to_seq(); fail_unless(rb.len() - rb.offset() == 16); if (um.source() == tp_->uuid()) { fail_unless(um.user_type() == my_type_); } } } Protostack& pstack() { return tp_->pstack(); } Date handle_timers() { Date now(Date::now()); if (now >= next_send_) { byte_t buf[16]; memset(buf, 0xa, sizeof(buf)); Datagram dg(Buffer(buf, buf + sizeof(buf))); // dg.get_header().resize(128); // dg.set_header_offset(128); int ret = send_down(dg, ProtoDownMeta(my_type_, rand() % 10 == 0 ? O_SAFE : O_LOCAL_CAUSAL)); if (ret != 0) { // log_debug << "send down " << ret; } next_send_ = next_send_ + send_period_; } return next_send_; } std::string listen_addr() const { return tp_->listen_addr(); } }; START_TEST(test_pc_transport) { log_info << "START (test_pc_transport)"; gu::Config conf; auto_ptr net(Protonet::create(conf)); PCUser2 pu1(*net, "pc://?" "evs.info_log_mask=0xff&" "gmcast.listen_addr=tcp://127.0.0.1:0&" "gmcast.group=pc&" "gmcast.time_wait=PT0.5S&" "node.name=n1"); gu_conf_self_tstamp_on(); pu1.start(); net->event_loop(5*Sec); PCUser2 pu2(*net, std::string("pc://") + pu1.listen_addr().erase(0, strlen("tcp://")) + "?evs.info_log_mask=0xff&" "gmcast.group=pc&" "gmcast.time_wait=PT0.5S&" "gmcast.listen_addr=tcp://127.0.0.1:0&" "node.name=n2"); PCUser2 pu3(*net, std::string("pc://") + pu1.listen_addr().erase(0, strlen("tcp://")) + "?evs.info_log_mask=0xff&" "gmcast.group=pc&" "gmcast.time_wait=PT0.5S&" "gmcast.listen_addr=tcp://127.0.0.1:0&" "node.name=n3"); pu2.start(); net->event_loop(5*Sec); pu3.start(); net->event_loop(5*Sec); pu3.stop(); net->event_loop(5*Sec); pu2.stop(); net->event_loop(5*Sec); pu1.stop(); log_info << "cleanup"; net->event_loop(0); log_info << "finished"; } END_TEST START_TEST(test_trac_191) { log_info << "START (test_trac_191)"; gu::Config conf; UUID uuid1(1), uuid2(2), uuid3(3), uuid4(4); Proto p(conf, uuid4); DummyTransport tp(uuid4, true); // gcomm::connect(&tp, &p); PCUser pu(conf, uuid4, &tp, &p); p.shift_to(Proto::S_NON_PRIM); View t0(ViewId(V_TRANS, uuid4, 0)); t0.add_member(uuid4); p.handle_view(t0); View r5(ViewId(V_REG, uuid2, 5)); r5.add_member(uuid3); r5.add_member(uuid4); p.handle_view(r5); Datagram* dg = tp.out(); fail_unless(dg != 0); Message sm4; get_msg(dg, &sm4); fail_unless(sm4.type() == Message::T_STATE); // Handle first sm from uuid3 StateMessage sm3(0); pc::NodeMap& im3(sm3.node_map()); im3.insert_unique(make_pair(uuid1, pc::Node(true, false, 254, ViewId(V_PRIM, uuid1, 3), 20))); im3.insert_unique(make_pair(uuid2, pc::Node(true, false, 254, ViewId(V_PRIM, uuid1, 3), 20))); im3.insert_unique(make_pair(uuid3, pc::Node(false, false, 254, ViewId(V_PRIM, uuid1, 3), 25))); p.handle_msg(sm3, Datagram(), ProtoUpMeta(uuid3)); p.handle_msg(sm4, Datagram(), ProtoUpMeta(uuid4)); } END_TEST START_TEST(test_trac_413) { log_info << "START (test_trac_413)"; class TN : gcomm::Toplay // test node { public: TN(gu::Config conf, const UUID& uuid) : Toplay(conf), p_(conf, uuid), tp_(uuid, true) { gcomm::connect(&tp_, &p_); gcomm::connect(&p_, this); } const UUID& uuid() const { return p_.uuid(); } gcomm::pc::Proto& p() { return p_; } DummyTransport& tp() { return tp_; } void handle_up(const void* id, const Datagram& dg, const gcomm::ProtoUpMeta& um) { // void } private: pc::Proto p_; DummyTransport tp_; }; gu::Config conf; TN n1(conf, 1), n2(conf, 2), n3(conf, 3); // boot to first prim { gcomm::View tr(ViewId(V_TRANS, n1.uuid(), 0)); tr.members().insert_unique(std::make_pair(n1.uuid(), "")); n1.p().connect(true); n1.p().handle_view(tr); Datagram* dg(n1.tp().out()); fail_unless(dg == 0 && n1.p().state() == gcomm::pc::Proto::S_TRANS); gcomm::View reg(ViewId(V_REG, n1.uuid(), 1)); reg.members().insert_unique(std::make_pair(n1.uuid(), "")); n1.p().handle_view(reg); dg = n1.tp().out(); fail_unless(dg != 0 && n1.p().state() == gcomm::pc::Proto::S_STATES_EXCH); n1.p().handle_up(0, *dg, gcomm::ProtoUpMeta(n1.uuid())); delete dg; dg = n1.tp().out(); fail_unless(dg != 0 && n1.p().state() == gcomm::pc::Proto::S_INSTALL); n1.p().handle_up(0, *dg, gcomm::ProtoUpMeta(n1.uuid())); delete dg; dg = n1.tp().out(); fail_unless(dg == 0 && n1.p().state() == gcomm::pc::Proto::S_PRIM); } // add remaining nodes { gcomm::View tr(ViewId(V_TRANS, n1.uuid(), 1)); tr.members().insert_unique(std::make_pair(n1.uuid(), "")); n1.p().handle_view(tr); } { gcomm::View tr(ViewId(V_TRANS, n2.uuid(), 0)); tr.members().insert_unique(std::make_pair(n2.uuid(), "")); n2.p().connect(false); n2.p().handle_view(tr); } { gcomm::View tr(ViewId(V_TRANS, n3.uuid(), 0)); tr.members().insert_unique(std::make_pair(n3.uuid(), "")); n3.p().connect(false); n3.p().handle_view(tr); } { gcomm::View reg(ViewId(V_REG, n1.uuid(), 2)); reg.members().insert_unique(std::make_pair(n1.uuid(), "")); reg.members().insert_unique(std::make_pair(n2.uuid(), "")); reg.members().insert_unique(std::make_pair(n3.uuid(), "")); n1.p().handle_view(reg); n2.p().handle_view(reg); n3.p().handle_view(reg); Datagram* dg(n1.tp().out()); fail_unless(dg != 0); n1.p().handle_up(0, *dg, gcomm::ProtoUpMeta(n1.uuid())); n2.p().handle_up(0, *dg, gcomm::ProtoUpMeta(n1.uuid())); n3.p().handle_up(0, *dg, gcomm::ProtoUpMeta(n1.uuid())); delete dg; dg = n2.tp().out(); fail_unless(dg != 0); n1.p().handle_up(0, *dg, gcomm::ProtoUpMeta(n2.uuid())); n2.p().handle_up(0, *dg, gcomm::ProtoUpMeta(n2.uuid())); n3.p().handle_up(0, *dg, gcomm::ProtoUpMeta(n2.uuid())); delete dg; dg = n3.tp().out(); fail_unless(dg != 0); n1.p().handle_up(0, *dg, gcomm::ProtoUpMeta(n3.uuid())); n2.p().handle_up(0, *dg, gcomm::ProtoUpMeta(n3.uuid())); n3.p().handle_up(0, *dg, gcomm::ProtoUpMeta(n3.uuid())); delete dg; dg = n1.tp().out(); fail_unless(dg != 0); n1.p().handle_up(0, *dg, gcomm::ProtoUpMeta(n1.uuid())); n2.p().handle_up(0, *dg, gcomm::ProtoUpMeta(n1.uuid())); n3.p().handle_up(0, *dg, gcomm::ProtoUpMeta(n1.uuid())); delete dg; fail_unless(n1.tp().out() == 0 && n1.p().state() == gcomm::pc::Proto::S_PRIM); fail_unless(n2.tp().out() == 0 && n2.p().state() == gcomm::pc::Proto::S_PRIM); fail_unless(n3.tp().out() == 0 && n3.p().state() == gcomm::pc::Proto::S_PRIM); } mark_point(); // drop n1 from view and deliver only state messages in // the following reg view { gcomm::View tr(gcomm::ViewId(V_TRANS, n1.uuid(), 2)); tr.members().insert_unique(std::make_pair(n2.uuid(), "")); tr.members().insert_unique(std::make_pair(n3.uuid(), "")); n2.p().handle_view(tr); n3.p().handle_view(tr); gcomm::View reg(gcomm::ViewId(V_REG, n2.uuid(), 3)); reg.members().insert_unique(std::make_pair(n2.uuid(), "")); reg.members().insert_unique(std::make_pair(n3.uuid(), "")); n2.p().handle_view(reg); n3.p().handle_view(reg); Datagram* dg(n2.tp().out()); n2.p().handle_up(0, *dg, gcomm::ProtoUpMeta(n2.uuid())); n3.p().handle_up(0, *dg, gcomm::ProtoUpMeta(n2.uuid())); delete dg; dg = n3.tp().out(); n2.p().handle_up(0, *dg, gcomm::ProtoUpMeta(n3.uuid())); n3.p().handle_up(0, *dg, gcomm::ProtoUpMeta(n3.uuid())); delete dg; } mark_point(); // drop n2 from view and make sure that n3 ends in non-prim { gcomm::View tr(gcomm::ViewId(V_TRANS, n2.uuid(), 3)); tr.members().insert_unique(std::make_pair(n3.uuid(), "")); n3.p().handle_view(tr); fail_unless(n3.tp().out() == 0 && n3.p().state() == gcomm::pc::Proto::S_TRANS); gcomm::View reg(gcomm::ViewId(V_REG, n3.uuid(), 4)); reg.members().insert_unique(std::make_pair(n3.uuid(), "")); n3.p().handle_view(reg); fail_unless(n3.p().state() == gcomm::pc::Proto::S_STATES_EXCH); Datagram* dg(n3.tp().out()); fail_unless(dg != 0); n3.p().handle_up(0, *dg, ProtoUpMeta(n3.uuid())); dg = n3.tp().out(); fail_unless(dg == 0 && n3.p().state() == gcomm::pc::Proto::S_NON_PRIM, "%p %d", dg, n3.p().state()); } } END_TEST START_TEST(test_fifo_violation) { log_info << "START (test_fifo_violation)"; gu::Config conf; UUID uuid1(1); ProtoUpMeta pum1(uuid1); Proto pc1(conf, uuid1); DummyTransport tp1; PCUser pu1(conf, uuid1, &tp1, &pc1); single_boot(&pu1); assert(pc1.state() == Proto::S_PRIM); pu1.send(); pu1.send(); Datagram* dg1(tp1.out()); fail_unless(dg1 != 0); Datagram* dg2(tp1.out()); fail_unless(dg2 != 0); try { pc1.handle_up(0, *dg2, ProtoUpMeta(uuid1, ViewId(), 0, 0xff, O_SAFE)); fail(""); } catch (Exception& e) { fail_unless(e.get_errno() == ENOTRECOVERABLE); } delete dg1; delete dg2; } END_TEST START_TEST(test_checksum) { log_info << "START (test_checksum)"; gu::Config conf; conf.set(Conf::PcChecksum, gu::to_string(true)); UUID uuid1(1); ProtoUpMeta pum1(uuid1); Proto pc1(conf, uuid1); DummyTransport tp1; PCUser pu1(conf, uuid1, &tp1, &pc1); single_boot(&pu1); assert(pc1.state() == Proto::S_PRIM); pu1.send(); Datagram* dg(tp1.out()); fail_unless(dg != 0); dg->normalize(); pc1.handle_up(0, *dg, ProtoUpMeta(uuid1)); delete dg; pu1.send(); dg = tp1.out(); fail_unless(dg != 0); dg->normalize(); *(&dg->payload()[0] + dg->payload().size() - 1) ^= 0x10; try { pc1.handle_up(0, *dg, ProtoUpMeta(uuid1)); fail(""); } catch (Exception& e) { fail_unless(e.get_errno() == ENOTRECOVERABLE); } delete dg; } END_TEST START_TEST(test_set_param) { log_info << "START (test_pc_transport)"; gu::Config conf; auto_ptr net(Protonet::create(conf)); PCUser2 pu1(*net, "pc://?" "evs.info_log_mask=0xff&" "gmcast.listen_addr=tcp://127.0.0.1:0&" "gmcast.group=pc&" "gmcast.time_wait=PT0.5S&" "node.name=n1"); pu1.start(); // no such a parameter fail_unless(net->set_param("foo.bar", "1") == false); const evs::seqno_t send_window( gu::from_string(conf.get("evs.send_window"))); const evs::seqno_t user_send_window( gu::from_string(conf.get("evs.user_send_window"))); try { net->set_param("evs.send_window", gu::to_string(user_send_window - 1)); fail("exception not thrown"); } catch (gu::Exception& e) { fail_unless(e.get_errno() == ERANGE, "%d: %s", e.get_errno(), e.what()); } try { net->set_param("evs.user_send_window", gu::to_string(send_window + 1)); fail("exception not thrown"); } catch (gu::Exception& e) { fail_unless(e.get_errno() == ERANGE, "%d: %s", e.get_errno(), e.what()); } // Note: These checks may have to change if defaults are changed fail_unless(net->set_param( "evs.send_window", gu::to_string(send_window - 1)) == true); fail_unless(gu::from_string(conf.get("evs.send_window")) == send_window - 1); fail_unless(net->set_param( "evs.user_send_window", gu::to_string(user_send_window + 1)) == true); fail_unless(gu::from_string( conf.get("evs.user_send_window")) == user_send_window + 1); pu1.stop(); } END_TEST START_TEST(test_trac_599) { class D : public gcomm::Toplay { public: D(gu::Config& conf) : gcomm::Toplay(conf) { } void handle_up(const void* id, const Datagram& dg, const gcomm::ProtoUpMeta& um) { } }; gu::Config conf; D d(conf); std::auto_ptr pnet(gcomm::Protonet::create(conf)); std::auto_ptr tp( gcomm::Transport::create(*pnet, "pc://?gmcast.group=test&gmcast.listen_addr=tcp://127.0.0.1:0")); gcomm::connect(tp.get(), &d); gu::Buffer buf(10); Datagram dg(buf); int err; err = tp->send_down(dg, gcomm::ProtoDownMeta()); fail_unless(err == ENOTCONN, "%d", err); tp->connect(); buf.resize(tp->mtu()); Datagram dg2(buf); err = tp->send_down(dg2, gcomm::ProtoDownMeta()); fail_unless(err == 0, "%d", err); buf.resize(buf.size() + 1); Datagram dg3(buf); err = tp->send_down(dg3, gcomm::ProtoDownMeta()); fail_unless(err == EMSGSIZE, "%d", err); pnet->event_loop(gu::datetime::Sec); tp->close(); } END_TEST // test for forced teardown START_TEST(test_trac_620) { gu::Config conf; auto_ptr net(Protonet::create(conf)); Transport* tp(Transport::create(*net, "pc://?" "evs.info_log_mask=0xff&" "gmcast.listen_addr=tcp://127.0.0.1:0&" "gmcast.group=pc&" "gmcast.time_wait=PT0.5S&" "node.name=n1")); class D : public gcomm::Toplay { public: D(gu::Config& conf) : gcomm::Toplay(conf) { } void handle_up(const void* id, const Datagram& dg, const gcomm::ProtoUpMeta& um) { } }; D d(conf); gcomm::connect(tp, &d); tp->connect(); tp->close(true); gcomm::disconnect(tp, &d); delete tp; } END_TEST START_TEST(test_trac_277) { log_info << "START (test_trac_277)"; size_t n_nodes(3); vector dn; PropagationMatrix prop; const string inactive_timeout("PT0.7S"); const string retrans_period("PT0.1S"); uint32_t view_seq = 0; for (size_t i = 0; i < n_nodes; ++i) { dn.push_back(create_dummy_node(i + 1, inactive_timeout, retrans_period)); gu_trace(join_node(&prop, dn[i], i == 0)); set_cvi(dn, 0, i, ++view_seq, V_PRIM); gu_trace(prop.propagate_until_cvi(false)); } log_info << "generate messages"; send_n(dn[0], 1); send_n(dn[1], 1); send_n(dn[2], 1); gu_trace(prop.propagate_until_empty()); log_info << "isolate 3"; prop.split(1, 3); prop.split(2, 3); ++view_seq; set_cvi(dn, 0, 1, view_seq, V_PRIM); set_cvi(dn, 2, 2, view_seq, V_NON_PRIM); gu_trace(prop.propagate_until_cvi(true)); log_info << "isolate 1 and 2"; ++view_seq; prop.split(1, 2); set_cvi(dn, 0, 1, view_seq, V_NON_PRIM); gu_trace(prop.propagate_until_cvi(true)); log_info << "merge 1 and 2"; ++view_seq; prop.merge(1, 2); set_cvi(dn, 0, 1, view_seq, V_PRIM); gu_trace(prop.propagate_until_cvi(true)); log_info << "merge 3"; ++view_seq; prop.merge(1, 3); prop.merge(2, 3); set_cvi(dn, 0, 2, view_seq, V_PRIM); gu_trace(prop.propagate_until_cvi(true)); check_trace(dn); for_each(dn.begin(), dn.end(), DeleteObject()); } END_TEST // This test checks the case when another node of two node cluster // crashes or becomes completely isolated and prim view of cluster // is established by starting third instance directly in prim mode. START_TEST(test_trac_622_638) { log_info << "START (test_trac_622_638)"; vector dn; PropagationMatrix prop; const string inactive_timeout("PT0.7S"); const string retrans_period("PT0.1S"); uint32_t view_seq = 0; // Create two node cluster and make it split. First node is // considered crashed after split (stay isolated in non-prim). dn.push_back(create_dummy_node(1, inactive_timeout, retrans_period)); gu_trace(join_node(&prop, dn[0], true)); set_cvi(dn, 0, 0, ++view_seq, V_PRIM); gu_trace(prop.propagate_until_cvi(false)); dn.push_back(create_dummy_node(2, inactive_timeout, retrans_period)); gu_trace(join_node(&prop, dn[1], false)); set_cvi(dn, 0, 1, ++view_seq, V_PRIM); gu_trace(prop.propagate_until_cvi(false)); log_info << "generate messages"; send_n(dn[0], 1); send_n(dn[1], 1); gu_trace(prop.propagate_until_empty()); log_info << "isolate 1 and 2"; prop.split(1, 2); ++view_seq; set_cvi(dn, 0, 0, view_seq, V_NON_PRIM); set_cvi(dn, 1, 1, view_seq, V_NON_PRIM); gu_trace(prop.propagate_until_cvi(true)); // Add third node which will be connected with node 2. This will // be started with prim status. dn.push_back(create_dummy_node(3, inactive_timeout, retrans_period)); gu_trace(join_node(&prop, dn[2], true)); prop.split(1, 3); // avoid 1 <-> 3 communication ++view_seq; set_cvi(dn, 1, 2, view_seq, V_PRIM); gu_trace(prop.propagate_until_cvi(false)); check_trace(dn); for_each(dn.begin(), dn.end(), DeleteObject()); } END_TEST START_TEST(test_weighted_quorum) { log_info << "START (test_weighted_quorum)"; size_t n_nodes(3); vector dn; PropagationMatrix prop; const string inactive_timeout("PT0.7S"); const string retrans_period("PT0.1S"); uint32_t view_seq = 0; for (size_t i = 0; i < n_nodes; ++i) { dn.push_back(create_dummy_node(i + 1, inactive_timeout, retrans_period, i)); gu_trace(join_node(&prop, dn[i], i == 0)); set_cvi(dn, 0, i, ++view_seq, V_PRIM); gu_trace(prop.propagate_until_cvi(false)); } // split node 3 (weight 2) out, node 3 should remain in prim while // nodes 1 and 2 (weights 0 + 1 = 1) should end up in non-prim prop.split(1, 3); prop.split(2, 3); ++view_seq; set_cvi(dn, 0, 1, view_seq, V_NON_PRIM); set_cvi(dn, 2, 2, view_seq, V_PRIM); gu_trace(prop.propagate_until_cvi(true)); } END_TEST // // The scenario is the following (before fix): // // - Two nodes 2 and 3 started with weights 1 // - Third node 1 with weight 3 is brought in the cluster // (becomes representative) // - Partitioning to (1) and (2, 3) happens so that INSTALL message is // delivered on 2 and 3 in TRANS and on 1 in REG // - Node 1 forms PC // - Nodes 2 and 3 renegotiate and form PC too because node 1 was not present // in the previous PC // // What should happen is that nodes 2 and 3 recompute quorum on handling // install message and shift to non-PC // START_TEST(test_weighted_partitioning_1) { log_info << "START (test_weighted_partitioning_1)"; gu::Config conf3; conf3.set("pc.weight", "1"); UUID uuid3(3); ProtoUpMeta pum3(uuid3); Proto pc3(conf3, uuid3); DummyTransport tp3; PCUser pu3(conf3, uuid3, &tp3, &pc3); single_boot(&pu3); gu::Config conf2; conf2.set("pc.weight", "1"); UUID uuid2(2); ProtoUpMeta pum2(uuid2); Proto pc2(conf2, uuid2); DummyTransport tp2; PCUser pu2(conf2, uuid2, &tp2, &pc2); double_boot(&pu3, &pu2); gu::Config conf1; conf1.set("pc.weight", "3"); UUID uuid1(1); ProtoUpMeta pum1(uuid1); Proto pc1(conf1, uuid1); DummyTransport tp1; PCUser pu1(conf1, uuid1, &tp1, &pc1); // trans views { View tr1(ViewId(V_TRANS, uuid1, 0)); tr1.add_member(uuid1, ""); pu1.pc()->connect(false); ProtoUpMeta um1(UUID::nil(), ViewId(), &tr1); pu1.pc()->handle_up(0, Datagram(), um1); View tr23(ViewId(V_TRANS, pu2.pc()->current_view().id())); tr23.add_member(uuid2, ""); tr23.add_member(uuid3, ""); ProtoUpMeta um23(UUID::nil(), ViewId(), &tr23); pu2.pc()->handle_up(0, Datagram(), um23); pu3.pc()->handle_up(0, Datagram(), um23); } // reg view { View reg( ViewId(V_REG, uuid1, pu2.pc()->current_view().id().seq() + 1)); reg.add_member(uuid1, ""); reg.add_member(uuid2, ""); reg.add_member(uuid3, ""); ProtoUpMeta um(UUID::nil(), ViewId(), ®); pu1.pc()->handle_up(0, Datagram(), um); pu2.pc()->handle_up(0, Datagram(), um); pu3.pc()->handle_up(0, Datagram(), um); } // states exch { Datagram* dg(pu1.tp()->out()); fail_unless(dg != 0); pu1.pc()->handle_up(0, *dg, ProtoUpMeta(uuid1)); pu2.pc()->handle_up(0, *dg, ProtoUpMeta(uuid1)); pu3.pc()->handle_up(0, *dg, ProtoUpMeta(uuid1)); delete dg; dg = pu2.tp()->out(); fail_unless(dg != 0); pu1.pc()->handle_up(0, *dg, ProtoUpMeta(uuid2)); pu2.pc()->handle_up(0, *dg, ProtoUpMeta(uuid2)); pu3.pc()->handle_up(0, *dg, ProtoUpMeta(uuid2)); delete dg; dg = pu3.tp()->out(); fail_unless(dg != 0); pu1.pc()->handle_up(0, *dg, ProtoUpMeta(uuid3)); pu2.pc()->handle_up(0, *dg, ProtoUpMeta(uuid3)); pu3.pc()->handle_up(0, *dg, ProtoUpMeta(uuid3)); delete dg; fail_unless(pu2.tp()->out() == 0); fail_unless(pu3.tp()->out() == 0); } // install msg { Datagram* dg(pu1.tp()->out()); fail_unless(dg != 0); pu1.pc()->handle_up(0, *dg, ProtoUpMeta(uuid1)); fail_unless(pu1.pc()->state() == Proto::S_PRIM); // trans view for 2 and 3 View tr23(ViewId(V_TRANS, pu2.pc()->current_view().id())); tr23.add_member(uuid2, ""); tr23.add_member(uuid3, ""); tr23.add_partitioned(uuid1, ""); ProtoUpMeta trum23(UUID::nil(), ViewId(), &tr23); pu2.pc()->handle_up(0, Datagram(), trum23); pu3.pc()->handle_up(0, Datagram(), trum23); // 2 and 3 handle install pu2.pc()->handle_up(0, *dg, ProtoUpMeta(uuid1)); pu3.pc()->handle_up(0, *dg, ProtoUpMeta(uuid1)); delete dg; // reg view for 2 and 3 View reg23(ViewId(V_REG, uuid2, pu2.pc()->current_view().id().seq() + 1)); reg23.add_member(uuid2, ""); reg23.add_member(uuid3, ""); ProtoUpMeta rum23(UUID::nil(), ViewId(), ®23); pu2.pc()->handle_up(0, Datagram(), rum23); pu3.pc()->handle_up(0, Datagram(), rum23); // states exch dg = pu2.tp()->out(); fail_unless(dg != 0); pu2.pc()->handle_up(0, *dg, ProtoUpMeta(uuid2)); pu3.pc()->handle_up(0, *dg, ProtoUpMeta(uuid2)); delete dg; dg = pu3.tp()->out(); fail_unless(dg != 0); pu2.pc()->handle_up(0, *dg, ProtoUpMeta(uuid3)); pu3.pc()->handle_up(0, *dg, ProtoUpMeta(uuid3)); delete dg; // 2 and 3 should end up in non prim fail_unless(pu2.pc()->state() == Proto::S_NON_PRIM, "state: %s", Proto::to_string(pu2.pc()->state()).c_str()); fail_unless(pu3.pc()->state() == Proto::S_NON_PRIM, "state: %s", Proto::to_string(pu3.pc()->state()).c_str()); } } END_TEST // // - Two nodes 2 and 3 started with weights 1 // - Third node 1 with weight 3 is brought in the cluster // (becomes representative) // - Partitioning to (1) and (2, 3) happens so that INSTALL message is // delivered in trans view on all nodes // - All nodes should end up in non-prim, nodes 2 and 3 because they don't know // if node 1 ended up in prim (see test_weighted_partitioning_1 above), // node 1 because it hasn't been in primary before and fails to deliver // install message in reg view // START_TEST(test_weighted_partitioning_2) { log_info << "START (test_weighted_partitioning_2)"; gu::Config conf3; conf3.set("pc.weight", "1"); UUID uuid3(3); ProtoUpMeta pum3(uuid3); Proto pc3(conf3, uuid3); DummyTransport tp3; PCUser pu3(conf3, uuid3, &tp3, &pc3); single_boot(&pu3); gu::Config conf2; conf2.set("pc.weight", "1"); UUID uuid2(2); ProtoUpMeta pum2(uuid2); Proto pc2(conf2, uuid2); DummyTransport tp2; PCUser pu2(conf2, uuid2, &tp2, &pc2); double_boot(&pu3, &pu2); gu::Config conf1; conf1.set("pc.weight", "3"); UUID uuid1(1); ProtoUpMeta pum1(uuid1); Proto pc1(conf1, uuid1); DummyTransport tp1; PCUser pu1(conf1, uuid1, &tp1, &pc1); // trans views { View tr1(ViewId(V_TRANS, uuid1, 0)); tr1.add_member(uuid1, ""); pu1.pc()->connect(false); ProtoUpMeta um1(UUID::nil(), ViewId(), &tr1); pu1.pc()->handle_up(0, Datagram(), um1); View tr23(ViewId(V_TRANS, pu2.pc()->current_view().id())); tr23.add_member(uuid2, ""); tr23.add_member(uuid3, ""); ProtoUpMeta um23(UUID::nil(), ViewId(), &tr23); pu2.pc()->handle_up(0, Datagram(), um23); pu3.pc()->handle_up(0, Datagram(), um23); } // reg view { View reg( ViewId(V_REG, uuid1, pu2.pc()->current_view().id().seq() + 1)); reg.add_member(uuid1, ""); reg.add_member(uuid2, ""); reg.add_member(uuid3, ""); ProtoUpMeta um(UUID::nil(), ViewId(), ®); pu1.pc()->handle_up(0, Datagram(), um); pu2.pc()->handle_up(0, Datagram(), um); pu3.pc()->handle_up(0, Datagram(), um); } // states exch { Datagram* dg(pu1.tp()->out()); fail_unless(dg != 0); pu1.pc()->handle_up(0, *dg, ProtoUpMeta(uuid1)); pu2.pc()->handle_up(0, *dg, ProtoUpMeta(uuid1)); pu3.pc()->handle_up(0, *dg, ProtoUpMeta(uuid1)); delete dg; dg = pu2.tp()->out(); fail_unless(dg != 0); pu1.pc()->handle_up(0, *dg, ProtoUpMeta(uuid2)); pu2.pc()->handle_up(0, *dg, ProtoUpMeta(uuid2)); pu3.pc()->handle_up(0, *dg, ProtoUpMeta(uuid2)); delete dg; dg = pu3.tp()->out(); fail_unless(dg != 0); pu1.pc()->handle_up(0, *dg, ProtoUpMeta(uuid3)); pu2.pc()->handle_up(0, *dg, ProtoUpMeta(uuid3)); pu3.pc()->handle_up(0, *dg, ProtoUpMeta(uuid3)); delete dg; fail_unless(pu2.tp()->out() == 0); fail_unless(pu3.tp()->out() == 0); } // install msg { Datagram* dg(pu1.tp()->out()); fail_unless(dg != 0); // trans view for 1 View tr1(ViewId(V_TRANS, pu1.pc()->current_view().id())); tr1.add_member(uuid1, ""); tr1.add_partitioned(uuid2, ""); tr1.add_partitioned(uuid3, ""); ProtoUpMeta trum1(UUID::nil(), ViewId(), &tr1); pu1.pc()->handle_up(0, Datagram(), trum1); fail_unless(pu1.pc()->state() == Proto::S_TRANS); // 1 handle install pu1.pc()->handle_up(0, *dg, ProtoUpMeta(uuid1)); fail_unless(pu1.pc()->state() == Proto::S_TRANS); // trans view for 2 and 3 View tr23(ViewId(V_TRANS, pu2.pc()->current_view().id())); tr23.add_member(uuid2, ""); tr23.add_member(uuid3, ""); tr23.add_partitioned(uuid1, ""); ProtoUpMeta trum23(UUID::nil(), ViewId(), &tr23); pu2.pc()->handle_up(0, Datagram(), trum23); pu3.pc()->handle_up(0, Datagram(), trum23); fail_unless(pu2.pc()->state() == Proto::S_TRANS); fail_unless(pu3.pc()->state() == Proto::S_TRANS); // 2 and 3 handle install pu2.pc()->handle_up(0, *dg, ProtoUpMeta(uuid1)); pu3.pc()->handle_up(0, *dg, ProtoUpMeta(uuid1)); fail_unless(pu2.pc()->state() == Proto::S_TRANS); fail_unless(pu3.pc()->state() == Proto::S_TRANS); delete dg; // reg view for 1 View reg1(ViewId(V_REG, uuid1, pu1.pc()->current_view().id().seq() + 1)); reg1.add_member(uuid1, ""); ProtoUpMeta rum1(UUID::nil(), ViewId(), ®1); pu1.pc()->handle_up(0, Datagram(), rum1); fail_unless(pu1.pc()->state() == Proto::S_STATES_EXCH); // reg view for 2 and 3 View reg23(ViewId(V_REG, uuid2, pu2.pc()->current_view().id().seq() + 1)); reg23.add_member(uuid2, ""); reg23.add_member(uuid3, ""); ProtoUpMeta rum23(UUID::nil(), ViewId(), ®23); pu2.pc()->handle_up(0, Datagram(), rum23); pu3.pc()->handle_up(0, Datagram(), rum23); fail_unless(pu2.pc()->state() == Proto::S_STATES_EXCH); fail_unless(pu3.pc()->state() == Proto::S_STATES_EXCH); // states exch dg = pu1.tp()->out(); fail_unless(dg != 0); pu1.pc()->handle_up(0, *dg, ProtoUpMeta(uuid1)); fail_unless(pu1.pc()->state() == Proto::S_NON_PRIM, "state: %s", Proto::to_string(pu1.pc()->state()).c_str()); delete dg; dg = pu2.tp()->out(); fail_unless(dg != 0); pu2.pc()->handle_up(0, *dg, ProtoUpMeta(uuid2)); pu3.pc()->handle_up(0, *dg, ProtoUpMeta(uuid2)); delete dg; dg = pu3.tp()->out(); fail_unless(dg != 0); pu2.pc()->handle_up(0, *dg, ProtoUpMeta(uuid3)); pu3.pc()->handle_up(0, *dg, ProtoUpMeta(uuid3)); delete dg; fail_unless(pu2.pc()->state() == Proto::S_NON_PRIM, "state: %s", Proto::to_string(pu2.pc()->state()).c_str()); fail_unless(pu3.pc()->state() == Proto::S_NON_PRIM, "state: %s", Proto::to_string(pu3.pc()->state()).c_str()); } } END_TEST // // - Nodes 1-3 started with equal weights // - Weight for node 1 is changed to 3 // - Group splits to (1), (2, 3) // - Weigh changing message is delivered in reg view in (1) and in // trans in (2, 3) // - Expected outcome: 1 stays in prim, 2 and 3 end up in non-prim // START_TEST(test_weight_change_partitioning_1) { log_info << "START (test_weight_change_partitioning_1)"; gu::Config conf1; conf1.set("pc.weight", "1"); UUID uuid1(1); ProtoUpMeta pum1(uuid1); Proto pc1(conf1, uuid1); DummyTransport tp1; PCUser pu1(conf1, uuid1, &tp1, &pc1); single_boot(&pu1); gu::Config conf2; conf2.set("pc.weight", "1"); UUID uuid2(2); ProtoUpMeta pum2(uuid2); Proto pc2(conf2, uuid2); DummyTransport tp2; PCUser pu2(conf2, uuid2, &tp2, &pc2); double_boot(&pu1, &pu2); gu::Config conf3; conf3.set("pc.weight", "1"); UUID uuid3(3); ProtoUpMeta pum3(uuid3); Proto pc3(conf3, uuid3); DummyTransport tp3; PCUser pu3(conf3, uuid3, &tp3, &pc3); triple_boot(&pu1, &pu2, &pu3); // weight change { pu1.pc()->set_param("pc.weight", "3"); Datagram* install_dg(pu1.tp()->out()); fail_unless(install_dg != 0); // node 1 handle weight change install, proceed to singleton prim pu1.pc()->handle_up(0, *install_dg, ProtoUpMeta(pu1.uuid())); View tr1(ViewId(V_TRANS, pu1.pc()->current_view().id())); tr1.add_member(pu1.uuid(), ""); tr1.add_partitioned(pu2.uuid(), ""); tr1.add_partitioned(pu3.uuid(), ""); pu1.pc()->handle_up(0, Datagram(), ProtoUpMeta(UUID::nil(), ViewId(), &tr1)); fail_unless(pu1.pc()->state() == Proto::S_TRANS); View reg1(ViewId(V_REG, pu1.uuid(), pu1.pc()->current_view().id().seq() + 1)); reg1.add_member(pu1.uuid(), ""); pu1.pc()->handle_up(0, Datagram(), ProtoUpMeta(UUID::nil(), ViewId(), ®1)); fail_unless(pu1.pc()->state() == Proto::S_STATES_EXCH); Datagram* dg(pu1.tp()->out()); fail_unless(dg != 0); pu1.pc()->handle_up(0, *dg, ProtoUpMeta(pu1.uuid())); delete dg; fail_unless(pu1.pc()->state() == Proto::S_INSTALL); dg = pu1.tp()->out(); fail_unless(dg != 0); pu1.pc()->handle_up(0, *dg, ProtoUpMeta(pu1.uuid())); delete dg; fail_unless(pu1.pc()->state() == Proto::S_PRIM); // nodes 2 and 3 go to trans, handle install View tr23(ViewId(V_TRANS, pu2.pc()->current_view().id())); tr23.add_member(pu2.uuid(), ""); tr23.add_member(pu3.uuid(), ""); tr23.add_partitioned(pu1.uuid(), ""); pu2.pc()->handle_up(0, Datagram(), ProtoUpMeta(UUID::nil(), ViewId(), &tr23)); pu3.pc()->handle_up(0, Datagram(), ProtoUpMeta(UUID::nil(), ViewId(), &tr23)); fail_unless(pu2.pc()->state() == Proto::S_TRANS); fail_unless(pu3.pc()->state() == Proto::S_TRANS); pu2.pc()->handle_up(0, *install_dg, ProtoUpMeta(pu1.uuid())); pu3.pc()->handle_up(0, *install_dg, ProtoUpMeta(pu1.uuid())); View reg23(ViewId(V_REG, pu2.uuid(), pu2.pc()->current_view().id().seq() + 1)); reg23.add_member(pu2.uuid()); reg23.add_member(pu3.uuid()); pu2.pc()->handle_up(0, Datagram(), ProtoUpMeta(UUID::nil(), ViewId(), ®23)); pu3.pc()->handle_up(0, Datagram(), ProtoUpMeta(UUID::nil(), ViewId(), ®23)); fail_unless(pu2.pc()->state() == Proto::S_STATES_EXCH); fail_unless(pu3.pc()->state() == Proto::S_STATES_EXCH); dg = pu2.tp()->out(); fail_unless(dg != 0); pu2.pc()->handle_up(0, *dg, ProtoUpMeta(pu2.uuid())); pu3.pc()->handle_up(0, *dg, ProtoUpMeta(pu2.uuid())); delete dg; dg = pu3.tp()->out(); fail_unless(dg != 0); pu2.pc()->handle_up(0, *dg, ProtoUpMeta(pu3.uuid())); pu3.pc()->handle_up(0, *dg, ProtoUpMeta(pu3.uuid())); delete dg; fail_unless(pu2.pc()->state() == Proto::S_NON_PRIM); fail_unless(pu3.pc()->state() == Proto::S_NON_PRIM); delete install_dg; } } END_TEST // // - Nodes 2 and 3 start with weight 1, node 1 with weight 3 // - Weight for node 1 is changed to 1 // - Group splits to (1), (2, 3) // - Weigh changing message is delivered in reg view in (1) and in // trans in (2, 3) // - Expected outcome: all nodes go non-prim // START_TEST(test_weight_change_partitioning_2) { log_info << "START (test_weight_change_partitioning_2)"; gu::Config conf1; conf1.set("pc.weight", "3"); UUID uuid1(1); ProtoUpMeta pum1(uuid1); Proto pc1(conf1, uuid1); DummyTransport tp1; PCUser pu1(conf1, uuid1, &tp1, &pc1); single_boot(&pu1); gu::Config conf2; conf2.set("pc.weight", "1"); UUID uuid2(2); ProtoUpMeta pum2(uuid2); Proto pc2(conf2, uuid2); DummyTransport tp2; PCUser pu2(conf2, uuid2, &tp2, &pc2); double_boot(&pu1, &pu2); gu::Config conf3; conf3.set("pc.weight", "1"); UUID uuid3(3); ProtoUpMeta pum3(uuid3); Proto pc3(conf3, uuid3); DummyTransport tp3; PCUser pu3(conf3, uuid3, &tp3, &pc3); triple_boot(&pu1, &pu2, &pu3); // weight change { pu1.pc()->set_param("pc.weight", "1"); Datagram* install_dg(pu1.tp()->out()); fail_unless(install_dg != 0); // node 1 handle weight change install, proceed to singleton prim pu1.pc()->handle_up(0, *install_dg, ProtoUpMeta(pu1.uuid())); View tr1(ViewId(V_TRANS, pu1.pc()->current_view().id())); tr1.add_member(pu1.uuid(), ""); tr1.add_partitioned(pu2.uuid(), ""); tr1.add_partitioned(pu3.uuid(), ""); pu1.pc()->handle_up(0, Datagram(), ProtoUpMeta(UUID::nil(), ViewId(), &tr1)); fail_unless(pu1.pc()->state() == Proto::S_TRANS); View reg1(ViewId(V_REG, pu1.uuid(), pu1.pc()->current_view().id().seq() + 1)); reg1.add_member(pu1.uuid(), ""); pu1.pc()->handle_up(0, Datagram(), ProtoUpMeta(UUID::nil(), ViewId(), ®1)); fail_unless(pu1.pc()->state() == Proto::S_STATES_EXCH); Datagram* dg(pu1.tp()->out()); fail_unless(dg != 0); pu1.pc()->handle_up(0, *dg, ProtoUpMeta(pu1.uuid())); delete dg; fail_unless(pu1.pc()->state() == Proto::S_NON_PRIM); // nodes 2 and 3 go to trans, handle install View tr23(ViewId(V_TRANS, pu2.pc()->current_view().id())); tr23.add_member(pu2.uuid(), ""); tr23.add_member(pu3.uuid(), ""); tr23.add_partitioned(pu1.uuid(), ""); pu2.pc()->handle_up(0, Datagram(), ProtoUpMeta(UUID::nil(), ViewId(), &tr23)); pu3.pc()->handle_up(0, Datagram(), ProtoUpMeta(UUID::nil(), ViewId(), &tr23)); fail_unless(pu2.pc()->state() == Proto::S_TRANS); fail_unless(pu3.pc()->state() == Proto::S_TRANS); pu2.pc()->handle_up(0, *install_dg, ProtoUpMeta(pu1.uuid())); pu3.pc()->handle_up(0, *install_dg, ProtoUpMeta(pu1.uuid())); View reg23(ViewId(V_REG, pu2.uuid(), pu2.pc()->current_view().id().seq() + 1)); reg23.add_member(pu2.uuid()); reg23.add_member(pu3.uuid()); pu2.pc()->handle_up(0, Datagram(), ProtoUpMeta(UUID::nil(), ViewId(), ®23)); pu3.pc()->handle_up(0, Datagram(), ProtoUpMeta(UUID::nil(), ViewId(), ®23)); fail_unless(pu2.pc()->state() == Proto::S_STATES_EXCH); fail_unless(pu3.pc()->state() == Proto::S_STATES_EXCH); dg = pu2.tp()->out(); fail_unless(dg != 0); pu2.pc()->handle_up(0, *dg, ProtoUpMeta(pu2.uuid())); pu3.pc()->handle_up(0, *dg, ProtoUpMeta(pu2.uuid())); delete dg; dg = pu3.tp()->out(); fail_unless(dg != 0); pu2.pc()->handle_up(0, *dg, ProtoUpMeta(pu3.uuid())); pu3.pc()->handle_up(0, *dg, ProtoUpMeta(pu3.uuid())); delete dg; fail_unless(pu2.pc()->state() == Proto::S_NON_PRIM); fail_unless(pu3.pc()->state() == Proto::S_NON_PRIM); delete install_dg; } } END_TEST // // Weight changing message is delivered in transitional view when new node is // joining. All nodes should end up in prim. // START_TEST(test_weight_change_joining) { log_info << "START (test_weight_change_joining)"; gu::Config conf1; conf1.set("pc.weight", "1"); UUID uuid1(1); ProtoUpMeta pum1(uuid1); Proto pc1(conf1, uuid1); DummyTransport tp1; PCUser pu1(conf1, uuid1, &tp1, &pc1); single_boot(&pu1); gu::Config conf2; conf2.set("pc.weight", "1"); UUID uuid2(2); ProtoUpMeta pum2(uuid2); Proto pc2(conf2, uuid2); DummyTransport tp2; PCUser pu2(conf2, uuid2, &tp2, &pc2); double_boot(&pu1, &pu2); gu::Config conf3; conf3.set("pc.weight", "1"); UUID uuid3(3); ProtoUpMeta pum3(uuid3); Proto pc3(conf3, uuid3); DummyTransport tp3; PCUser pu3(conf3, uuid3, &tp3, &pc3); // weight change { pu1.pc()->set_param("pc.weight", "1"); Datagram* install_dg(pu1.tp()->out()); fail_unless(install_dg != 0); // trans views { View tr12(ViewId(V_TRANS, pu1.pc()->current_view().id())); tr12.add_member(pu1.uuid(), ""); tr12.add_member(pu2.uuid(), ""); ProtoUpMeta trum12(UUID::nil(), ViewId(), &tr12); pu1.pc()->handle_up(0, Datagram(), trum12); pu2.pc()->handle_up(0, Datagram(), trum12); fail_unless(pu1.pc()->state() == Proto::S_TRANS); fail_unless(pu2.pc()->state() == Proto::S_TRANS); // deliver weight change install in trans view pu1.pc()->handle_up(0, *install_dg, ProtoUpMeta(pu1.uuid())); pu2.pc()->handle_up(0, *install_dg, ProtoUpMeta(pu1.uuid())); pu3.pc()->connect(false); View tr3(ViewId(V_TRANS, pu3.uuid(), 0)); tr3.add_member(pu3.uuid(), ""); ProtoUpMeta trum3(UUID::nil(), ViewId(), &tr3); pu3.pc()->handle_up(0, Datagram(), trum3); fail_unless(pu3.pc()->state() == Proto::S_TRANS); } // reg view { View reg( ViewId(V_REG, pu1.uuid(), pu1.pc()->current_view().id().seq() + 1)); reg.add_member(pu1.uuid(), ""); reg.add_member(pu2.uuid(), ""); reg.add_member(pu3.uuid(), ""); ProtoUpMeta pum(UUID::nil(), ViewId(), ®); pu1.pc()->handle_up(0, Datagram(), pum); pu2.pc()->handle_up(0, Datagram(), pum); pu3.pc()->handle_up(0, Datagram(), pum); fail_unless(pu1.pc()->state() == Proto::S_STATES_EXCH); fail_unless(pu2.pc()->state() == Proto::S_STATES_EXCH); fail_unless(pu3.pc()->state() == Proto::S_STATES_EXCH); } // states exch { Datagram* dg(pu1.tp()->out()); fail_unless(dg != 0); pu1.pc()->handle_up(0, *dg, ProtoUpMeta(pu1.uuid())); pu2.pc()->handle_up(0, *dg, ProtoUpMeta(pu1.uuid())); pu3.pc()->handle_up(0, *dg, ProtoUpMeta(pu1.uuid())); delete dg; dg = pu2.tp()->out(); fail_unless(dg != 0); pu1.pc()->handle_up(0, *dg, ProtoUpMeta(pu2.uuid())); pu2.pc()->handle_up(0, *dg, ProtoUpMeta(pu2.uuid())); pu3.pc()->handle_up(0, *dg, ProtoUpMeta(pu2.uuid())); delete dg; dg = pu3.tp()->out(); fail_unless(dg != 0); pu1.pc()->handle_up(0, *dg, ProtoUpMeta(pu3.uuid())); pu2.pc()->handle_up(0, *dg, ProtoUpMeta(pu3.uuid())); pu3.pc()->handle_up(0, *dg, ProtoUpMeta(pu3.uuid())); delete dg; fail_unless(pu1.pc()->state() == Proto::S_INSTALL); fail_unless(pu2.pc()->state() == Proto::S_INSTALL); fail_unless(pu3.pc()->state() == Proto::S_INSTALL); } // install { Datagram* dg(pu1.tp()->out()); fail_unless(dg != 0); pu1.pc()->handle_up(0, *dg, ProtoUpMeta(pu1.uuid())); pu2.pc()->handle_up(0, *dg, ProtoUpMeta(pu1.uuid())); pu3.pc()->handle_up(0, *dg, ProtoUpMeta(pu1.uuid())); delete dg; fail_unless(pu1.pc()->state() == Proto::S_PRIM); fail_unless(pu2.pc()->state() == Proto::S_PRIM); fail_unless(pu3.pc()->state() == Proto::S_PRIM); } delete install_dg; } } END_TEST // // One of the nodes leaves gracefully from group and weight change message // is delivered in trans view. Remaining nodes must not enter non-prim. // START_TEST(test_weight_change_leaving) { log_info << "START (test_weight_change_leaving)"; gu::Config conf1; conf1.set("pc.weight", "3"); UUID uuid1(1); ProtoUpMeta pum1(uuid1); Proto pc1(conf1, uuid1); DummyTransport tp1; PCUser pu1(conf1, uuid1, &tp1, &pc1); single_boot(&pu1); gu::Config conf2; conf2.set("pc.weight", "2"); UUID uuid2(2); ProtoUpMeta pum2(uuid2); Proto pc2(conf2, uuid2); DummyTransport tp2; PCUser pu2(conf2, uuid2, &tp2, &pc2); double_boot(&pu1, &pu2); gu::Config conf3; conf3.set("pc.weight", "1"); UUID uuid3(3); ProtoUpMeta pum3(uuid3); Proto pc3(conf3, uuid3); DummyTransport tp3; PCUser pu3(conf3, uuid3, &tp3, &pc3); triple_boot(&pu1, &pu2, &pu3); // weight change { // change weight for node 2 while node 1 leaves the group gracefully pu2.pc()->set_param("pc.weight", "1"); Datagram* install_dg(pu2.tp()->out()); fail_unless(install_dg != 0); // nodes 2 and 3 go to trans, handle install View tr23(ViewId(V_TRANS, pu2.pc()->current_view().id())); tr23.add_member(pu2.uuid(), ""); tr23.add_member(pu3.uuid(), ""); tr23.add_left(pu1.uuid(), ""); pu2.pc()->handle_up(0, Datagram(), ProtoUpMeta(UUID::nil(), ViewId(), &tr23)); pu3.pc()->handle_up(0, Datagram(), ProtoUpMeta(UUID::nil(), ViewId(), &tr23)); fail_unless(pu2.pc()->state() == Proto::S_TRANS); fail_unless(pu3.pc()->state() == Proto::S_TRANS); pu2.pc()->handle_up(0, *install_dg, ProtoUpMeta(pu1.uuid())); pu3.pc()->handle_up(0, *install_dg, ProtoUpMeta(pu1.uuid())); View reg23(ViewId(V_REG, pu2.uuid(), pu2.pc()->current_view().id().seq() + 1)); reg23.add_member(pu2.uuid()); reg23.add_member(pu3.uuid()); pu2.pc()->handle_up(0, Datagram(), ProtoUpMeta(UUID::nil(), ViewId(), ®23)); pu3.pc()->handle_up(0, Datagram(), ProtoUpMeta(UUID::nil(), ViewId(), ®23)); fail_unless(pu2.pc()->state() == Proto::S_STATES_EXCH); fail_unless(pu3.pc()->state() == Proto::S_STATES_EXCH); Datagram* dg(pu2.tp()->out()); fail_unless(dg != 0); pu2.pc()->handle_up(0, *dg, ProtoUpMeta(pu2.uuid())); pu3.pc()->handle_up(0, *dg, ProtoUpMeta(pu2.uuid())); delete dg; dg = pu3.tp()->out(); fail_unless(dg != 0); pu2.pc()->handle_up(0, *dg, ProtoUpMeta(pu3.uuid())); pu3.pc()->handle_up(0, *dg, ProtoUpMeta(pu3.uuid())); delete dg; fail_unless(pu2.pc()->state() == Proto::S_INSTALL); fail_unless(pu3.pc()->state() == Proto::S_INSTALL); dg = pu2.tp()->out(); fail_unless(dg != 0); pu2.pc()->handle_up(0, *dg, ProtoUpMeta(pu3.uuid())); pu3.pc()->handle_up(0, *dg, ProtoUpMeta(pu3.uuid())); delete dg; fail_unless(pu2.pc()->state() == Proto::S_PRIM); fail_unless(pu3.pc()->state() == Proto::S_PRIM); delete install_dg; } } END_TEST Suite* pc_suite() { Suite* s = suite_create("gcomm::pc"); TCase* tc; tc = tcase_create("test_pc_messages"); tcase_add_test(tc, test_pc_messages); suite_add_tcase(s, tc); tc = tcase_create("test_pc_view_changes_single"); tcase_add_test(tc, test_pc_view_changes_single); suite_add_tcase(s, tc); tc = tcase_create("test_pc_view_changes_double"); tcase_add_test(tc, test_pc_view_changes_double); suite_add_tcase(s, tc); tc = tcase_create("test_pc_view_changes_reverse"); tcase_add_test(tc, test_pc_view_changes_reverse); suite_add_tcase(s, tc); tc = tcase_create("test_pc_state1"); tcase_add_test(tc, test_pc_state1); suite_add_tcase(s, tc); tc = tcase_create("test_pc_state2"); tcase_add_test(tc, test_pc_state2); suite_add_tcase(s, tc); tc = tcase_create("test_pc_state3"); tcase_add_test(tc, test_pc_state3); suite_add_tcase(s, tc); tc = tcase_create("test_pc_conflicting_prims"); tcase_add_test(tc, test_pc_conflicting_prims); suite_add_tcase(s, tc); tc = tcase_create("test_pc_conflicting_prims_npvo"); tcase_add_test(tc, test_pc_conflicting_prims_npvo); suite_add_tcase(s, tc); tc = tcase_create("test_pc_split_merge"); tcase_add_test(tc, test_pc_split_merge); tcase_set_timeout(tc, 15); suite_add_tcase(s, tc); tc = tcase_create("test_pc_split_merge_w_user_msg"); tcase_add_test(tc, test_pc_split_merge_w_user_msg); tcase_set_timeout(tc, 15); suite_add_tcase(s, tc); tc = tcase_create("test_pc_complete_split_merge"); tcase_add_test(tc, test_pc_complete_split_merge); tcase_set_timeout(tc, 25); suite_add_tcase(s, tc); tc = tcase_create("test_pc_transport"); tcase_add_test(tc, test_pc_transport); tcase_set_timeout(tc, 35); suite_add_tcase(s, tc); tc = tcase_create("test_trac_191"); tcase_add_test(tc, test_trac_191); suite_add_tcase(s, tc); tc = tcase_create("test_trac_413"); tcase_add_test(tc, test_trac_413); suite_add_tcase(s, tc); tc = tcase_create("test_fifo_violation"); tcase_add_test(tc, test_fifo_violation); suite_add_tcase(s, tc); tc = tcase_create("test_checksum"); tcase_add_test(tc, test_checksum); suite_add_tcase(s, tc); tc = tcase_create("test_set_param"); tcase_add_test(tc, test_set_param); suite_add_tcase(s, tc); tc = tcase_create("test_trac_599"); tcase_add_test(tc, test_trac_599); suite_add_tcase(s, tc); tc = tcase_create("test_trac_620"); tcase_add_test(tc, test_trac_620); suite_add_tcase(s, tc); tc = tcase_create("test_trac_277"); tcase_add_test(tc, test_trac_277); suite_add_tcase(s, tc); tc = tcase_create("test_trac_622_638"); tcase_add_test(tc, test_trac_622_638); suite_add_tcase(s, tc); tc = tcase_create("test_weighted_quorum"); tcase_add_test(tc, test_weighted_quorum); suite_add_tcase(s, tc); tc = tcase_create("test_weighted_partitioning_1"); tcase_add_test(tc, test_weighted_partitioning_1); suite_add_tcase(s, tc); tc = tcase_create("test_weighted_partitioning_2"); tcase_add_test(tc, test_weighted_partitioning_2); suite_add_tcase(s, tc); tc = tcase_create("test_weight_change_partitioning_1"); tcase_add_test(tc, test_weight_change_partitioning_1); suite_add_tcase(s, tc); tc = tcase_create("test_weight_change_partitioning_2"); tcase_add_test(tc, test_weight_change_partitioning_2); suite_add_tcase(s, tc); tc = tcase_create("test_weight_change_joining"); tcase_add_test(tc, test_weight_change_joining); suite_add_tcase(s, tc); tc = tcase_create("test_weight_change_leaving"); tcase_add_test(tc, test_weight_change_leaving); suite_add_tcase(s, tc); return s; } percona-xtradb-cluster-galera/gcomm/test/check_templ.hpp0000644000000000000000000000343012247075736023722 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #ifndef GCOMM_CHECK_TEMPL_HPP #define GCOMM_CHECK_TEMPL_HPP #include "gcomm/types.hpp" #include "gcomm/transport.hpp" #include #include #include namespace gcomm { template void check_serialization(const T& c, const size_t expected_size, const T& default_c) { fail_unless(c.serial_size() == expected_size, "size = %lu expected = %lu", c.serial_size(), expected_size); gu::byte_t* buf = new gu::byte_t[expected_size + 7]; size_t ret; // Check that what is written gets also read try { (void)c.serialize(buf, expected_size, 1); fail("exception not thrown"); } catch (gu::Exception& e) { // OK } fail_unless(c.serialize(buf, expected_size, 0) == expected_size); T c2(default_c); try { (void)c2.unserialize(buf, expected_size, 1); fail("exception not thrown"); } catch (gu::Exception& e) { // OK } ret = c2.unserialize(buf, expected_size, 0); fail_unless(ret == expected_size, "expected %z ret %z", expected_size, ret); if ((c == c2) == false) { log_warn << "\n\t" << c << " !=\n\t" << c2; } fail_unless(c == c2); // Check that read/write return offset properly fail_unless(c.serialize(buf, expected_size + 7, 5) == expected_size + 5); fail_unless(c2.unserialize(buf, expected_size + 7, 5) == expected_size + 5); fail_unless(c == c2); delete[] buf; } } // namespace gcomm #endif // CHECK_TEMPL_HPP percona-xtradb-cluster-galera/gcomm/test/check_trace.cpp0000644000000000000000000002043312247075736023674 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy * * $Id$ */ /*! * @brief Check trace implementation */ #include "check_trace.hpp" using namespace std; using namespace gu; using namespace gcomm; gu::Config check_trace_conf; ostream& gcomm::operator<<(ostream& os, const TraceMsg& msg) { return (os << "(" << msg.source() << "," << msg.source_view_id() << "," << msg.seq() << ")"); } ostream& gcomm::operator<<(ostream& os, const ViewTrace& vtr) { os << vtr.view() << ": "; copy(vtr.msgs().begin(), vtr.msgs().end(), ostream_iterator(os, " ")); return os; } ostream& gcomm::operator<<(ostream& os, const Trace& tr) { os << "trace: \n"; os << tr.view_traces(); return os; } ostream& gcomm::operator<<(ostream& os, const Channel& ch) { return (os << "(" << ch.latency() << "," << ch.loss() << ")"); } ostream& gcomm::operator<<(ostream& os, const Channel* chp) { return (os << *chp); } ostream& gcomm::operator<<(ostream& os, const MatrixElem& me) { return (os << "(" << me.ii() << "," << me.jj() << ")"); } ostream& gcomm::operator<<(ostream& os, const PropagationMatrix& prop) { os << "("; copy(prop.prop_.begin(), prop.prop_.end(), ostream_iterator(os, ",")); os << ")"; return os; } class LinkOp { public: LinkOp(DummyNode& node, ChannelMap& prop) : node_(node), prop_(prop) { } void operator()(NodeMap::value_type& l) { if (NodeMap::key(l) != node_.index()) { ChannelMap::iterator ii; gu_trace(ii = prop_.insert_unique( make_pair(MatrixElem(node_.index(), NodeMap::key(l)), new Channel(check_trace_conf)))); gcomm::connect(ChannelMap::value(ii), node_.protos().front()); gu_trace(ii = prop_.insert_unique( make_pair(MatrixElem(NodeMap::key(l), node_.index()), new Channel(check_trace_conf)))); gcomm::connect(ChannelMap::value(ii), NodeMap::value(l)->protos().front()); } } private: DummyNode& node_; ChannelMap& prop_; }; class PropagateOp { public: PropagateOp(NodeMap& tp) : tp_(tp) { } void operator()(ChannelMap::value_type& vt) { ChannelMsg cmsg(vt.second->get()); if (cmsg.rb().len() != 0) { NodeMap::iterator i(tp_.find(vt.first.jj())); gcomm_assert(i != tp_.end()); gu_trace(NodeMap::value(i)->protos().front()->handle_up( &tp_, cmsg.rb(), ProtoUpMeta(cmsg.source()))); } } private: NodeMap& tp_; }; class ExpireTimersOp { public: ExpireTimersOp() { } void operator()(NodeMap::value_type& vt) { NodeMap::value(vt)->handle_timers(); } }; void gcomm::Channel::put(const Datagram& rb, const UUID& source) { Datagram dg(rb); // if (dg.is_normalized() == false) // { // dg.normalize(); // } queue_.push_back(make_pair(latency_, ChannelMsg(dg, source))); } ChannelMsg gcomm::Channel::get() { while (queue_.empty() == false) { pair& p(queue_.front()); if (p.first == 0) { // todo: packet loss goes here if (loss() < 1.) { double rnd(double(rand())/double(RAND_MAX)); if (loss() < rnd) { queue_.pop_front(); return ChannelMsg(Datagram(), UUID::nil()); } } ChannelMsg ret(p.second); queue_.pop_front(); return ret; } else { --p.first; return ChannelMsg(Datagram(), UUID::nil()); } } return ChannelMsg(Datagram(), UUID::nil()); } gcomm::PropagationMatrix::~PropagationMatrix() { for_each(prop_.begin(), prop_.end(), ChannelMap::DeleteObject()); } void gcomm::PropagationMatrix::insert_tp(DummyNode* t) { gu_trace(tp_.insert_unique(make_pair(t->index(), t))); for_each(tp_.begin(), tp_.end(), LinkOp(*t, prop_)); } void gcomm::PropagationMatrix::set_latency(const size_t ii, const size_t jj, const size_t lat) { ChannelMap::iterator i; gu_trace(i = prop_.find_checked(MatrixElem(ii, jj))); ChannelMap::value(i)->set_latency(lat); } void gcomm::PropagationMatrix::set_loss(const size_t ii, const size_t jj, const double loss) { ChannelMap::iterator i; gu_trace(i = prop_.find_checked(MatrixElem(ii, jj))); ChannelMap::value(i)->set_loss(loss); } void gcomm::PropagationMatrix::split(const size_t ii, const size_t jj) { set_loss(ii, jj, 0.); set_loss(jj, ii, 0.); } void gcomm::PropagationMatrix::merge(const size_t ii, const size_t jj, const double loss) { set_loss(ii, jj, loss); set_loss(jj, ii, loss); } void gcomm::PropagationMatrix::expire_timers() { for_each(tp_.begin(), tp_.end(), ExpireTimersOp()); } void gcomm::PropagationMatrix::propagate_n(size_t n) { while (n-- > 0) { for_each(prop_.begin(), prop_.end(), PropagateOp(tp_)); } } void gcomm::PropagationMatrix::propagate_until_empty() { do { for_each(prop_.begin(), prop_.end(), PropagateOp(tp_)); } while (count_channel_msgs() > 0); } void gcomm::PropagationMatrix::propagate_until_cvi(bool handle_timers) { bool all_in = false; do { propagate_n(10); all_in = all_in_cvi(); if (all_in == false && handle_timers == true) { expire_timers(); } } while (all_in == false); } size_t gcomm::PropagationMatrix::count_channel_msgs() const { size_t ret = 0; for (ChannelMap::const_iterator i = prop_.begin(); i != prop_.end(); ++i) { ret += ChannelMap::value(i)->n_msgs(); } return ret; } bool gcomm::PropagationMatrix::all_in_cvi() const { for (map::const_iterator i = tp_.begin(); i != tp_.end(); ++i) { if (i->second->in_cvi() == false) { return false; } } return true; } static void check_traces(const Trace& t1, const Trace& t2) { for (Trace::ViewTraceMap::const_iterator i = t1.view_traces().begin(); i != t1.view_traces().end(); ++i) { Trace::ViewTraceMap::const_iterator i_next(i); ++i_next; if (i_next != t1.view_traces().end()) { const Trace::ViewTraceMap::const_iterator j(t2.view_traces().find(Trace::ViewTraceMap::key(i))); Trace::ViewTraceMap::const_iterator j_next(j); ++j_next; // Note: Comparision is meaningful if also next view is the // same. // @todo Proper checks for PRIM and NON_PRIM if (j != t2.view_traces().end() && j_next != t2.view_traces().end() && i_next->first == j_next->first && i_next->second.view().members() == j_next->second.view().members()) { if (i->first.type() != V_NON_PRIM && i->first.type() != V_PRIM) { gcomm_assert(*i == *j) << "traces differ: \n\n" << *i << "\n\n" << *j << "\n\n" << "next views: \n\n" << *i_next << "\n\n" << *j_next; } else { // todo } } } } } class CheckTraceOp { public: CheckTraceOp(const vector& nvec) : nvec_(nvec) { } void operator()(const DummyNode* n) const { for (vector::const_iterator i = nvec_.begin(); i != nvec_.end(); ++i) { if ((*i)->index() != n->index()) { gu_trace(check_traces((*i)->trace(), n->trace())); } } } private: const vector& nvec_; }; void gcomm::check_trace(const vector& nvec) { for_each(nvec.begin(), nvec.end(), CheckTraceOp(nvec)); } percona-xtradb-cluster-galera/gcomm/test/check_trace.hpp0000644000000000000000000003646012247075736023710 0ustar rootroot00000000000000/* * Copyright (C) 2009-2012 Codership Oy * * $Id$ */ /*! * Classes for tracing views and messages */ #include "gu_uri.hpp" #include "gu_datetime.hpp" #include "gcomm/datagram.hpp" #include "gcomm/uuid.hpp" #include "gcomm/protolay.hpp" #include "gcomm/protostack.hpp" #include "gcomm/transport.hpp" #include "gcomm/map.hpp" #include "gcomm/util.hpp" #include #include #include extern gu::Config check_trace_conf; namespace gcomm { class TraceMsg { public: TraceMsg(const UUID& source = UUID::nil(), const ViewId& source_view_id = ViewId(), const int64_t seq = -1) : source_(source), source_view_id_(source_view_id), seq_(seq) { } const UUID& source() const { return source_; } const ViewId& source_view_id() const { return source_view_id_; } int64_t seq() const { return seq_; } bool operator==(const TraceMsg& cmp) const { return (source_ == cmp.source_ && source_view_id_ == cmp.source_view_id_ && seq_ == cmp.seq_ ); } private: UUID source_; ViewId source_view_id_; int64_t seq_; }; std::ostream& operator<<(std::ostream& os, const TraceMsg& msg); class ViewTrace { public: ViewTrace(const View& view) : view_(view), msgs_() { } void insert_msg(const TraceMsg& msg) { switch (view_.type()) { case V_REG: gcomm_assert(view_.id() == msg.source_view_id()); gcomm_assert(contains(msg.source()) == true) << "msg source " << msg.source() << " not int view " << view_; break; case V_TRANS: gcomm_assert(view_.id().uuid() == msg.source_view_id().uuid() && view_.id().seq() == msg.source_view_id().seq()); break; case V_NON_PRIM: break; case V_PRIM: gcomm_assert(view_.id() == msg.source_view_id()) << " view id " << view_.id() << " source view " << msg.source_view_id(); gcomm_assert(contains(msg.source()) == true); break; case V_NONE: gu_throw_fatal; break; } if (view_.type() != V_NON_PRIM) { msgs_.push_back(msg); } } const View& view() const { return view_; } const std::deque& msgs() const { return msgs_; } bool operator==(const ViewTrace& cmp) const { // Note: Cannot compare joining members since seen differently // on different merging subsets return (view_.members() == cmp.view_.members() && view_.left() == cmp.view_.left() && view_.partitioned() == cmp.view_.partitioned() && msgs_ == cmp.msgs_ ); } private: bool contains(const UUID& uuid) const { return (view_.members().find(uuid) != view_.members().end() || view_.left().find(uuid) != view_.left().end() || view_.partitioned().find(uuid) != view_.partitioned().end()); } View view_; std::deque msgs_; }; std::ostream& operator<<(std::ostream& os, const ViewTrace& vtr); class Trace { public: class ViewTraceMap : public Map { }; Trace() : views_(), current_view_(views_.end()) { } void insert_view(const View& view) { gu_trace(current_view_ = views_.insert_unique( std::make_pair(view.id(), ViewTrace(view)))); log_debug << view; } void insert_msg(const TraceMsg& msg) { gcomm_assert(current_view_ != views_.end()) << "no view set before msg delivery"; gu_trace(ViewTraceMap::value(current_view_).insert_msg(msg)); } const ViewTraceMap& view_traces() const { return views_; } const ViewTrace& current_view_trace() const { gcomm_assert(current_view_ != views_.end()); return ViewTraceMap::value(current_view_); } private: ViewTraceMap views_; ViewTraceMap::iterator current_view_; }; std::ostream& operator<<(std::ostream& os, const Trace& tr); class DummyTransport : public Transport { UUID uuid_; std::deque out_; bool queue_; public: DummyTransport(const UUID& uuid = UUID::nil(), bool queue = true, const gu::URI& uri = gu::URI("dummy:")) : Transport(*std::auto_ptr(Protonet::create(check_trace_conf)), uri), uuid_(uuid), out_(), queue_(queue) {} ~DummyTransport() { out_.clear(); } const UUID& uuid() const { return uuid_; } size_t mtu() const { return (1U << 31); } void connect(bool first) { } void close(bool force) { } void close(const UUID&) { } void connect() { } void listen() { gu_throw_fatal << "not implemented"; } Transport *accept() { gu_throw_fatal << "not implemented"; return 0; } void handle_up(const void* cid, const Datagram& rb, const ProtoUpMeta& um) { send_up(rb, um); } int handle_down(Datagram& wb, const ProtoDownMeta& dm) { if (queue_ == true) { // assert(wb.header().size() == 0); out_.push_back(new Datagram(wb)); return 0; } else { gu_trace(return send_down(wb, ProtoDownMeta(0xff, O_UNRELIABLE, uuid_))); } } Datagram* out() { if (out_.empty()) { return 0; } Datagram* rb = out_.front(); out_.pop_front(); return rb; } }; class DummyNode : public Toplay { public: DummyNode(gu::Config& conf, const size_t index, const std::list& protos) : Toplay (conf), index_ (index), uuid_ (UUID(static_cast(index))), protos_ (protos), cvi_ (), tr_ (), curr_seq_(0) { gcomm_assert(protos_.empty() == false); std::list::iterator i, i_next; i = i_next = protos_.begin(); for (++i_next; i_next != protos_.end(); ++i, ++i_next) { gu_trace(gcomm::connect(*i, *i_next)); } gu_trace(gcomm::connect(*i, this)); } ~DummyNode() { std::list::iterator i, i_next; i = i_next = protos_.begin(); for (++i_next; i_next != protos_.end(); ++i, ++i_next) { gu_trace(gcomm::disconnect(*i, *i_next)); } gu_trace(gcomm::disconnect(*i, this)); std::for_each(protos_.begin(), protos_.end(), gu::DeleteObject()); } const UUID& uuid() const { return uuid_; } std::list& protos() { return protos_; } size_t index() const { return index_; } void connect(bool first) { gu_trace(std::for_each(protos_.rbegin(), protos_.rend(), std::bind2nd( std::mem_fun(&Protolay::connect), first))); } void close() { for (std::list::iterator i = protos_.begin(); i != protos_.end(); ++i) { (*i)->close(); } // gu_trace(std::for_each(protos.rbegin(), protos.rend(), // std::mem_fun(&Protolay::close))); } void close(const UUID& uuid) { for (std::list::iterator i = protos_.begin(); i != protos_.end(); ++i) { (*i)->close(uuid); } // gu_trace(std::for_each(protos.rbegin(), protos.rend(), // std::mem_fun(&Protolay::close))); } void send() { const int64_t seq(curr_seq_); gu::byte_t buf[sizeof(seq)]; size_t sz; gu_trace(sz = gu::serialize8(seq, buf, sizeof(buf), 0)); Datagram dg(gu::Buffer(buf, buf + sz)); int err = send_down(dg, ProtoDownMeta(0)); if (err != 0) { log_debug << "failed to send: " << strerror(err); } else { ++curr_seq_; } } const Trace& trace() const { return tr_; } void set_cvi(const ViewId& vi) { log_debug << uuid() << " setting cvi to " << vi; cvi_ = vi; } bool in_cvi() const { for (Trace::ViewTraceMap::const_reverse_iterator i( tr_.view_traces().rbegin()); i != tr_.view_traces().rend(); ++i) { if (i->first.uuid() == cvi_.uuid() && i->first.type() == cvi_.type() && i->first.seq() >= cvi_.seq()) { return true; } } return false; } void handle_up(const void* cid, const Datagram& rb, const ProtoUpMeta& um) { if (rb.len() != 0) { gcomm_assert((um.source() == UUID::nil()) == false); // assert(rb.header().size() == 0); const gu::byte_t* begin(gcomm::begin(rb)); const size_t available(gcomm::available(rb)); // log_debug << um.source() << " " << uuid() // << " " << available ; // log_debug << rb.len() << " " << rb.offset() << " " // << rb.header_len(); if (available != 8) { log_info << "check_trace fail"; } gcomm_assert(available == 8); int64_t seq; gu_trace(gu::unserialize8(begin, available, 0, seq)); tr_.insert_msg(TraceMsg(um.source(), um.source_view_id(), seq)); } else { gcomm_assert(um.has_view() == true); tr_.insert_view(um.view()); } } gu::datetime::Date handle_timers() { std::for_each(protos_.begin(), protos_.end(), std::mem_fun(&Protolay::handle_timers)); return gu::datetime::Date::max(); } private: size_t index_; UUID uuid_; std::list protos_; ViewId cvi_; Trace tr_; int64_t curr_seq_; }; class ChannelMsg { public: ChannelMsg(const Datagram& rb, const UUID& source) : rb_(rb), source_(source) { } const Datagram& rb() const { return rb_; } const UUID& source() const { return source_; } private: Datagram rb_; UUID source_; }; class Channel : public Bottomlay { public: Channel(gu::Config& conf, const size_t ttl = 1, const size_t latency = 1, const double loss = 1.) : Bottomlay(conf), ttl_(ttl), latency_(latency), loss_(loss), queue_() { } ~Channel() { } int handle_down(Datagram& wb, const ProtoDownMeta& dm) { gcomm_assert((dm.source() == UUID::nil()) == false); gu_trace(put(wb, dm.source())); return 0; } void put(const Datagram& rb, const UUID& source); ChannelMsg get(); void set_ttl(const size_t t) { ttl_ = t; } size_t ttl() const { return ttl_; } void set_latency(const size_t l) { gcomm_assert(l > 0); latency_ = l; } size_t latency() const { return latency_; } void set_loss(const double l) { loss_ = l; } double loss() const { return loss_; } size_t n_msgs() const { return queue_.size(); } private: size_t ttl_; size_t latency_; double loss_; std::deque > queue_; }; std::ostream& operator<<(std::ostream& os, const Channel& ch); std::ostream& operator<<(std::ostream& os, const Channel* ch); class MatrixElem { public: MatrixElem(const size_t ii, const size_t jj) : ii_(ii), jj_(jj) { } size_t ii() const { return ii_; } size_t jj() const { return jj_; } bool operator<(const MatrixElem& cmp) const { return (ii_ < cmp.ii_ || (ii_ == cmp.ii_ && jj_ < cmp.jj_)); } private: size_t ii_; size_t jj_; }; std::ostream& operator<<(std::ostream& os, const MatrixElem& me); class ChannelMap : public Map { public: struct DeleteObject { void operator()(ChannelMap::value_type& vt) { delete ChannelMap::value(vt); } }; }; class NodeMap : public Map { public: struct DeleteObject { void operator()(NodeMap::value_type& vt) { delete NodeMap::value(vt); } }; }; class PropagationMatrix { public: PropagationMatrix() : tp_(), prop_() { } ~PropagationMatrix(); void insert_tp(DummyNode* t); void set_latency(const size_t ii, const size_t jj, const size_t lat); void set_loss(const size_t ii, const size_t jj, const double loss); void split(const size_t ii, const size_t jj); void merge(const size_t ii, const size_t jj, const double loss = 1.0); void propagate_n(size_t n); void propagate_until_empty(); void propagate_until_cvi(bool handle_timers); friend std::ostream& operator<<(std::ostream&, const PropagationMatrix&); private: void expire_timers(); size_t count_channel_msgs() const; bool all_in_cvi() const; NodeMap tp_; ChannelMap prop_; }; std::ostream& operator<<(std::ostream& os, const PropagationMatrix& prop); // Cross check traces from vector of dummy nodes void check_trace(const std::vector& nvec); } // namespace gcomm percona-xtradb-cluster-galera/gcomm/test/check_types.cpp0000644000000000000000000000611312247075736023741 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #include "check_gcomm.hpp" #include "gcomm/view.hpp" #include "gcomm/types.hpp" #include "gcomm/map.hpp" #include #include #include #include using std::pair; using std::make_pair; using std::string; #include "check_templ.hpp" #include using namespace gu; using namespace gcomm; START_TEST(test_uuid) { UUID uuid; fail_unless(uuid._str() == "00000000-0000-0000-0000-000000000000"); for (size_t i = 0; i < 159; ++i) { UUID uuidrnd(0, 0); log_debug << uuidrnd; } UUID uuid1(0, 0); UUID uuid2(0, 0); fail_unless(uuid1 < uuid2); } END_TEST START_TEST(test_view) { const UUID uuid1(1); const UUID uuid2(2); const UUID uuid3(3); // View id ordering: // 1) view seq less than // 2) uuid newer than (higher timestamp, greater leading bytes) // 3) view type (reg, trans, non-prim, prim) ViewId v1(V_REG, uuid2, 1); ViewId v2(V_REG, uuid1, 1); ViewId v3(V_TRANS, uuid1, 1); ViewId v4(V_TRANS, uuid3, 2); ViewId v5(V_REG, uuid2, 2); ViewId v6(V_REG, uuid1, 2); ViewId v7(V_TRANS, uuid1, 2); fail_unless(v1 < v2); fail_unless(v2 < v3); fail_unless(v3 < v4); fail_unless(v4 < v5); fail_unless(v5 < v6); fail_unless(v6 < v7); ViewId vid; fail_unless(vid.uuid() == UUID()); fail_unless(vid.seq() == 0); UUID uuid(0, 0); vid = ViewId(V_REG, uuid, 7); fail_unless(vid.uuid() == uuid); fail_unless(vid.seq() == 7); check_serialization(vid, UUID::serial_size() + sizeof(uint32_t), ViewId()); NodeList nl; for (size_t i = 0; i < 7; ++i) { nl.insert(make_pair(UUID(0, 0), Node())); } fail_unless(nl.size() == 7); check_serialization(nl, 4 + 7*(UUID::serial_size() + Node::serial_size()), NodeList()); View v(ViewId(V_TRANS, vid)); for (size_t i = 0; i < 10; ++i) { UUID uuid(0, 0); string name("n" + gu::to_string(i)); if (i < 3) { v.add_joined(uuid, name); } if (i < 7) { v.add_member(uuid, name); } else if (i < 9) { v.add_left(uuid, name); } else { v.add_partitioned(uuid, name); } } check_serialization(v, /* view id */ + ViewId::serial_size() /* 4 times node list length */ + 4*4 /* 10 nodes which of 3 twice */ + (10 + 3)*(UUID::serial_size() + Node::serial_size()), View()); } END_TEST Suite* types_suite() { Suite* s = suite_create("types"); TCase* tc; tc = tcase_create("test_uuid"); tcase_add_test(tc, test_uuid); suite_add_tcase(s, tc); tc = tcase_create("test_view"); tcase_add_test(tc, test_view); suite_add_tcase(s, tc); return s; } percona-xtradb-cluster-galera/gcomm/test/check_util.cpp0000644000000000000000000001142612247075736023555 0ustar rootroot00000000000000/* * Copyright (C) 2009 Codership Oy */ #include "gcomm/util.hpp" #include "histogram.hpp" #include "gcomm/protonet.hpp" #include "gcomm/datagram.hpp" #ifdef HAVE_ASIO_HPP #include "asio_protonet.hpp" #endif // HAVE_ASIO_HPP #include "check_gcomm.hpp" #include "gu_string.hpp" #include "gu_logger.hpp" #include #include #include #include using std::vector; using std::numeric_limits; using std::string; using namespace gcomm; using namespace gu; START_TEST(test_histogram) { Histogram hs("0.0,0.0005,0.001,0.002,0.005,0.01,0.02,0.05,0.1,0.5,1.,5."); hs.insert(0.001); log_info << hs; for (size_t i = 0; i < 1000; ++i) { hs.insert(double(::rand())/RAND_MAX); } log_info << hs; hs.clear(); log_info << hs; } END_TEST START_TEST(test_datagram) { // Header check gcomm::NetHeader hdr(42, 0); fail_unless(hdr.len() == 42); fail_unless(hdr.has_crc32() == false); fail_unless(hdr.version() == 0); hdr.set_crc32(1234); fail_unless(hdr.has_crc32() == true); fail_unless(hdr.len() == 42); gcomm::NetHeader hdr1(42, 1); fail_unless(hdr1.len() == 42); fail_unless(hdr1.has_crc32() == false); fail_unless(hdr1.version() == 1); gu::byte_t hdrbuf[NetHeader::serial_size_]; fail_unless(serialize(hdr1, hdrbuf, sizeof(hdrbuf), 0) == NetHeader::serial_size_); try { unserialize(hdrbuf, sizeof(hdrbuf), 0, hdr); fail(""); } catch (Exception& e) { // ok } gu::byte_t b[128]; for (gu::byte_t i = 0; i < sizeof(b); ++i) { b[i] = i; } gu::Buffer buf(b, b + sizeof(b)); gcomm::Datagram dg(buf); fail_unless(dg.len() == sizeof(b)); // Normal copy construction gcomm::Datagram dgcopy(buf); fail_unless(dgcopy.len() == sizeof(b)); fail_unless(memcmp(dgcopy.header() + dgcopy.header_offset(), dg.header() + dg.header_offset(), dg.header_len()) == 0); fail_unless(dgcopy.payload() == dg.payload()); // Copy construction from offset of 16 gcomm::Datagram dg16(dg, 16); log_info << dg16.len(); fail_unless(dg16.len() - dg16.offset() == sizeof(b) - 16); for (gu::byte_t i = 0; i < sizeof(b) - 16; ++i) { fail_unless(dg16.payload()[i + dg16.offset()] == i + 16); } #if 0 // Normalize datagram, all data is moved into payload, data from // beginning to offset is discarded. Normalization must not change // dg dg16.normalize(); fail_unless(dg16.len() == sizeof(b) - 16); for (byte_t i = 0; i < sizeof(b) - 16; ++i) { fail_unless(dg16.payload()[i] == i + 16); } fail_unless(dg.len() == sizeof(b)); for (byte_t i = 0; i < sizeof(b); ++i) { fail_unless(dg.payload()[i] == i); } Datagram dgoff(buf, 16); dgoff.header().resize(8); dgoff.set_header_offset(4); fail_unless(dgoff.len() == buf.size() + 4); fail_unless(dgoff.header_offset() == 4); fail_unless(dgoff.header().size() == 8); for (byte_t i = 0; i < 4; ++i) { *(&dgoff.header()[0] + i) = i; } dgoff.normalize(); fail_unless(dgoff.len() == sizeof(b) - 16 + 4); fail_unless(dgoff.header_offset() == 0); fail_unless(dgoff.header().size() == 0); #endif // 0 } END_TEST #if defined(HAVE_ASIO_HPP) START_TEST(test_asio) { gu::Config conf; AsioProtonet pn(conf); string uri_str("tcp://127.0.0.1:0"); Acceptor* acc = pn.acceptor(uri_str); acc->listen(uri_str); uri_str = acc->listen_addr(); SocketPtr cl = pn.socket(uri_str); cl->connect(uri_str); pn.event_loop(datetime::Sec); SocketPtr sr = acc->accept(); fail_unless(sr->state() == Socket::S_CONNECTED); vector buf(cl->mtu()); for (size_t i = 0; i < buf.size(); ++i) { buf[i] = static_cast(i & 0xff); } for (size_t i = 0; i < 13; ++i) { Datagram dg(Buffer(&buf[0], &buf[0] + buf.size())); cl->send(dg); } pn.event_loop(datetime::Sec); delete acc; } END_TEST #endif // HAVE_ASIO_HPP START_TEST(test_protonet) { gu::Config conf; Protonet* pn(Protonet::create(conf)); pn->event_loop(1); } END_TEST Suite* util_suite() { Suite* s = suite_create("util"); TCase* tc; tc = tcase_create("test_histogram"); tcase_add_test(tc, test_histogram); suite_add_tcase(s, tc); tc = tcase_create("test_datagram"); tcase_add_test(tc, test_datagram); suite_add_tcase(s, tc); #ifdef HAVE_ASIO_HPP tc = tcase_create("test_asio"); tcase_add_test(tc, test_asio); suite_add_tcase(s, tc); #endif // HAVE_ASIO_HPP tc = tcase_create("test_protonet"); tcase_add_test(tc, test_protonet); suite_add_tcase(s, tc); return s; } percona-xtradb-cluster-galera/gcomm/test/ssl_test.cpp0000644000000000000000000001103612247075736023300 0ustar rootroot00000000000000 #include "gcomm/protonet.hpp" #include "gcomm/util.hpp" #include #include static gu::Config conf; class Client : public gcomm::Toplay { public: Client(gcomm::Protonet& pnet, const std::string& uri) : gcomm::Toplay(conf), uri_ (uri), pnet_ (pnet), pstack_(), socket_(pnet_.socket(uri)), msg_ () { pstack_.push_proto(this); pnet_.insert(&pstack_); } ~Client() { pnet_.erase(&pstack_); pstack_.pop_proto(this); socket_->close(); } void connect() { socket_->connect(uri_); } std::string msg() const { return std::string(msg_.begin(), msg_.end()); } void handle_up(const void* id, const gcomm::Datagram& dg, const gcomm::ProtoUpMeta& um) { if (um.err_no() != 0) { log_error << "socket failed: " << um.err_no(); socket_->close(); throw std::exception(); } else { assert(id == socket_->id()); msg_.insert(msg_.begin(), gcomm::begin(dg), gcomm::begin(dg) + gcomm::available(dg)); } } private: gu::URI uri_; gcomm::Protonet& pnet_; gcomm::Protostack pstack_; gcomm::SocketPtr socket_; gu::Buffer msg_; }; class Server : public gcomm::Toplay { public: Server(gcomm::Protonet& pnet, const std::string& uri) : gcomm::Toplay(conf), uri_(uri), pnet_(pnet), pstack_(), listener_(), smap_(), msg_("hello ssl") { pstack_.push_proto(this); pnet_.insert(&pstack_); listener_ = pnet_.acceptor(uri_); } ~Server() { delete listener_; pnet_.erase(&pstack_); pstack_.pop_proto(this); } void listen() { listener_->listen(uri_); } void handle_up(const void* id, const gcomm::Datagram& dg, const gcomm::ProtoUpMeta& um) { if (id == listener_->id()) { gcomm::SocketPtr socket(listener_->accept()); if (smap_.insert( std::make_pair(socket->id(), socket)).second == false) { throw std::logic_error("duplicate socket entry"); } return; } std::map::iterator si(smap_.find(id)); if (si == smap_.end()) { throw std::logic_error("could not find socket from map"); } gcomm::SocketPtr socket(si->second); if (socket->state() == gcomm::Socket::S_CONNECTED) { gcomm::Datagram msg; msg.payload().resize(msg_.size()); std::copy(msg_.begin(), msg_.end(), msg.payload().begin()); socket->send(msg); } else if (socket->state() == gcomm::Socket::S_CLOSED || socket->state() == gcomm::Socket::S_FAILED) { std::cerr << "socket " << id << " failed" << std::endl; socket->close(); smap_.erase(id); } else { std::cerr << "socket state: " << socket->state() << std::endl; } } private: Server(const Server&); void operator=(const Server&); gu::URI uri_; gcomm::Protonet& pnet_; gcomm::Protostack pstack_; gcomm::Acceptor* listener_; std::map smap_; const std::string msg_; }; int main(int argc, char* argv[]) { if (argc != 4) { std::cerr << "usage: " << argv[0] << " <-s|-c> " << std::endl; return 1; } conf = gu::Config(argv[2]); std::auto_ptr pnet(gcomm::Protonet::create(conf)); if (std::string("-s") == argv[1]) { Server server(*pnet, argv[3]); server.listen(); while (true) { pnet->event_loop(gu::datetime::Period(1 * gu::datetime::Sec)); } } else if (std::string("-c") == argv[1]) { Client client(*pnet, argv[3]); client.connect(); while (true) { pnet->event_loop(gu::datetime::Period(1*gu::datetime::MSec)); std::string msg(client.msg()); if (msg != "") { std::cout << "read message from server: '" << msg << "'" << std::endl; break; } } } return 0; } percona-xtradb-cluster-galera/gcs/ChangeLog0000644000000000000000000000552712247075736021211 0ustar rootroot000000000000002010-07-18 Alex Substituted gcs_slave_queue_len() with gcs_get_stats() to return a wider range of gcs performance statistics. At this time it includes average slave queue length, average send queue length, fraction of time spent paused and number of flow control events sent and received. 2010-06-16 Alex Added gcs_interrupt() call to be able to interrupt scheduled threads. Version 0.13.1 2010-05-31 Alex Added flow control monitor and ability to synchronize with gcs_send() and gcs_repl() calls thus guranteeing FIFO order. Version 0.13.0 2010-05-20 Alex Added gcs_slave_queue_len() query to API. 2009-11-21 Alex Extended state message to contain previous primary configuraiton info. Many bugfixes and cleanups. Version 0.12.0 2009-08-09 Alex Added possibility to specify desired donor. Version 0.11.0 2009-08-06 Alex Refactored interface. Now connection URL is supplied to gcs_open() and not gcs_create(). It is done to satisfy wsrep API changes and is generally cleaner as it separates library initialisation from connection establishment. Version: 0.10.0 2009-07-21 Alex Added node name and incoming address arguments to gcs_create(). Thus it should be possible to give nodes sensible names and see them in logs. Version: 0.9.0 2009-06-21 Alex Moved TO module out of the library. Since it no longer offers this interface, bumped minor version: 0.8.0 2008-11-16 Alex Many bugfixes. Fixed handling of self-leave meassages. Switched to "mallocless" FIFO implementaiton in gu_fifo.c Resolved apparent race condition and optimized FC message sending. Package version 0.7.2 2008-11-09 Alex Changed state transfer protocol to require join message to be sent by both parties involved in state transfer. Package version 0.7.1, library interface 9.0.0. 2008-10-21 Alex First implementation of state transfer request protocol. Bumped package version to 0.7.0, library interface to 8.0.0. 2008-09-29 Alex (postfactum) State exchange (GCS state exchange, not application state exchange) implemented. Now we have some sort of quourum calculations and global-scope sequence numbers. New nodes can join without having to restart the whole group. Bumped package version to 0.6.0. 2008-08-01 Alex (postfactum) START/STOP-based flow control. A little bit ahead of the plan. 2008-07-30 Alex Added gcs_join() and gcs_wait() getting closer to final API. gcs_join() moves conneciton to JOINED state. gcs_wait() blocks waiting for the group memebers to catch up. 2008-05-14 Alex Added gcs_create() and gcs_destroy() for safe and clean initialization and deinitialization of GCS connection handle. 2008-03-23 Alex Added gcs_set_last_applied() and gcs_get_last_applied() - calls for voting for the last applied action. percona-xtradb-cluster-galera/gcs/README0000644000000000000000000000115112247075736020304 0ustar rootroot00000000000000Welcome to libgcs - Group Communication system abstraction library. libgcs is intended to simplify the use of Group Communication semantics in applications which are not initially targeted for it. This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY, to the extent permitted by law; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. libgcs is free software. Please see the file COPYING for details. For documentation, please see the files in the doc subdirectory. For building and installation instructions please see the INSTALL file. percona-xtradb-cluster-galera/gcs/SConscript0000644000000000000000000000003512247075736021436 0ustar rootroot00000000000000SConscript('src/SConscript') percona-xtradb-cluster-galera/gcs/doc/0000755000000000000000000000000012247075736020173 5ustar rootroot00000000000000percona-xtradb-cluster-galera/gcs/src/0000755000000000000000000000000012247075736020215 5ustar rootroot00000000000000percona-xtradb-cluster-galera/gcs/doc/Coding_conventions.txt0000644000000000000000000000533612247075736024573 0ustar rootroot00000000000000These coding conventions were not set from the very beginning. They emerged as a result of coding. Therefore they have some practical basis for it. Not all code adheres to them, but that's what it should be like. Attempt was made to justify at least some of these conventions, but it's not about right or wrong really. It's about consistency. 1. Indentation. Tab width is 4, tabs filled with spaces. No real tabs. There is a controversy about this issue among programmers. In defense of this decision I can say that indentation is some sort of ASCII art and ASCII art does not always work good with tabs, especially when you need to alter it by hand. In other words: spaces are less flexible, but more predictable. 2. Braces position. Opening brace on the same line as the statement, see example below. 3. Spaces between tokens. See example below. 4. Function declarations/definitions. See example below. 5. Naming conventions. All names and identifiers are all lower case with underscores except macros which are all UPPER case. 5.1 File names. All C file names are prefixed with 'gcs' to avoid collisions in the file namespace with headers from other software. Prefix is followed by the module name. If module consists of more than one unit, unit names follow module name. Like gcs_module_unit1.[h|c], gcs_module_unit2.[h|c] and so on. 5.2 Symbol names. All global symbols - exported through header files - are prefixed with 'gcs' (or 'GCS' where appropriate) followed by the module name. This is done again to avoid namespace collisions with the third party software and with global symbols from other modules that may be called similarly. Static symbols defined in the *.c files simply start with the module name. This is done to easily distinguish between global and static symbols and to prevent collisions of static symbols from different modules when doing tags search. Example: int gcs_module_external_var; static int module_static_function (int a, int b) { int c; if (a < b) { c = b - a; } else { c = a - b; } return c; } 6. Long lines. Lines should not exceed 80 characters in length. One more reason to use spaces instead of tabs. (Suppose you have tabs of width 4 and then some other person reads the code with tabs width of 8. Many lines will grow more than 80 characters and may not fit in the screen.) 7. Spaces at the end of line. Should be avoided to minimize the diff. 8. NO COMPILER WARNINGS WHATSOEVER. These conventions are not set in stone and can be altered eventually. However, for the sake of consistency, try to follow these conventions unless there is a REAL (and well articulated) need to do otherwise. IF IN DOUBT - SEE HOW SIMILAR THINGS ARE DONE IN RECENT FILES. percona-xtradb-cluster-galera/gcs/doc/Doxyfile0000644000000000000000000014366112247075736021714 0ustar rootroot00000000000000# Doxyfile 1.4.6 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = GCS # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = 0.2.3 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = ./ # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, # Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, # Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, # Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, # Swedish, and Ukrainian. OUTPUT_LANGUAGE = English # This tag can be used to specify the encoding used in the generated output. # The encoding is not always determined by the language that is chosen, # but also whether or not the output is meant for Windows or non-Windows users. # In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES # forces the Windows encoding (this is the default for the Windows binary), # whereas setting the tag to NO uses a Unix-style encoding (the default for # all platforms other than Windows). USE_WINDOWS_ENCODING = NO # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like the Qt-style comments (thus requiring an # explicit @brief command for a brief description. JAVADOC_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the DETAILS_AT_TOP tag is set to YES then Doxygen # will output the detailed description near the top, like JavaDoc. # If set to NO, the detailed description appears after the member # documentation. DETAILS_AT_TOP = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for Java. # For instance, namespaces will be presented as packages, qualified scopes # will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to # include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from the # version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = ../src # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py FILE_PATTERNS = *.c *.h # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES (the default) # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES (the default) # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compressed HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # If the GENERATE_TREEVIEW tag is set to YES, a side panel will be # generated containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = YES # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will # generate a call dependency graph for every global function or class method. # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected # functions only using the \callgraph command. CALL_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width # (in pixels) of the graphs generated by dot. If a graph becomes larger than # this value, doxygen will try to truncate the graph, so that it fits within # the specified constraint. Beware that most browsers cannot cope with very # large images. MAX_DOT_GRAPH_WIDTH = 1024 # The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height # (in pixels) of the graphs generated by dot. If a graph becomes larger than # this value, doxygen will try to truncate the graph, so that it fits within # the specified constraint. Beware that most browsers cannot cope with very # large images. MAX_DOT_GRAPH_HEIGHT = 1024 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that a graph may be further truncated if the graph's # image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH # and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), # the graph is not depth-constrained. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, which results in a white background. # Warning: Depending on the platform used, enabling this option may lead to # badly anti-aliased labels on the edges of a graph (i.e. they become hard to # read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO percona-xtradb-cluster-galera/gcs/doc/GCS_Architecture.odt0000644000000000000000000007265512247075736024040 0ustar rootroot00000000000000PK O7^2 ''mimetypeapplication/vnd.oasis.opendocument.textPK O7 content.xml][s6~_dlKE$O.'Imw:5nK "!$bYyݗi%{;):SqKpwAOCYܻGgrڟ^W|Mvcq3vME/^x#[o{qpG]D )R߈08 b=Em;#m/Y{[]팴j:]Fv'>VQ zD4q0ʵM1-9d`OPwO@Wܥj e_{Ϣ#3yPbٷ#fa?1lM,w6}3MۢBgC~d['n=w;3W(,Vc8fha$o*'<Ky,!xvdMޝ^>uh'h0o9rᶈ\qFdZ.1͕0Ӏ.җ(V?iOj/<HAd .j>u%$ad/6~k@[P'R!v ֋s'Ft/X{ FgdrǶz$k²r ,s_^(z*9lCHXa;)!hёiBڼQΑ8vX&x1k{l{l{LU~T|WdC,A~^P*4> *@J%V*䳑R䳑zsNQtXe%a>Յ4'+?-Ow$ŚKtuڿ@Qvd1{bucrGmlʬ';޳XmA5}۳#l/CǑ]vw>=n?~5@è6.pww"j dX-y}8v$K 4g@;| v@cˠv>.y^+K2^|v(a)R~t.la.1+;{)O!5VTږ尔BX03" 훭%>/hk0281Rv+J9 3 x߱];㪽v c}5x*N?He`Ee;UV{nUGIeJ14q-D9:0WH|壏FRCM1Nf ~S ⨊XZdEռ)/.hg\%ƙ TFJt+*OS !gxJ{h:hoNiitvvVڬS[`lf6|G`nE#s$;Xۃ mu~fk;dR'U'b^)}|-㨵 4oq4h?xN.pLEZ5:4RԾsfN״' ~ U B70NΦoܺrglReC7}GE\K@: Qу|+Q~PUj7}`숙Q0 fkrP ]'ӭi]a#)m|V7_-|: -pr KT#Ph'3;[ypFjz O4 Ԋ8K*%m } ƽoX hhs€:|Taf+%^ձuh0N2>+`?L$a _2(؈h_s&! TCI~B^/&VH>A .̹e/l@_ISƈ/;˩9JoJ1ʫMO>+D|.D2vm zʞZ`w& B'_"xCzohϬFSֳNzF>+T𹐊/Re&ԇvcWDv$% 4=cl1ӀS󫴡:&OMɟ5LUc΀2ҷ* 2iPBR@/l>hʹ%읯gL" p%6fҨ83k-$Cgڠ/1յvi۸/3kZJ1mc10fy HmeaHEܲ[lFz]|u<e\[)l"ߙG[REQkW څ"s8m6P&M2S+yA7 ف6'KoH 51rиc̴ّm\T6pHTFECTgJQ92 4<D'՚4STI5 ;8 PzKԲnF hکnn7ɺc0Gc4_X‚5Z9Vľ\^(\KaӸ\Ykq|,RX  3 | YA2'Vxp. lp.E%)UGmܑ!G%BaYJ\7BY_'vHD2@:f@~-4~'ߕl,vBRLҁ^ 3 0cU$lQ3By0ئ:\(N X p$(tSrmg8xCjN"-'FQJWu36z%1גYYȌ$Rq`a9epj %P+urOLDKA./P|N1G[X~qaE((Z2!"#-) *uB[ i g"i+y}T$h-#HM vEX@B=f5B|ɇ h' ^$AJNH`Ϣygq%<:ey Uz$s׉@Μ0r*#F&Q;mU>DTl@5+7 +eơ b_$KqX&2%!2l4lL B^q 3.佌U\ ~S%*~ɨd0]e Bccl eΎ'0 6k_oi-&^?PKPPK O7 layout-cachecd`d(c``d``2PKXsPK O7 styles.xmlZ[۸~\ldKMvI Zm6)/$ʦ|t.-NĜpԻe1t"L3?x5ßޱݎdx.1:S1 C E%mX=iʆv`g9v5stYa~,x⌕ʊsAf29Nl~뉖6g yQy6VI:N'[bڧI.v &}ksC;%&AS :v\qR AcYi'淇>݅8{.c9g`'"+zenym{t\<2&4G.ⱒC{J9?HO=r"9qJ'Yd8 |TU(E*"6J'LytOhz9qo @N4}{A ܒfNgN pzbW:ώmX{# [Trץ'nP+n~`*GO ߻IrmgZ=%Deb;(w!ajVϯSD뻛S~mDx[0 oX=ungS3n!y$lS=D҉}~s~%~nhåuglnWCvHA>fb{V~24.ѹ5Ll+`3}>x\U&CwРz04,<봽p`e#Lz%z&Dьfo4Z*ÕIcPK) l/PK O7meta.xmlA01$jzCCOcO`#c0u7&yL-J} |BKThޔ,*2mU5uoղPS˩@-tN~SnT_5Zicq_Q'jLSc(Pc"xdφy$4]~\k־7l:,sP`&{kfn{WFnq J~B= ^RI+Y^Z ޯ w=-1ܯH0 YF4QDpYG-""CMLɮ]?2RYtl 3bֲ=1mgDd`*oX(tه$ =)#~z;-t75$,W˖?Y{`sXֶx @\7ʦ~{(X]ťZp .S 6b7xPmPb'PKA#PK O7Thumbnails/thumbnail.pngeT[KK h-^{pV8/R]B/nAA)(5K$D04$SysBL-_mF6!L- `TuVԐ`D1N'"9>^[3nF͟2e/)4p(X8S-ElZwYc8ϏgݞX3A-N1yL5mEb042DV?0bNf3uC F70`h Sv帀S\mߎP{Y],u]z4ZA7kx@l^G4y S1!8 Ƭ]:nq]篔aΎVrfx;#aˎuo^ z; ƻ17#,ud~ˮ90+]u`Aq%3f<^*$>m Q us082̘ϊ,D9cdiwΖ%* |<67َOHpmY\>.Mҳ6?ɬC1Λ- _+GOIMNe yGNa7)tzCOCgKU/Z>K\7z`t6cw5{9)KZeC6 }S=+.Z(v @pʡӵNXu>?e>f&lG%e`YH_|{5a=ㅚTdYy㓬y11u wqfܰ,)Y}T%lv]Z<~0s,Nَ㮞 r M}G+tC.p7 4 Ft%jv}f( yWvNDžfPz edAZZ,5ސq! (tL."h1;u,@4Rܷ})ׁFu~CYr/c?/67򯵤b9z<~&vZ%'wu1 {<nI:ڃ qMBAg0ǥ)Fz\lw4 ~:D)"\q$Ymֽ9כƄIya0PtzKLbs ǘ s{AsS0KfVbyW2 9D߈А嗎$8L#|c ƣDöAu -a !J\N "äidy%|o?F?#IάfE6ӒyW ‘O4=FѝI'.}烰טn}ӥ}haSq{DsxR~dCΐy=fgUj6Hl-i!^I_n9:2sg[ֿXp&xGC$ 1)nϮvx: ҽD+ ygEkbk-ݟx* X9nWuƢSFyxR@8C}yO},M "o' D+tRx|RGIŵlUA7{g'902}BА"IBYx^B'x8t@ HB5b]"/Vb)sNaba;>[.K/n?'cl.KOlI@ɲ`G R595m9tey,җ5`/@҈9UjFƐ!‰6ٿ1'?Qz6pWލ,QEQQ,>^T8EEA~$a (7FUi5ԝuh}0|d=:#uZc|G8]z8&*O@ ?[eu_:؋b(SXCd_vni4Z`QW&~!yPyadZisM'5̸0_iu4j"AB:yeuXQQ1|SHUi4[-XC$^ i%6{q )r5ANܱl }>l'k~%He %ΰIK.QlqFlWJE3Дa . T4?C9#$ޯ3$P͍.ք}FbfGs^Z?1D'N=U.߆jцV=C˓زTaꧾZ=ސIȒK".t}lLxjuүv#JBg۔zO|F1[zHO7K.jSև!y<:Y3,zǙJݼ}_X_^Pku]s cQ \ ~b!j$2^hbn[FFOMdzqs#iSBk, n1 ЧLORBfx<|Ȥ(z4 PQ wzZ2ﲱVRٕTQ8X8XRT]WБ^潵1}nϴ,1Al+9(x>c,:Rώ%J.WhCA;zuʱ%}"Y&A;lfb?=gJO|,m׷9?2j"0ib.6>vʠNL1ۜN\ }ǁЎwGDhmajk;Fjz|{ ggJ31N8h@šDfAJN"&z$ǴLVd'%XyUZO/jx@"ǜ5٠G p#i~xtp(+gZ#ǢB5mJufRzwww\"jƲh ȴɘx|w,G'%CC#ڡ!3#;ʖ^g#;|H͝4sbhClc vbkZ<Ճhl6(sRqmE)=ҜYCg@0K..wH uՌ2i`XWvܼZȚ,ؗ%lQBۜ 4HMAq\6ja*ƅt^,T9˚OK!-W&&$ +ܶeISYUs¿[Q*kxiE{iMʧD[ZYKm; x hFY]xjK`%RPz!A׌t!.+emj*]xfCw<5 1W箧=r 5 sٹ)47BO wť!v=3TyE[Cqj*m ɐx`Acޜcv)A֎ &#lNT.l`*˒r L7Hy"yK8AI]~u؛}1Fm>!M pb ׿POW;֌)YgN@,xqUr<Ш4Ǜ f=4: Hqt@Ȕ0ڋ̊ÇeH,ۆpqE?8nbdOli+@s2pAJ78.z ޟ( JffʝdGFKq*&H̽$͋(ǎ@a0H* Wh{gqIc.߽,(txwo/\X¨َa{Ё}t y:uzvG3ަuDROR@\]["ufSM=®ѢS.uQr9K)rlNMbѬ!k9Yݛ#G{-#RX5K= 7`t\7.q6$HsMqk "ԇD"]s <Wޔ9M!JƇEPD*il6_]Mqc?8Sgyժtч-CCjQ9tNoAe[&F.Zkw#I sa?,``M%F;ǭVw]UrqbA]{8wЌf_et԰'ڢj3}.%mJ8~Ka.XO;ԙe*]x`ն;FT:RNdpw7g$:E 7!7e01Wx㒬fKN&`o:\>`]MqLW? šnbyL]czZ0${=ٯ4Ī-u^9w8uQ$s%uu" V: ʤZqE̳W*I3ng_r037J5Z`+mpOj%_iffM{5ڲh!mETg61%vzS3F@o5UV,G7Le?).qFqH u:S,Q0z{!&RnA[s1@+Lݸۉf/I3 2h1.Djxk {hmbOe%nwфø>SZc83.ov,Xc_}&\)muHo'@'#Eج+S%Ho7WM!,r#$!Y(uIDiPL'lȭdXrc7y?U.j_$o]ڌ*8Wk̙ռҞ"!{G7GMϑzP NZqhT-ԟ* ﻜH kêu 9h;Yr4u bܔͮn`cIܜ^+: s-u.4ܭ&֡Zc|xk; io/7 |^VTd<2F~?{>|YTc D)ǀaϿHwoʆJS͐#{oF9 ]*a84@-kH6]=:t*j>g6*&v"xRVa t4]W\DHnlD4!GX9KXioSB1S[#|g*rn^м7*t,5>t[ /iz"$i>Ը$j\bo[u:U-єp?W׀"Dq ݁(\$ʊeqpv 98fļ>fePs\i x~ۜ8iŹp8g>'x*dP5Đ Qdph5>czRJ~o& ),$)@9#ѭ`'8KQmCi>j[yG~"9.XH٨ 8(k_3~c9  kBCO4wsU~j5m9h:jJN]K!%F..+}7Փ4Noj?-f)CqhOIZrhوkzS{ x V ?O|bE5A Sq--xɼaA6jg{EuVOw =(d)P7Mא̤uf_ۋa+ UL`z#`J o=)|Ǥ3պj@ eKwG'GA'ڄ@GDn~_ Xhq4ax3EH Q-ϞBONR!,Zsnp{Z䐵?S>Qud?uއ Rƨ%E47W"3ߩFYY->AN7K7Ht> o*X e\:|~;nL0 :.'wc+?6^uICK֣;'rQ8H>j.# CyM|/xӹ{/ZX Me1>,]N?ރˇټ-z|ۨ:4ֻ)h yj޳J*eWK7; MY [L.Cpw=8.AT8$^ҽJkUk$`93K,#W 9?)p2R!y n8lBpE},j v ;z8N\ܜ4?Xߜ]-F!.ER858W,,'w_ q 8Ɵna"c뛿á~{~\=J] 3r_`J!c" {0?hdY$"i'5ae|;(ޥ7`a^$H4 Ͽ}!Z 9Q=y vM|wƓv26-`BFHuzkuvaղS9ǻZKAXx.a-qXfFߣ.܄$ 5N۱LEs`wWOsĂ@㎛ޝ+Fټ\dQ7٨Ӑd?wݭ1ԷZքdt d9)j >.vqt.u[mjY 8xZ2  G&>SO] HҧR;U7:H,kŘxxqFɡnM&өΈۨ-SEҖ5NWpm>$$"}8=e!lע(ޞ' Q :]9 2TPsֽG%6kZ@RjD وk#ݱ'<_ mXCOPfR@!\|~ϻx#mI]S,g.=) BE!Er]jq$NI&(Ek2!BY. /l *챂G{?hLpjzLkv8=OIA?[=h]6S){cޒ==w F]|x"v#^1ePM¯_ UzM)Y!CÜsM;*$vm+] U#w3_Mq<ܗQJfݾw^͔/=Jx5%45%/ZkII&UN>?'zvQP?ܜύ6LN8woPiP' IN%HxX T Eg qip:JA]#0!T5jg8]Qf2˞y6m+!V>y '"/clcqta\4o*59\?Mg # a{ 9dJ}~oΙ)N;` 9,4M`蟪 %˙nPV/݂LLOn!u{OsKuH=i4FJbE3.-.W&Vb" AF)y]3 UVb֗=0H413}QQ|M tKޠ|?N+Th(!uuCƹǟ Gc)ދ"`q'Sޒ8iZ\n}~`KKm}!1EuyΛ 8n2FHՋM2(~t3xۻ)TW}IיŁӉ~85`v2%>M1=MOh_>\p2X_փ`{WvJz?`Q A]9 `gP#"LñhSGSM2,pFp>э?I] JI|kKnWuc5{V׉W=RƌEB{z~4rAveT֘6ʋ´~e,rOy0_;- 祚T<Uuɯ+o> _tMl~%];,N ֪@.*f6 1r5w_jho1>C 1X%+<\+Rx11ḹ([\L?#Oq3ٸ䛡GVaN?x/*LE鰒~ԉ"_Jwߝ>yvfU֙R:ĨecNg)y04?לʨ7KS _eE{0M,;^t8( /)ڸv.^~m_DLyV#ê,?dy2A}9j~Y7g5F&;"Fepl &6/X`~} ޞz(R Z֗mI0Ƶ=fT~RT{T;MF7't$쀆U4ؙ%*ICf=i^vt+H h2<[-cT`"i$QzOJDg1e@%%g'zpIvk6BkA7;o,@EL9@{Y|9pE{~H:WE#e$Z*EfRĴ i=iT8 C[WoP{k-,#>$R}2V,1M7M}fdfƇn9;)m11D_tL~e5v;Yp]Q|UE)W6UٖՁp|h(IGP<_kIYQ7ˡ>eH[_b$3JGy8}H sO34%'n[S.\ R@/uNˋe.,P)>(#{'N'uk;qur|h@|K)#'{<~~" 3۾r_ \wh?oZjFKP(p6"t ˽*&K` oGGo3}=GW72<8Ɩ($.6Zrs;@Mi9D8n Lg3RwJ5DPLM#Yj{ᇢ&#hPrn:cC͠NgG$@֯uxCYޜZݥ(i΋u/rCO.!L=Q;|CP {kq-nJЄZg6ᐹ: jﲉ;{k^!ߒ8'OȪ $- %h*R 2Ȫ~U?hC49?9OWM;ro)ˇڡHͫA7kޝR_Tzo_̝=&z;Q.[~u6?ț5 3+wᄈH{b.S>3w>!7![WݿD`)og\ $IJKO5\B}u'$|ms 򯭉qj , m4YWО=v?oy߰j;[`W:͵bU׺cƄU.Zܒ=ܕ|W_#&kc5;;,+O1mSe?G(dXy'ѧދorOJJ^CW ޴u]&@DCTQ](!n>eol')[ƙ /RM*EM7=Ӂ:.C@ZrQB,K.7kw5_suӘ,>XLjދܽ~=Fȏ80mE,[)yωaRa%@6D[ cOBZ9GbEO}`FO!lIW:iFVоٝ2KRndU( %OV;B8rJ />ŤѨA~Q:.¹S2fJWadzH(-/7ՃU~']hf'ޜLN`@9 &-ry64 Z2Zu:)0@ ' myW^<4<,^^jێy }54.ɷ[綝/y=y*x?H7VKʲ!l`=~rʢ$͐ϛD!@0*.I/'|qo]6v֜yo)^&PbfG0g)W,Qi%]554{u̇ f߫?$&Z%0 CS'ThWkw&$Zj==|;QF3j'apY_Tj D3>}U1CcȒq/ZER;̭%؏~I+Y/|,3WDXLCl"oKKI~7Ww\|cs`~^T<#fT:[KYNA5;ՇXGXƚs&j<㍯`hO{]jiѢ9[& Y礨;t(q9~uI?Nc]XG֎t2hrԘ|^yz\^r(YŖt:j}1gXҳy+Ex@}N>' dYnr0u|%Քrmm`Sw|zAyD(;{eޮmcN]3͈'/>)rz(cEk)'.c^?T4k=4慊XðYOƿL`'d#OuPr kv[䩌WtPT3 (1UP٣nQ*e>+{Y_7)o!D8Є~bSXB YrXъ2^>.ͱ;_v?۳ 70]y#>)W^<¤e{WyiCoI}3 fIuw0ji+Fc|Yc)msq7X="q뇉wB L W-e >t9M@%9!|&6OШ{iuXy}3*u;o̗jW:~cBWE0oC l[/J No=duҌc뗉UJñ&/ţk<íS{'2s&c ECBXP:Lc]&Tp@݀)[R3<.qzUZGl&.0B԰O[s޼~AuQn.@lBHJ 9TO=|a??LOppZ%u{3wL1oGS9;w=Ջ-Uyd*Zʠy~SYC>My,]z8¹SQIqxq_#Ş'Scu%Q h9IAVuM0=83p R]{. z\^5vM pײf$PP2Am?aF1oX!FL7MdL.\ZBèAecS ]f#>U{S V*50S*R G,>hY4tUu#Ԝ!yyQY)ilfOhSXwˍ*t;EE5UV= / 8&J/1.g#3ϟ}o=FXSx=rFzlg|K UֹѨGq"R=K#,cg: iyԱ)@AEK9/iv7|?taAS޾Ear|\-Pڄ g7 5rN.&2ͫ\ov&MrB(i_-TFs~s6}MMN_ײ'b@󫒱GΛAC:n c5Vc(،8mN'w~Xp==b;*~~5c|(h,B|''i~T7(mSDQG/}J73}'B ,Ӌ7{QipO@&kjK]4|ݬXy\JL>BG_=բՄNq$s[Ts{ 1ؤe\wq;Ɍm_HFFKE~޷I'שGIo936Tdx᫓Z:Pw 8͐Nvn>_+,zw<53ǻELM,ȊWxF!&ڭQ5ܫm yvXsZˮQ^Qp`L?HbZd-:Y7$|#X<};R)ZsOn7-ؼקKGJ6<\-q֏.cuu_\cߕ/6uOP[\1O?0gW QkibmRyo5Kpe+E'.Io_֩I߱k' V2Ko6~_wdn|GRkW׳~|MJ%FJ66qZ|!uӂ{})*=R}=q:)kk^|.}ngɧ F]O7?Gy%zQg֚ew_x3N?]roUx|q;r4S_*weWLaV ;^02/ٹ4z˫?OҮGƝW^p\v׹nHn`4r,yӘ)g~/[\!T:%N=έu/n^yb#~4z#sˮR"zZֻ(=̗_٪$}QÛs336-x贄/>g= tsYPKCRrC DPK O7'Configurations2/accelerator/current.xmlPKPK O7Configurations2/progressbar/PK O7Configurations2/floater/PK O7Configurations2/popupmenu/PK O7Configurations2/menubar/PK O7Configurations2/toolbar/PK O7Configurations2/images/Bitmaps/PK O7Configurations2/statusbar/PK O7 settings.xmlZr8}߯H@T”c›l p"$$co@vLC(,uԗ2w?6+z&Bzgrߴa.[gq==1waZ`Lw@2GғeVD[>aegce'걗R)͆a-,|b͕Jl:ܒ ~\\q_ YWlx <9:O /_H@ )MHj|" W&QG'Nk|Lghc#11^1aLG:tBX2Ljn'gu`Oyd`-KnNnbد*Q;ڥng6 D`ٷ֍mH4/g'_\;p(|\6Fa`6}4gqksKkaLLZ45;z=ے}B`\RgJ{PC h\*8)U̷R+_uާ9aBCTdz``8^]Ҙ|8tGNDŽ Pȝ4PN!!.:\?1Fi}n-ID)ty :p6HLU/dw#g5eEwn{y`]4)wo"z*ΉK\ 8'A=_oYQg.XK-`06ni@-= (OMΐd2. *So -._IPFX-Hw>qӴ)P XX5y֩d^_OCqw`98˰P x)wVWu,ΫdL遷B$HrL| n^nb p``pKjF<w$kuz@(8 )~"g0zU|9#{MJRŘaF瞞rPKrTPK O7^2 ''mimetypePK O7P Mcontent.xmlPK O7Xs 1layout-cachePK O7) l/ }styles.xmlPK O7A#^!meta.xmlPK O7CRrC D#Thumbnails/thumbnail.pngPK O7':gConfigurations2/accelerator/current.xmlPK O7gConfigurations2/progressbar/PK O7gConfigurations2/floater/PK O7hConfigurations2/popupmenu/PK O79hConfigurations2/menubar/PK O7ohConfigurations2/toolbar/PK O7hConfigurations2/images/Bitmaps/PK O7hConfigurations2/statusbar/PK O7! isettings.xmlPK O7rToMETA-INF/manifest.xmlPK(oqpercona-xtradb-cluster-galera/gcs/doc/GCS_Architecture.png0000644000000000000000000010240612247075736024022 0ustar rootroot00000000000000PNG  IHDR[bKGD pHYsaa?itIME  L  IDATxwTY7HoR" X@Ab]u]{Rm ( tP=fxdrgd3wJXl6%EpppLppܹsUUU9ClllwVw OeK"Ç{zz}|ܹl6{ԨQ㗔HHT5˪"<ɒʖ;7xବ۷_FFݻw'NdFx捲2G>ݓ'OpM"<ɒʖ;@*gеkPmmm?>}t6fa6ϟ?o>77&+R+\eKp@=z444t<ɀƍ3wXÃ͛=0((_~ݳgϘ999kܸ/hȐ!Lᒒbbb,//oii|0$$յiӦcƌQPP055b>RTׯ'NTSSSPP>|x||9)] ō;VYYYYYyĈ?.wwʘ7$~@LݻweĚ5k233W^MDOs퉉ϟ'ۗ./333"͛7ؘfkjj2ܰaCjjjHHk޽[V$11Yf666qqq]t4'7hР/^hjjŕpx1˾}^p!777(()l STT$"c\pwCd6AD̮lv~~>5jHpбcVZ좢""d"hǎ#}nqvv&@͉0.[$`^D4mڴrA3&~)--sׯ_# |AII n) 999cr޽{666KGD[nEtttP9@b!"MMrgLy+PZZz̙o߾EC1?‡/rrr*rAVV֨QԮ]V HFFHZZsRn5yyy‡A3VysNӦM#F[86l0|U iܸ1={LtҫW^paĈeID-[ĻR8@2m~آw5 @jڕj}`Q#))^J$dR T8~ ΦV)P.AX8@b@uD:E@bԯ|P<@8@ UJ .g,ϼ٣[Wx>K10eD "uH y l89$J Ed@>@w#]8@ D֑ N%b  ,FFW| ,B3`ݱXP ,˘ XPx " @Ât4D<@8B @E  k"b93e9c'Otյ^¾t4&僄D#Vs\wўY<r2:彬p N`~Ŏ_|Y,&# ^ٱoo2nsR2Ý[p}rR ͛Զ5[D{hqWZZaqPU';G:gVeyw\uZgqHkDGGNj`ԃ]gq=pEz(,r'G>mK-W'9鞢L㌼G{-#Ohb/֡N+@=0}ǺuC,-J/tv>#/#Gv{J6VWn2b԰dzmw5S7hl8#|J mj/+%$jqʐο;]v~I /*)bI5iCDrjT6k9o55eQBX,66%$\~ƍ1c܇…_qt<2dȁ+oUPPlkۖ{_=!7gMs\&IX_]#}ZQ\Rļn=I'];zf3پM/ѥK[ddX&&krkP <(苕68@8QZ#x3)ww;xxLx1Ï֓'=qb/r*.ޡ;l ۻ/ύ; l}Fc|&e{p?^k2\,jQIFnC^JJ˞J:yx^k}f^6=<|͛˾}8ݻy}Di$޽Y{-GLj\UMMС ۷張{RӦJ{8 s>;|]m}=㈔L"JI|4-JPy+=]k$%V4386 OXLNF[V"q__Ť|v}ʲ'%"Y,y岋Ri9`5lЗhe'6b^v7,+A<y{qI(݌˪֡C3%Lv+?2ZJZ @QybnYǣ?& Y)GEmiZiu z`3D%ю 43CڷFB[1GE)K+Ykpq+'?cQ+Q9 U!wf…}z-k1ccKxZRjjVbbř3{ Y{y8:Z` bk|,.-qvMlO?\=2}2RryY|*.)z:3868\|||݄ +((ݸ[7I[[ji)z&[?99\TT8>~;󖥥̙gK.׵kK2:(($++YY)w} ?\ ~df9%pmi4E-\U 2sӟ_vuq: !!iNBBV.UPw+p'>|H(((nbeX3-8]~gXu*.e?P˹SLO<Νc (99shݻDH}f|W= CCVJAiyCczi3і w>lO®AGҒAD9})'7Q>̕& ZA 8CӦJ7na>sfZ5M@$B /L 4\|sW$ 4|#"Ͽx/ ".LjpP" ЀPLl@], "4$U@DQ"hX,g,%h4:@A]\Wl+4 Jn6zi;HD]lV`7$0}VCR\8dPg! "4],  "@H|dp$QoP@2@8j8PO ڻ #@tD <"I$|"b "b"v$T~gPD@J@Dw\*yY8F, {oJ;0}~:Ƣ-A%VFq |ܛ~g ! ~.)AD ".>)_x#"@u䶎}Fj4o]D(\PGܿ٣<zֽ6<)eG"zYfz'\_q_: &:?.n|IMɑ#hڝMϜ.yףGdz.82 ٿgDϞ;wO9wqqts }ϙ3nn9erc>W|˗O~TA!G SY, :SV\ڵb~;{:澆^c <@Sg VOd2l/ML[z☿?ڵʍ9FFm_[˷ QB8osfZ<}}9#.-#㻉*wVP^Pjfk?g,$L-.Ci>x澆"նmY/]˹499gכU^^{ȤIΧNmܸyep乎t!reϧRVV){hP+pZb3э} CgeRhν <6L[bKtg2QR[s"N4X`UNN39CԚ4--{ȨQ||$=|=t;҅T-o+7 @]%,Evj7^ECc ;'ٵ#YSzl Eh-OLJDRRR{ضm瑴"4hܜѠA/_>]\\ں)**;_?DP@^^C"[dzzjPKfNcJII1\"h"JJ'N1*,,Xh'N "KKr4@8:} ?XC }^:ڢw E <"7'Νخ]S,Y2}{/\ǫW&&*ӦܰaX5 Hi܉ e>3Jve]dddرo!ÇL])3'->bz R:u%Nt8U(5QGsC̉C>} 0A݁ 6tw՝9Y aF{MH;]^>(..SlW"||`1\>4n_*DƼE~7Ϯh@ cދ?a~z]IBcuSq2wO1Xg7B/H)y%!_M^sI ~7ONY{E؏!7ZT?bҽ%"p~z+܉('7NX:F IDATxrdc L=:uGDmڍ~JPiu?". g DOM[oJw"!8c~xyyBz◧W܂ksIڋDHJ˪k1\>X]SQ׏}33=yn=iC]/B=6B,KS5OS$~vU9rD ࿯sSBwܗx-y 3"BE,7(N+@sZ}f4؏N^JDfdlv.*okmzdpM\䛮v5Zeg(k-9쟷LbJ*aPGu2yچ9YۧwHKkWR^VUD@8"-Ҵ˶)Veg4bQeXHpbb996z=wZ䐙k+]"h%D}uJ:`5| e4c\ tvAv GdڭטY]K4ŗ^f[@Ǫ=j֥ @<#N4T:LdqXQA/sKQA>3NKGͶZ쒒nkjϵ;pxvF:O.a61#VWInywh7JzCz\kVқwg΀~Qm%w1S< ߩG:r'NnN+@`킚h cZ_d`֍gv?*#6"dsm/Y<}DM ԪȝONMVa9_:mp[ fTqeO2c)楯39`׮sf>2v0lUC%%;wӚbQDD&EG0t||3S䞴'KK~ћ7qUTٺ8p"UR9%,,a뺺*;6t)yz$"?*/0&&͛-ZegopKAAz3"̋Lv޽}|=x֯͛oHCC?ƍPÅDt!C3e޾_6ν͛gF 16TR% |`n͕bA?Ǐaa &&Zܓy󍕕>3Bddr``VXX̙=tbݻ ]~O!P}c]PGj]-X䈈$GqӧKeloNjrF\>z⒣G}G\2++6m I,zr[`` 5j$ҳVc^ \PVm{L|/PkV< aumTp  O@81C" j秖NJ~N+TƻkFsF)B*1j pf+]=ʸ0?W$yn%rgU߄UUMm5ق^s@&uL$mQb7]Z*^q:V <k;(+T ;ۧ%(*?aAC_[;AˡP3aN@_=)̼e}O%WW m9F)|+^>Ӿ)iYC]()côor3z_LuZzvJT%*337ťcp_cfmlu|k'h9Trxf\u NY0yq"E&.޿wU\ʷP7v a{2`,$$v>tN^&dĜ-w/*cxi[nDqudâ=7Ѷ> IJv1jfvFz:n遦 dڍэD+Fۦgj!)%m=l*gZ*3z;̑UP1l7q3;2rDȬFv"2TgφPZWeIF}ǻ)6ȌID&]у'>B^IE/l]: /8X8ePO~ gj5y}ČgwKJ~eG6KxPKu3#3&uk/~ꯅÖ 4܅D=hފؘD$)%MD,f(%-[RR\*0h-6nw=9wd.Vha=o.JZ*LM9"߬fwXطIA^3<G@e5T#[9f[Is6q.YRRZJFVInvϰ'2gA03\X_uacu-i9rqQ!g'ܸ~C]޻L-uiY?V44f=%DGާ?g]R|E{ן;xB"Wwϭ^30)9y9LWALx5gLA1ly?{-w|kw9TT.1\e~rmw&ȼC׏0Ye[gE),'|+^ `ͮh2 "V%>,NfV3{G{o~vmdHDr'u~YMO!.y\ȹ[Yhr|[gUVfKi1c hmч{>GDͷ™"]DTZ S[nRLω_?r=xH箻s:2z{(xیG'Tr_O"@Uܚ`+k~v^ Yd5l6꯴/f=qnF2pX۠bnHKTVf8k?qKO|bq?b2 </\DV[ 6O4_bc`~6o΅4HJY5Dj X DXGlRgqt `8=p 2@8@8U µ#p NU8ѣ E/cJ{cwKR =Xy ;ͣje8 ҥt˖-===633ڵkXX3|||ڴiΝ;:t655r 8...O>eUPPprrBhpNAdQ$}}}srr8=zǯ\}7Y---}~)''S^^ݻl6[WW.]mۖlvhhk5@+!@F8.h׮ݻwPZZSl^vݹsg"`$77W^^2bĈm۶0oرٳg222%%%L@^^bQQlrϞ=#F@ԫHPGF_qA"E_ziӦϟsȐ, QUUMIIgdd[S!!!f͒YrebSP"ɓ'FONN9m!|9L`hii)((;[nɒ%ǏǗ P'i>|8iҤFqo۶Ç/ҒtӧOa>ػw۷oX_*@ᩩSL!,fyVXYTTtȑ޽{BppիW_|!_ѪU,YPXXhѢ%K0)b(..X,Vpp0npDue={nܸalld[۷ooҤѣlƍt}rŋRRR:tȖ3K":v영A=zLq4V@:q@հBYqO+x#p5 p @ @wx#P Ka &|W.)X9Ckد 5QWAR$3&jW1쑫X,@ FU_dP7SBd8@2q=z* E>\Dp8@2}*ׅP  :bԑ7Fz @$1oT}#ӐIE; H ~[@Ujar @E&)@Ì z[a`8@ +@Ch l@@J@,׎  $b2hP h}Pl;B`x/侧8fևp8 .9@82" U `@81CpBµ#"s~x Ah-P,iA8pȠ;@XΥd~۷"4x`` Up+;KK6UUhN .`#hzhzp  g^jh(]8Lvv]GazwZ$E'^ #PU妮PFfN׮[ybK[ddX&&kx]X,g!b""hj.nȅ7Bnjq: ˿v-j̜͗_]go~j0τ mm.S⯿GD_EX%3"W8_ -N'UXmlKHX[ߦwꖖ[pf_!8۳A>}ڔ6Sz׮]{l lwrӧ۷k quꔿݻ<<\ș3/ǞU-Xp3?^|d7/^|ХKÇw(=Çw%--]"a+ُߣU*h˛7(.>2@AMe߾Gp޼>u7̘]UUqc5k|ykf3gi$-]glddn}ȤU}][WtBVVjo㴵+1gBaa MJπA5L>>utXzz>>׈dӦ&&*;j{zڢD$--S(c.pƌL=ٳaލ4th7==2kRMvG jz"lwIz6fN㈔L"JI|4MOlJ IDAT={>~[NId*nݺС'əֆ7ԩ9}ь$*##f7W ?4*:%%lV?pqqts0pHk..11oUSkKwo(**:zʀ8#*jWcb~*L355Y̨'''>zs˞x7=;JN]ZZrʔn?[r)S!8ݻb@{{smjj s8o驿zmcc(l6%\СQvvKC"Qyb3lb]SSϟSJJmVׅ$GB8:NڝA/|T zm` /hf^G__nڴyL]^BMOAMOԝ{l_ϹOO1Vhbbҵo}-_n7a± JysG>VhZl6;' ;@AAw! 5o]LMJJXϻ7nWn_8/9] hLʕQl]X 5yZP+U& vn &M:nmm\@4=X9yrӉmh/Y2ܹלO\ʇ Eڍs93f gSirELLRSӧONNz>%L IKKNxqcq]++YY)w] ?\ ~dQ~[W7MKK\~XИ<B%^MOAMϟն/[`jzjOhĉK_}76$LnndLHcnV\\vNN.̻RRR>>טRRRD4h˗O,)Ør^Qnn|bWKϘ |ⓤdzzjtgfDӼFQRRNRRmPv+UqSߦW--˾E!7= Vnܘm ^ڳBvقUs::%k̘1w.8谘~"Zbk@ܹ.\ve]dddر:wn $kޔ)¢ߙaө.ué~ShzRpcX襩˜-X\]GUGM+xVel~" oJTPZ\lz·>g e|pZD@,D"p&oFׄ=H ڻ>]uk.F:^&|M7n1e.hP7D/߿_Xsbo?Bнv1Yl/H$ѪU֯ڻwccD;BYƽ{ 6UiS-LrrO)e\3ztЉ Y~GED$}Pju1z굳k׭))C%#3z-W W9u_CcqI ;))]Ii>ӱ,%iiپ\f/ɓ"JH1f 1ŮXq]Qq~NNAQQE%~~Q6]+)9ׯoe,3.99WUMWwكa&Q+KX^^'wo-6%">آzF$vQ+Q#NҡDZwݲeĉ/kaDDGܹ_ yhРv,G#66ǎM%K-iG—-}SZZE_-~k-?t޽FFn2ܼ˙1c̣S[ ==E ڵC}ouT;?hȐ^eK}w/]: 33t`# @9r@"sƍFDDzr ee jFD1`]"ݼd[)Fӧ{(+ˎС'Zip~ctq#ʀ@Ӧٴjq"{=+M\ 06n=yrɓр?ʘbHzݲTZ˯xYlv^ƂEdž\8Sڰ8<<۶w;fs<`)m8r7o.]r&í `k_Op!}{ݝ;ϛw~߾LEE%νvp06 kVGCC˗l33]AS!++qWSrVV~j&Q[;: JJJ6mZjbұo-ZH}|GZZ? EOO3͟d` ߯_hbZz02~oLoؠ# EE FD$.m~ׯ y\t>}Z33w<#XX5kf38}]MڿZukָ=z%><8n_p ցnzkemڛE{#E.}-$[ތё/jr;fm[Oϗ*ׯڱc͛W{hQccs^?x֯͛oM{Clbߺ&7ŀ_/^_$uydDgyyG)Q_ FD,,,;3pƌ*ܣ]]l);q X%%ϟSVұcKZibqƣG͙G8]]ٽ ^`%̜#8KΛ-,ܽ9nkKD߳%%%SS.]e.tfo8xEF&Ƙhd>}:QQ)1M..ysI+1񫼼祏5==i/2Sx6l,ޕ+c͚N|8E 5V##^30QQV]?{vw%ʲl6NN'w7S^зY+?ԩΝ;?;. 8tt~8oݡVhh-P^w} KB/4\mڬ=gۛ7Si/ύLnFKR 4>$ܜ$$s7cjj2[(79ƍ/ v(hP 4\]nuro"<<:DM7#U1.Ē+Vl 32R;w…k.8Q;֖ܼ9uMDffDdaт)m?^51Q6m {xizN71= ~*qZ-N%n^|rτ-,,_h\m6pJYXxo.^v~Oud˂'H@EqzתjjkqƐ !uC߼_?3-ٟ}ѡԷ nɏHug|2k?_9ooW0 \*_I_%^۳SWUtt XbP!D}JnөCzXY[bOЀI&{-m"\jϽw!ޓ7ٰ"*oTl+4Z&MQ(KU^[}qxMU\k~+X2SWҷ"!zڢVLXBږVJ˜y  ͎AͼsEsM !_t֧)5׆LU9`K]OkSx2v K,L19$Rd iJZMm}WP%'\9O}>uΈ9ٙ'/ 8%㢥ycQ:O|)R/6Aq}!īFyjlD"/5 wҨG|%/7q?WؿSWI3X<ލy9Q;6hBZF=5_{B^>{ן\MMzTƾG}?n GM2R_~_-We_WcڗBTrr{ BcTrxoѰu~W]+\7~5K+]b^Oիܻwk6xgԜFmzh/EScYuQy/3?M}ե_ g hw¥~g\cyjUz M;~o!hHJ9x`zRSS^\ի8pѣGQƉ'T3\vJrϞ=...vvvRemmeU)))=zprrRsAJ(X8Mvvv׮]SMJǏ ̓^f͚ݸqCTmVTϐqFxaiiYp833S㿴W@e\\\f \`ĉ/]f͚uJsoڽ{L2|pRYjs)+WH;RYNgϦ/[rʪ0wvYakkoAJ(p8P*ӧOoӦBP}bQ=zVX1fR9lذy/_]4>hFiIwE8RTl qV¤vvv cjOQ* 6TZ;C*0,,ud„M\RW=z,ͦ=E׫Wʕ+ tuuwի...dd|BDEEIOV^T*G1]*QF͝;7777((VZDEE !"""Ti׮jS}w !-THHH~;U\\祕Q*>T-NZthT^ GBB2eٙp@8T.Kh;qDӦM׭[oɓ']]]7oޜ*pʕ=zOJJjݺ (ޫO>]rĉ~ںN:ҭ))){}7o, ϣ]```_{wI kk} ,}"6+k.\ްaóg֯_„ 5٭]N: t?mّ֬#GT7٢4ԧԬY֭[FGG_U\@i$ m۶M.[R${(ihK~*3f̘#F\z533sҥN>_+䠠 {{{S n݊2d"55UѫW FDDmڴ]]KQ 9 NG8(fxz@8 p~\"@a8  V <E{sVw/I`EA>x۠+L~QUk#aW^iU'§LF `bWDoH$PbSB!o,H8 ` d$Iw& e P/fErF[`CƆ)(|C9 y19jp-K9{EH~x ӗ^jDyb8 8p@8 `[#o; !tpH 1pRpb`@7_e,%ʀg>+B$XJ*9|h=A/pY p0Ap@8@8H;B8"õjp`>[ҲhG8@g~ּm.O?~ֻtpgcy˖sԳΝf|.ԫ7];w^z]yUz}EDg@o66geK˗cI>v 4f-igҹ/²uk1ťb߾^?|=x84TZtmYX|֦z:WU9"J+d:t2<^jUոqդV-G =|]6ЮCvvn c32i4 }{37ǩݣG#coEsԙ~Grqq)-[ ڲ]>X gg7##&ӳJ\\Çɋ;y28<<>11]x[?jMg ݑDvv;w\]'?{t\Ry11IkWW5gΡ+NƦiSk֬_~E~B)~gYE\jȐ֫W7V:3dHd@+-&O*ݺ-:G>͝+Tl_tmkinݠ7c X˨TZȚxx8Ī޾iS~Ts神=ݿC̥7h|NΗ6taGYu=} ,Zߊthv޸q]֯?;9w=cFLܐh:Ə;||ګ߻PZƍ]RݨW?hݑ#7ߦ!w>cv~U.ShrG||Ç4~]JezzvZZ`~iow~أM(Xqvкú׷iSZt=F"j9|<ĉ]7zp!m~\ĉ]U7TI[Iq#&;;ٹBt<tNر;.@嗯 X ƽ.[ B?lpgES{$*W{պ.آtoeX:t]c5&&?OzX35o~~S ܐXi%$ oɸhll{\Λ &ߛ pC"rY}eʕ9w_p7nEOz x}OXn߾UOl^z PZ10 PqY`:UF k`wp! k`wp! k`wp! #XLC#%3=t*!.QIḙ 8@>@,3'|#@h0ݷkŀՁp*G {L@8)?<ѹ@8@Ȧ(qhoE@Zt^wKb'85jayִg0dJL/Џs1|ޗpY%t Fj p`ɀXL9o2ps9XnݒSl1է`~֞}$}c$$|֩zt3M {77PlNVw6튵ެ0Cc&n[tJ/!];>օRҾ\>k3mk_ug1r9EX-Ms{rQK+s$+4,,<x !Ur+_iޣ8f%)fl>tJĭ 8U.4WB^S ÁRvqHcTO#/b|j~~U}6s{Sl;R UUOZ5WQm6!{g%:/:gHiU!.@mc^kf{A{|ۯNm]x=\G0}l,UVU,7;kw|{ v#h*P/A}aӑ}Mag-iҾy&?7iS uvﺷF|_~?W:A4!Ħo?~~Kz|-?qSIo| 8@SF%_7︅.5v4Ve+V(:Wdj2c(G"1lLGuEummlۿ;<-9A5}o_T[*m{z)ٟ>{lB[rv*v0R9kos=xYYۼ5TuݿXF=dҢBE46~^ƾKF0/7m3v޾_<^>[y')uweSi l**飘{u^- 8Gƭ l'qW4Or(%gd D~oH$dRw"adnvRHI R'=9Ag BdJ_7zSc#BygvY0Sƒ!_}Ƭ/=4k:gDT՜ۗ΋L; 7.a(!f|x@zkhl~%<H GL>(ob g/@PNW9ߵ @8L' h+NZE '<@8L<"S JZD(s6#B;J&fD20~PAH(ƒD]?( `X` $s:FX`d`Ak0Z@D pX@D p@X`nA灑d BP @8t ! @8 ?C$ *A804#GKK@2@?"p0d@,fAdWڿ RC|pU< KB%H T}BuK7ݬ5V6nݙz3G5aˀ;U5zQE o#7龦W"Tk}hIRwgܭ^)StSK)Q?G5ԠMXqGN<<68=$=zugޮ]C \:ҵuHkq!B2z5?b`uv;B:C7v9:Wp5|8W3^Y*uDo-+߲E Aι?FԜUF$Q5EBR_wpڧնq`:5vgP[ !裠>MII ,K˓ s U>Wo@~˧.7Mzrukдi,E$$!DVyf !'T3S !5]izqf^NU(¦r˺cYelejRB\hy ,vs B&,Zm\sgiWni[cjoz8x~),*ji"SQJfk:df5g*QKz94%K;KҥUx+.lZJVgYЉ5g֬zekT@:DvuV嬤ߞBdeȬe;TԘSgiWޯU+;c[Bu/۸ٞ=6)+˜9eePnn4j韛'UǺG.T8SCZPڅWd(%r !=y* ;PZYIodPsIw=<۩ro[W~t O}sjF+mT[ N50r0xPjTh[ѶF3ßq\1J*e- ̩ή]dӸ=qN Ri,*DvvR_}U:5tbhڍ4E"zmtoR,!DliOy6J.+|r!Z͡7:&,eN}lWn_]y}= !>t}{yiy^¯Ԓ~77v&$QF4>gV<١{Љ 0G6uό̸Ѝd@2O5*s3]GIsf\|\\>va fkA2E;@8 ogɄ\b |X P `@0I<3h  " h ^"0@F !b@8@D Db@8P@J(NW9-mw %0N:p@P  8" H LO ٮC|D# h3 B pAATAʀ@61O^dt(0a| 00 sdAd7J5/IENDB`percona-xtradb-cluster-galera/gcs/doc/GCS_connection_states.txt0000644000000000000000000001177412247075736025164 0ustar rootroot00000000000000 GCS CONNECTION STATES (from the application viewpoint) Since GCS is a library to be utilized by an application, it has to export some sort of a Group handle to the application. So far this handle was attempted to have a connection-oriented socket semantics. Reasons for that being: 1) It is better to expand on a well understood and established concept rather than invent something. 2) The whole idea of GCS is to avoid exporting Group Communication concepts to application. It is much easier to work with a socket. 3) The main point of the Group is a linearly serialized stream of messages with a Group being a single source/sink of the messages. This effectively makes Group communication a point-to-point connection. Initially this seemed rather plain to me: when we're part of the primary configuration, we can send and receive messages. When not - all calls just return -ENOTCONN. However, there are certain aspects to GC that make its interpretation as a socket not so straightforward. These are configuration changes, primary/non-primary configurations and state snapshot. For the demo these were deemed not essential and were not addressed. As we're moving on this has to be addressed since we have to settle the API the sooner the better. Basically it goes this way. Whenever DBMS process joins the primary configuration any other way than by configuration change from the previous primary configuration, it has to take a state snapshot (be it incremental or complete) and only after that it can be considered a part of the quorum. It could be done the following way: 1) Process receives configuration change message and decides whether it needs to take a state snapshot. 2) If "yes" then it sends snapshot request message. One of quorum members is dispatches snapshot to the joiner. 3) When the snapshot is complete, the joiner sends the final join message. (maybe "join" is not a good term here, but I'll use it just for now) 4) When the join message is received, every configuration member puts the process in the quorum member list. Only now the process is a full-fledged member of the Group. Note that I've been speaking of two separate memberships here: "configuration" and "quorum". A process is a member of the configuration as soon as it receives a configuration change message (bear in mind, I'm assuming Spread as a communication backend now), so it can receive and theoretically - send messages. However, it does not have the up-to-date state and in case of DBMS: 1) Cannot really apply messages (write sets in our case). 2) Cannot give a snapshot in case of another configuration change, so it cannot be used in quorum calculation. All this makes the process a sort of the "2nd grade" configuration member until it gets a snapshot. The problem is that every configuration member has to be notified when the snapshot is complete, hence we need this "join" message. As a result, state machine for the GCS connection will get one more state: own JOIN message received +-------------------------+ ______ | V V \ gcs_open() +----------+ +---------------+ | conf. -------------->| GCS_OPEN | | GCS_CONNECTED | | change +----------+ +---------------+ | to PRIM ^ | \______/ +-------------------------+ own LEAVE message received, conf. change to NON-PRIM Rough explanation: GCS_OPEN (perhaps should be split into OPEN_PRIM and OPEN_NON_PRIM). Only snapshot request and join messages are allowed. Attempt to send anything else results in -ENOTCONN. Attempt to send join message when in non-primary configuration should result in -ECONNREFUSED. GCS_CONNECTED. Attempt to send snapshot request or join message results in -EISCONN. Application messages are sent alright. When GCS_CONNECTED->GCS_OPEN change happens all pending GCS calls return -ECONNRESET. So GCS API is about to get more complicated. And here we have two alternatives: 1) Implicitly expose GCS connection state to the application through those error codes. Application will have to keep its own track of GCS connection state and not forget to send join message. In this case API can stay the same, but it's usage will get a bit more complicated. 2) Application can provide request_snapshot(), send_snapshot() and receive_snapshot() callbacks to the library. Then all this could be handled by the library and application would not have to know anything about snapshot request or join messages. This won't simplify the application much though: callbacks will have to be able to communicate and synchronize with other threads, since in this case application will have no control on when the send or receive callback is called. This also would mean additional 4 parameters for gcs_open() (3 callbacks + context) and make GCS connection much less of a "socket". percona-xtradb-cluster-galera/gcs/src/SConscript0000644000000000000000000000414012247075736022226 0ustar rootroot00000000000000# Import('env') # Clone environment as we need to tune compilation flags libgcs_env = env.Clone() # Backends (TODO: Get from global options) libgcs_env.Append(CPPFLAGS = ' -DGCS_USE_GCOMM') gcs4garb_env = libgcs_env.Clone() libgcs_sources = Split(''' gcs_params.c gcs_conf.c gcs_fifo_lite.c gcs_msg_type.c gcs_comp_msg.c gcs_sm.c gcs_backend.c gcs_dummy.c gcs_act_proto.c gcs_defrag.c gcs_state_msg.c gcs_node.c gcs_group.c gcs_core.c gcs_fc.c gcs.c gcs_gcomm.cpp ''') #libgcs_env.VariantDir('.gcs', '.', duplicate=0) libgcs_env.StaticLibrary('gcs', libgcs_sources) # TODO: How to tell scons portably that C++ linker should be used # and program should be linked statically gcs_test_env = libgcs_env.Clone() gcs_test_env.Prepend(LIBS = File('#/galerautils/src/libgalerautils.a')) gcs_test_env.Prepend(LIBS = File('#/galerautils/src/libgalerautils++.a')) gcs_test_env.Prepend(LIBS = File('#/gcomm/src/libgcomm.a')) gcs_test_env.Prepend(LIBS = File('#/gcache/src/libgcache.a')) gcs_test_env.Prepend(LIBS = File('#/gcs/src/libgcs.a')) gcs_test_env.Program(target = 'gcs_test', source = 'gcs_test.c', LINK = libgcs_env['CXX']) SConscript('unit_tests/SConscript') # env.Append(LIBGALERA_OBJS = libgcs_env.SharedObject(libgcs_sources)) gcs4garb_env.Append(CPPFLAGS = ' -DGCS_FOR_GARB') garb_obj_dir = '.garb' gcs4garb_env.VariantDir(garb_obj_dir, '.', duplicate = 0) #garb_objects = [os.path.splitext(src)[0] + '_garb' + # env['OBJSUFFIX'] for src in libgcs_sources] garb_sources = [ garb_obj_dir + '/' + src for src in libgcs_sources ] gcs4garb_env.StaticLibrary('gcs4garb', garb_sources) Clean('.', garb_obj_dir) percona-xtradb-cluster-galera/gcs/src/gcs.c0000644000000000000000000016444712247075736021155 0ustar rootroot00000000000000/* * Copyright (C) 2008-2013 Codership Oy * * $Id: gcs.c 3336 2013-10-28 07:41:56Z teemu $ */ /* * Top-level application interface implementation. */ #include #include #include #include #include #include #include #include "gcs_priv.h" #include "gcs_params.h" #include "gcs_fc.h" #include "gcs_seqno.h" #include "gcs_core.h" #include "gcs_fifo_lite.h" #include "gcs_sm.h" #include "gcs_gcache.h" #define min(a,b) (a <= b ? a : b) const char* gcs_node_state_to_str (gcs_node_state_t state) { static const char* str[GCS_NODE_STATE_MAX + 1] = { "NON-PRIMARY", "PRIMARY", "JOINER", "DONOR", "JOINED", "SYNCED", "UNKNOWN" }; if (state < GCS_NODE_STATE_MAX) return str[state]; return str[GCS_NODE_STATE_MAX]; } const char* gcs_act_type_to_str (gcs_act_type_t type) { static const char* str[GCS_ACT_UNKNOWN + 1] = { "TORDERED", "COMMIT_CUT", "STATE_REQUEST", "CONFIGURATION", "JOIN", "SYNC", "FLOW", "SERVICE", "ERROR", "UNKNOWN" }; if (type < GCS_ACT_UNKNOWN) return str[type]; return str[GCS_ACT_UNKNOWN]; } static const long GCS_MAX_REPL_THREADS = 16384; typedef enum { GCS_CONN_SYNCED, // caught up with the rest of the group GCS_CONN_JOINED, // state transfer complete GCS_CONN_DONOR, // in state transfer, donor GCS_CONN_JOINER, // in state transfer, joiner GCS_CONN_PRIMARY, // in primary conf, needs state transfer GCS_CONN_OPEN, // just connected to group, non-primary GCS_CONN_CLOSED, GCS_CONN_DESTROYED, GCS_CONN_ERROR, GCS_CONN_STATE_MAX } gcs_conn_state_t; #define GCS_CLOSED_ERROR -EBADFD; // file descriptor in bad state static const char* gcs_conn_state_str[GCS_CONN_STATE_MAX] = { "SYNCED", "JOINED", "DONOR/DESYNCED", "JOINER", "PRIMARY", "OPEN", "CLOSED", "DESTROYED", "ERROR" }; static bool const GCS_FC_STOP = true; static bool const GCS_FC_CONT = false; /** Flow control message */ struct gcs_fc_event { uint32_t conf_id; // least significant part of configuraiton seqno uint32_t stop; // boolean value } __attribute__((__packed__)); struct gcs_conn { long my_idx; long memb_num; char* my_name; char* channel; char* socket; gcs_conn_state_t state; gu_config_t* config; bool config_is_local; struct gcs_params params; gcache_t* gcache; gcs_sm_t* sm; gcs_seqno_t local_act_id; /* local seqno of the action */ gcs_seqno_t global_seqno; /* A queue for threads waiting for replicated actions */ gcs_fifo_lite_t* repl_q; gu_thread_t send_thread; /* A queue for threads waiting for received actions */ gu_fifo_t* recv_q; ssize_t recv_q_size; gu_thread_t recv_thread; /* Message receiving timeout - absolute date in nanoseconds */ long long timeout; /* Flow Control */ gu_mutex_t fc_lock; uint32_t conf_id; // configuration ID long stop_sent; // how many STOPs - CONTs were sent long stop_count; // counts stop requests received long queue_len; // slave queue length long upper_limit; // upper slave queue limit long lower_limit; // lower slave queue limit long fc_offset; // offset for catchup phase gcs_conn_state_t max_fc_state; // maximum state when FC is enabled long stats_fc_sent; // FC stats counters long stats_fc_received; // gcs_fc_t stfc; // state transfer FC object /* #603, #606 join control */ bool volatile need_to_join; gcs_seqno_t volatile join_seqno; /* sync control */ bool sync_sent; /* gcs_core object */ gcs_core_t* core; // the context that is returned by // the core group communication system }; // Oh C++, where art thou? struct gcs_recv_act { struct gcs_act_rcvd rcvd; gcs_seqno_t local_id; }; struct gcs_repl_act { struct gcs_action* action; gu_mutex_t wait_mutex; gu_cond_t wait_cond; }; /*! Releases resources associated with parameters */ static void _cleanup_params (gcs_conn_t* conn) { if (conn->config_is_local) gu_config_destroy(conn->config); } /*! Creates local configuration object if no external is submitted */ static long _init_params (gcs_conn_t* conn, gu_config_t* conf) { long rc; conn->config = conf; conn->config_is_local = false; if (!conn->config) { conn->config = gu_config_create(""); if (conn->config) { conn->config_is_local = true; } else { rc = -ENOMEM; goto enomem; } } rc = gcs_params_init (&conn->params, conn->config); if (!rc) return 0; _cleanup_params (conn); enomem: gu_error ("Parameter initialization failed: %s", strerror (-rc)); return rc; } /* Creates a group connection handle */ gcs_conn_t* gcs_create (gu_config_t* const conf, gcache_t* const gcache, const char* const node_name, const char* const inc_addr, int const repl_proto_ver, int const appl_proto_ver) { gcs_conn_t* conn = GU_CALLOC (1, gcs_conn_t); if (!conn) { gu_error ("Could not allocate GCS connection handle: %s", strerror (ENOMEM)); return NULL; } if (_init_params (conn, conf)) { goto init_params_failed; } if (gcs_fc_init (&conn->stfc, conn->params.recv_q_hard_limit, conn->params.recv_q_soft_limit, conn->params.max_throttle)) { gu_error ("FC initialization failed"); goto fc_init_failed; } conn->state = GCS_CONN_DESTROYED; conn->core = gcs_core_create (conf, gcache, node_name, inc_addr, repl_proto_ver, appl_proto_ver); if (!conn->core) { gu_error ("Failed to create core."); goto core_create_failed; } conn->repl_q = gcs_fifo_lite_create (GCS_MAX_REPL_THREADS, sizeof (struct gcs_repl_act*)); if (!conn->repl_q) { gu_error ("Failed to create repl_q."); goto repl_q_failed; } size_t recv_q_len = gu_avphys_bytes() / sizeof(struct gcs_recv_act) / 4; gu_debug ("Requesting recv queue len: %zu", recv_q_len); conn->recv_q = gu_fifo_create (recv_q_len, sizeof(struct gcs_recv_act)); if (!conn->recv_q) { gu_error ("Failed to create recv_q."); goto recv_q_failed; } conn->sm = gcs_sm_create(1<<16, 1); if (!conn->sm) { gu_error ("Failed to create send monitor"); goto sm_create_failed; } conn->state = GCS_CONN_CLOSED; conn->my_idx = -1; conn->local_act_id = GCS_SEQNO_FIRST; conn->global_seqno = 0; conn->fc_offset = 0; conn->timeout = GU_TIME_ETERNITY; conn->gcache = gcache; conn->max_fc_state = conn->params.sync_donor ? GCS_CONN_DONOR : GCS_CONN_JOINED; gu_mutex_init (&conn->fc_lock, NULL); return conn; // success sm_create_failed: gu_fifo_destroy (conn->recv_q); recv_q_failed: gcs_fifo_lite_destroy (conn->repl_q); repl_q_failed: gcs_core_destroy (conn->core); core_create_failed: fc_init_failed: _cleanup_params (conn); init_params_failed: gu_free (conn); gu_error ("Failed to create GCS connection handle."); return NULL; // failure } long gcs_init (gcs_conn_t* conn, gcs_seqno_t seqno, const uint8_t uuid[GU_UUID_LEN]) { if (GCS_CONN_CLOSED == conn->state) { return gcs_core_init (conn->core, seqno, (const gu_uuid_t*)uuid); } else { gu_error ("State must be CLOSED"); if (conn->state < GCS_CONN_CLOSED) return -EBUSY; else // DESTROYED return -EBADFD; } } /*! * Checks if we should freak out on send/recv errors. * Sometimes errors are ok, e.g. when attempting to send FC_CONT message * on a closing connection. This can happen because GCS connection state * change propagation from lower layers to upper layers is not atomic. * * @param err error code returned by send/recv function * @param warning warning to log if necessary * @return 0 if error can be ignored, original err value if not */ static long gcs_check_error (long err, const char* warning) { switch (err) { case -ENOTCONN: case -ECONNABORTED: if (NULL != warning) { gu_warn ("%s: %d (%s)", warning, err, strerror(-err)); } err = 0; break; default:; } return err; } static inline long gcs_send_fc_event (gcs_conn_t* conn, bool stop) { struct gcs_fc_event fc = { htogl(conn->conf_id), stop }; return gcs_core_send_fc (conn->core, &fc, sizeof(fc)); } /* To be called under slave queue lock. Returns true if FC_STOP must be sent */ static inline bool gcs_fc_stop_begin (gcs_conn_t* conn) { long err = 0; bool ret = (conn->stop_count <= 0 && conn->stop_sent <= 0 && conn->queue_len > (conn->upper_limit + conn->fc_offset) && conn->state <= conn->max_fc_state && !(err = gu_mutex_lock (&conn->fc_lock))); if (gu_unlikely(err)) { gu_fatal ("Mutex lock failed: %d (%s)", err, strerror(err)); abort(); } conn->stop_sent += ret; return ret; } /* Complement to gcs_fc_stop_begin. */ static inline long gcs_fc_stop_end (gcs_conn_t* conn) { long ret; gu_debug ("SENDING FC_STOP (local seqno: %lld, fc_offset: %ld)", conn->local_act_id, conn->fc_offset); ret = gcs_send_fc_event (conn, GCS_FC_STOP); if (ret >= 0) { ret = 0; conn->stats_fc_sent++; } else { conn->stop_sent--; assert (conn->stop_sent >= 0); } gu_mutex_unlock (&conn->fc_lock); ret = gcs_check_error (ret, "Failed to send FC_STOP signal"); return ret; } /* To be called under slave queue lock. Returns true if FC_CONT must be sent */ static inline bool gcs_fc_cont_begin (gcs_conn_t* conn) { long err = 0; bool queue_decreased = (conn->fc_offset > conn->queue_len && (conn->fc_offset = conn->queue_len, true)); bool ret = (conn->stop_sent > 0 && (conn->lower_limit >= conn->queue_len || queue_decreased) && conn->state <= conn->max_fc_state && !(err = gu_mutex_lock (&conn->fc_lock))); if (gu_unlikely(err)) { gu_fatal ("Mutex lock failed: %d (%s)", err, strerror(err)); abort(); } conn->stop_sent -= ret; // decrement optimistically to allow for parallel // recv threads return ret; } /* Complement to gcs_fc_cont_begin() */ static inline long gcs_fc_cont_end (gcs_conn_t* conn) { long ret; assert (GCS_CONN_DONOR >= conn->state); gu_debug ("SENDING FC_CONT (local seqno: %lld, fc_offset: %ld)", conn->local_act_id, conn->fc_offset); ret = gcs_send_fc_event (conn, GCS_FC_CONT); if (gu_likely (ret >= 0)) { ret = 0; } conn->stop_sent += (ret != 0); // fix count in case of error gu_mutex_unlock (&conn->fc_lock); ret = gcs_check_error (ret, "Failed to send FC_CONT signal"); return ret; } /* To be called under slave queue lock. Returns true if SYNC must be sent */ static inline bool gcs_send_sync_begin (gcs_conn_t* conn) { if (gu_unlikely(GCS_CONN_JOINED == conn->state)) { if (conn->lower_limit >= conn->queue_len && !conn->sync_sent) { // tripped lower slave queue limit, send SYNC message conn->sync_sent = true; return true; } #if 0 else { gu_info ("Not sending SYNC: state = %s, queue_len = %ld, " "lower_limit = %ld, sync_sent = %s", gcs_conn_state_str[conn->state], conn->queue_len, conn->lower_limit, conn->sync_sent ? "true" : "false"); } #endif } return false; } static inline long gcs_send_sync_end (gcs_conn_t* conn) { long ret = 0; gu_debug ("SENDING SYNC"); ret = gcs_core_send_sync (conn->core, 0); if (gu_likely (ret >= 0)) { ret = 0; } else { conn->sync_sent = false; } ret = gcs_check_error (ret, "Failed to send SYNC signal"); return ret; } static inline long gcs_send_sync (gcs_conn_t* conn) { if (gcs_send_sync_begin (conn)) { return gcs_send_sync_end (conn); } else { return 0; } } /*! * State transition functions - just in case we want to add something there. * @todo: need to be reworked, see #231 */ static bool gcs_shift_state (gcs_conn_t* conn, gcs_conn_state_t new_state) { static const bool allowed [GCS_CONN_STATE_MAX][GCS_CONN_STATE_MAX] = { // SYNCED JOINED DONOR JOINER PRIM OPEN CLOSED DESTR { false, true, false, false, false, false, false, false }, // SYNCED { false, false, true, true, false, false, false, false }, // JOINED { true, true, false, false, false, false, false, false }, // DONOR { false, false, false, false, true, false, false, false }, // JOINER { true, true, true, true, true, true, false, false }, // PRIMARY { true, true, true, true, true, false, true, false }, // OPEN { true, true, true, true, true, true, false, false }, // CLOSED { false, false, false, false, false, false, true, false } // DESTROYED }; gcs_conn_state_t old_state = conn->state; if (!allowed[new_state][old_state]) { if (old_state != new_state) { gu_warn ("Shifting %s -> %s is not allowed (TO: %lld)", gcs_conn_state_str[old_state], gcs_conn_state_str[new_state], conn->global_seqno); } return false; } gu_info ("Shifting %s -> %s (TO: %lld)", gcs_conn_state_str[old_state], gcs_conn_state_str[new_state], conn->global_seqno); conn->state = new_state; return true; } static void gcs_become_open (gcs_conn_t* conn) { gcs_shift_state (conn, GCS_CONN_OPEN); } static long gcs_set_pkt_size (gcs_conn_t *conn, long pkt_size) { if (conn->state != GCS_CONN_CLOSED) return -EPERM; // #600 workaround long ret = gcs_core_set_pkt_size (conn->core, pkt_size); if (ret >= 0) { conn->params.max_packet_size = ret; gu_config_set_int64 (conn->config, GCS_PARAMS_MAX_PKT_SIZE, conn->params.max_packet_size); } return ret; } static long _release_flow_control (gcs_conn_t* conn) { int err = 0; if (gu_unlikely(err = gu_mutex_lock (&conn->fc_lock))) { gu_fatal ("Mutex lock failed: %d (%s)", err, strerror(err)); abort(); } if (conn->stop_sent) { assert (1 == conn->stop_sent); conn->stop_sent--; err = gcs_fc_cont_end (conn); } else { gu_mutex_unlock (&conn->fc_lock); } return err; } static void gcs_become_primary (gcs_conn_t* conn) { if (!gcs_shift_state (conn, GCS_CONN_PRIMARY)) { gu_fatal ("Protocol violation, can't continue"); gcs_close (conn); abort(); } long ret; if ((ret = _release_flow_control (conn))) { gu_fatal ("Failed to release flow control: %ld (%s)", ret, strerror(ret)); gcs_close (conn); abort(); } } static void gcs_become_joiner (gcs_conn_t* conn) { if (!gcs_shift_state (conn, GCS_CONN_JOINER)) { gu_fatal ("Protocol violation, can't continue"); assert (0); abort(); } if (gcs_fc_init (&conn->stfc, conn->params.recv_q_hard_limit, conn->params.recv_q_soft_limit, conn->params.max_throttle)) { gu_fatal ("Becoming JOINER: FC initialization failed, can't continue."); abort(); } gcs_fc_reset (&conn->stfc, conn->recv_q_size); gcs_fc_debug (&conn->stfc, conn->params.fc_debug); } // returns 1 if accepts, 0 if rejects, negative error code if fails. static long gcs_become_donor (gcs_conn_t* conn) { if (gcs_shift_state (conn, GCS_CONN_DONOR)) { long err = 0; if (conn->max_fc_state < GCS_CONN_DONOR) { err = _release_flow_control (conn); } return (0 == err ? 1 : err); } gu_warn ("Rejecting State Transfer Request in state '%s'. " "Joiner should be restarted.", gcs_conn_state_str[conn->state]); if (conn->state < GCS_CONN_OPEN){ ssize_t err; gu_warn ("Received State Transfer Request in wrong state %s. " "Rejecting.", gcs_conn_state_str[conn->state]); // reject the request. // error handling currently is way too simplistic err = gcs_join (conn, -EPROTO); if (err < 0 && !(err == -ENOTCONN || err == -EBADFD)) { gu_fatal ("Failed to send State Transfer Request rejection: " "%zd (%s)", err, (strerror (-err))); assert (0); return -ENOTRECOVERABLE; // failed to clear donor status, } } return 0; // do not pass to application } static long _release_sst_flow_control (gcs_conn_t* conn) { long ret = 0; do { if (conn->stop_sent > 0) { ret = gcs_send_fc_event (conn, GCS_FC_CONT); conn->stop_sent -= (ret >= 0); } } while (ret < 0 && -EAGAIN == ret); // we need to send CONT here at all costs ret = gcs_check_error (ret, "Failed to release SST flow control."); return ret; } static void gcs_become_joined (gcs_conn_t* conn) { long ret; if (GCS_CONN_JOINER == conn->state) { ret = _release_sst_flow_control (conn); if (ret < 0) { gu_fatal ("Releasing SST flow control failed: %ld (%s)", ret, strerror (-ret)); abort(); } conn->timeout = GU_TIME_ETERNITY; } /* See also gcs_handle_act_conf () for a case of cluster bootstrapping */ if (gcs_shift_state (conn, GCS_CONN_JOINED)) { conn->fc_offset = conn->queue_len; conn->need_to_join = false; gu_debug("Become joined, FC offset %ld", conn->fc_offset); /* One of the cases when the node can become SYNCED */ if ((ret = gcs_send_sync (conn))) { gu_warn ("Sending SYNC failed: %ld (%s)", ret, strerror (-ret)); } } else { assert (0); } } static void gcs_become_synced (gcs_conn_t* conn) { gcs_shift_state (conn, GCS_CONN_SYNCED); conn->sync_sent = false; gu_debug("Become synced, FC offset %ld", conn->fc_offset); conn->fc_offset = 0; } /* to be called under protection of both recv_q and fc_lock */ static void _set_fc_limits (gcs_conn_t* conn) { /* Killing two birds with one stone: flat FC profile for master-slave setups * plus #440: giving single node some slack at some math correctness exp.*/ double fn = conn->params.fc_master_slave ? 1.0 : sqrt(conn->memb_num); conn->upper_limit = conn->params.fc_base_limit * fn + .5; conn->lower_limit = conn->upper_limit * conn->params.fc_resume_factor + .5; gu_info ("Flow-control interval: [%ld, %ld]", conn->lower_limit, conn->upper_limit); } /*! Handles flow control events * (this is frequent, so leave it inlined) */ static inline void gcs_handle_flow_control (gcs_conn_t* conn, const struct gcs_fc_event* fc) { if (gtohl(fc->conf_id) != (uint32_t)conn->conf_id) { // obsolete fc request return; } conn->stop_count += ((fc->stop != 0) << 1) - 1; // +1 if !0, -1 if 0 conn->stats_fc_received += (fc->stop != 0); if (1 == conn->stop_count) { gcs_sm_pause (conn->sm); // first STOP request } else if (0 == conn->stop_count) { gcs_sm_continue (conn->sm); // last CONT request } return; } static void _reset_pkt_size(gcs_conn_t* conn) { if (conn->state != GCS_CONN_CLOSED) return; // #600 workaround long ret; if (0 > (ret = gcs_core_set_pkt_size (conn->core, conn->params.max_packet_size))) { gu_warn ("Failed to set packet size: %ld (%s)", ret, strerror(-ret)); } } static long _join (gcs_conn_t* conn, gcs_seqno_t seqno) { long err; while (-EAGAIN == (err = gcs_core_send_join (conn->core, seqno))) usleep (10000); switch (err) { case -ENOTCONN: gu_warn ("Sending JOIN failed: %d (%s). " "Will retry in new primary component.", err, strerror(-err)); case 0: return 0; default: gu_error ("Sending JOIN failed: %d (%s).", err, strerror(-err)); return err; } } /*! Handles configuration action */ // TODO: this function does not provide any way for recv_thread to gracefully // exit in case of self-leave message. static void gcs_handle_act_conf (gcs_conn_t* conn, const void* action) { const gcs_act_conf_t* conf = action; long ret; conn->my_idx = conf->my_idx; gu_fifo_lock(conn->recv_q); { /* reset flow control as membership is most likely changed */ if (!gu_mutex_lock (&conn->fc_lock)) { conn->stop_sent = 0; conn->stop_count = 0; conn->conf_id = conf->conf_id; conn->memb_num = conf->memb_num; _set_fc_limits (conn); gu_mutex_unlock (&conn->fc_lock); } else { gu_fatal ("Failed to lock mutex."); abort(); } conn->sync_sent = false; // need to wake up send monitor if it was paused during CC gcs_sm_continue(conn->sm); } gu_fifo_release (conn->recv_q); if (conf->conf_id < 0) { if (0 == conf->memb_num) { assert (conf->my_idx < 0); gu_info ("Received SELF-LEAVE. Closing connection."); gcs_shift_state (conn, GCS_CONN_CLOSED); } else { gu_info ("Received NON-PRIMARY."); assert (GCS_NODE_STATE_NON_PRIM == conf->my_state); gcs_become_open (conn); conn->global_seqno = conf->seqno; } return; } assert (conf->conf_id >= 0); /* */ if (conf->memb_num < 1) { gu_fatal ("Internal error: PRIMARY configuration with %d nodes", conf->memb_num); abort(); } if (conf->my_idx < 0 || conf->my_idx >= conf->memb_num) { gu_fatal ("Internal error: index of this node (%d) is out of bounds: " "[%d, %d]", conf->my_idx, 0, conf->memb_num - 1); abort(); } if (conf->my_state < GCS_NODE_STATE_PRIM) { gu_fatal ("Internal error: NON-PRIM node state in PRIM configuraiton"); abort(); } /* */ conn->global_seqno = conf->seqno; /* at this point we have established protocol version, * so can set packet size */ // Ticket #600: commented out as unsafe under load _reset_pkt_size(conn); const gcs_conn_state_t old_state = conn->state; switch (conf->my_state) { case GCS_NODE_STATE_PRIM: gcs_become_primary(conn); return; /* Below are not real state transitions, rather state recovery, * so bypassing state transition matrix */ case GCS_NODE_STATE_JOINER: conn->state = GCS_CONN_JOINER; break; case GCS_NODE_STATE_DONOR: conn->state = GCS_CONN_DONOR; break; case GCS_NODE_STATE_JOINED: conn->state = GCS_CONN_JOINED; break; case GCS_NODE_STATE_SYNCED: conn->state = GCS_CONN_SYNCED; break; default: gu_fatal ("Internal error: unrecognized node state: %d", conf->my_state); abort(); } if (old_state != conn->state) { gu_info ("Restored state %s -> %s (%lld)", gcs_conn_state_str[old_state], gcs_conn_state_str[conn->state], conn->global_seqno); } switch (conn->state) { case GCS_CONN_JOINED: /* One of the cases when the node can become SYNCED */ { bool send_sync = false; gu_fifo_lock(conn->recv_q); { send_sync = gcs_send_sync_begin(conn); } gu_fifo_release (conn->recv_q); if (send_sync && (ret = gcs_send_sync_end (conn))) { gu_warn ("CC: sending SYNC failed: %ld (%s)", ret, strerror (-ret)); } } break; case GCS_CONN_JOINER: case GCS_CONN_DONOR: /* #603, #606 - duplicate JOIN msg in case we lost it */ assert (conf->conf_id >= 0); if (conn->need_to_join) _join (conn, conn->join_seqno); break; default: break; } } static long gcs_handle_act_state_req (gcs_conn_t* conn, struct gcs_act_rcvd* rcvd) { if ((gcs_seqno_t)conn->my_idx == rcvd->id) { int const donor_idx = (int)rcvd->id; // to pacify valgrind gu_debug("Got GCS_ACT_STATE_REQ to %i, my idx: %ld", donor_idx, conn->my_idx); // rewrite to pass global seqno for application rcvd->id = conn->global_seqno; return gcs_become_donor (conn); } else { if (rcvd->id >= 0) { gcs_become_joiner (conn); } return 1; // pass to gcs_request_state_transfer() caller. } } /*! Allocates buffer with malloc to pass to the upper layer. */ static long gcs_handle_state_change (gcs_conn_t* conn, const struct gcs_act* act) { gu_debug ("Got '%s' dated %lld", gcs_act_type_to_str (act->type), gcs_seqno_gtoh(*(gcs_seqno_t*)act->buf)); void* buf = malloc (act->buf_len); if (buf) { memcpy (buf, act->buf, act->buf_len); /* initially act->buf points to internal static recv buffer. No leak here */ ((struct gcs_act*)act)->buf = buf; return 1; } else { gu_fatal ("Could not allocate state change action (%zd bytes)", act->buf_len); abort(); return -ENOMEM; } } /*! * Performs work requred by action in current context. * @return negative error code, 0 if action should be discarded, 1 if should be * passed to application. */ static long gcs_handle_actions (gcs_conn_t* conn, struct gcs_act_rcvd* rcvd) { long ret = 0; switch (rcvd->act.type) { case GCS_ACT_FLOW: assert (sizeof(struct gcs_fc_event) == rcvd->act.buf_len); gcs_handle_flow_control (conn, rcvd->act.buf); break; case GCS_ACT_CONF: gcs_handle_act_conf (conn, rcvd->act.buf); ret = 1; break; case GCS_ACT_STATE_REQ: ret = gcs_handle_act_state_req (conn, rcvd); break; case GCS_ACT_JOIN: ret = gcs_handle_state_change (conn, &rcvd->act); if (gcs_seqno_gtoh(*(gcs_seqno_t*)rcvd->act.buf) < 0 && GCS_CONN_JOINER == conn->state) gcs_become_primary (conn); else gcs_become_joined (conn); break; case GCS_ACT_SYNC: ret = gcs_handle_state_change (conn, &rcvd->act); gcs_become_synced (conn); break; default: break; } return ret; } static inline void GCS_FIFO_PUSH_TAIL (gcs_conn_t* conn, ssize_t size) { conn->recv_q_size += size; gu_fifo_push_tail(conn->recv_q); } /* Returns true if timeout was handled and false otherwise */ static bool _handle_timeout (gcs_conn_t* conn) { bool ret; long long now = gu_time_calendar(); /* TODO: now the only point for timeout is flow control (#412), * later we might need to handle more timers. */ if (conn->timeout <= now) { ret = ((GCS_CONN_JOINER != conn->state) || (_release_sst_flow_control (conn) >= 0)); } else { gu_error ("Unplanned timeout! (tout: %lld, now: %lld)", conn->timeout, now); ret = false; } conn->timeout = GU_TIME_ETERNITY; return ret; } static long _check_recv_queue_growth (gcs_conn_t* conn, ssize_t size) { assert (GCS_CONN_JOINER == conn->state); long ret = 0; long long pause = gcs_fc_process (&conn->stfc, size); if (pause > 0) { /* replication needs throttling */ if (conn->stop_sent <= 0) { if ((ret = gcs_send_fc_event (conn, GCS_FC_STOP)) >= 0) { conn->stop_sent++; ret = 0; } else { ret = gcs_check_error (ret, "Failed to send SST FC_STOP."); } } if (gu_likely(pause != GU_TIME_ETERNITY)) { if (GU_TIME_ETERNITY == conn->timeout) { conn->timeout = gu_time_calendar(); } conn->timeout += pause; // we need to track pauses regardless } else if (conn->timeout != GU_TIME_ETERNITY) { conn->timeout = GU_TIME_ETERNITY; gu_warn ("Replication paused until state transfer is complete " "due to reaching hard limit on the writeset queue size."); } return ret; } else { return pause; // 0 or error code } } /* * gcs_recv_thread() receives whatever actions arrive from group, * and performs necessary actions based on action type. */ static void *gcs_recv_thread (void *arg) { gcs_conn_t* conn = arg; ssize_t ret = -ECONNABORTED; // To avoid race between gcs_open() and the following state check in while() gu_cond_t tmp_cond; /* TODO: rework when concurrency in SM is allowed */ gu_cond_init (&tmp_cond, NULL); gcs_sm_enter(conn->sm, &tmp_cond, false); gcs_sm_leave(conn->sm); gu_cond_destroy (&tmp_cond); while (conn->state < GCS_CONN_CLOSED) { gcs_seqno_t this_act_id = GCS_SEQNO_ILL; struct gcs_repl_act** repl_act_ptr; struct gcs_act_rcvd rcvd; ret = gcs_core_recv (conn->core, &rcvd, conn->timeout); if (gu_unlikely(ret <= 0)) { if (-ETIMEDOUT == ret && _handle_timeout(conn)) continue; struct gcs_recv_act* err_act = gu_fifo_get_tail(conn->recv_q); assert (NULL == rcvd.act.buf); assert (0 == rcvd.act.buf_len); assert (GCS_ACT_ERROR == rcvd.act.type); assert (GCS_SEQNO_ILL == rcvd.id); err_act->rcvd = rcvd; err_act->local_id = GCS_SEQNO_ILL; GCS_FIFO_PUSH_TAIL (conn, rcvd.act.buf_len); gu_debug ("gcs_core_recv returned %d: %s", ret, strerror(-ret)); break; } // gu_info ("Received action type: %d, size: %d, global seqno: %lld", // act_type, act_size, (long long)act_id); assert (rcvd.act.type < GCS_ACT_ERROR); assert (ret == rcvd.act.buf_len); if (gu_unlikely(rcvd.act.type >= GCS_ACT_STATE_REQ)) { ret = gcs_handle_actions (conn, &rcvd); if (gu_unlikely(ret < 0)) { // error gu_debug ("gcs_handle_actions returned %d: %s", ret, strerror(-ret)); break; } if (gu_likely(ret <= 0)) continue; // not for application } /* deliver to application (note matching assert in the bottom-half of * gcs_repl()) */ if (gu_likely (rcvd.act.type != GCS_ACT_TORDERED || (rcvd.id > 0 && (conn->global_seqno = rcvd.id)))) { /* successful delivery - increment local order */ this_act_id = conn->local_act_id++; } if (NULL != rcvd.repl_buf && (repl_act_ptr = gcs_fifo_lite_get_head (conn->repl_q)) && (gu_likely ((*repl_act_ptr)->action->buf == rcvd.repl_buf) || /* at this point repl_q is locked and we need to unlock it and * return false to fall to the 'else' branch; unlikely case */ (gcs_fifo_lite_release (conn->repl_q), false))) { /* local action from repl_q */ struct gcs_repl_act* repl_act = *repl_act_ptr; gcs_fifo_lite_pop_head (conn->repl_q); assert (repl_act->action->type == rcvd.act.type); assert (repl_act->action->size == rcvd.act.buf_len || repl_act->action->type == GCS_ACT_STATE_REQ); repl_act->action->buf = rcvd.act.buf; repl_act->action->seqno_g = rcvd.id; repl_act->action->seqno_l = this_act_id; gu_mutex_lock (&repl_act->wait_mutex); gu_cond_signal (&repl_act->wait_cond); gu_mutex_unlock (&repl_act->wait_mutex); } else if (gu_likely(this_act_id >= 0)) { /* remote/non-repl'ed action */ struct gcs_recv_act* recv_act = gu_fifo_get_tail (conn->recv_q); if (gu_likely (NULL != recv_act)) { recv_act->rcvd = rcvd; recv_act->local_id = this_act_id; conn->queue_len = gu_fifo_length (conn->recv_q) + 1; bool send_stop = gcs_fc_stop_begin (conn); // release queue GCS_FIFO_PUSH_TAIL (conn, rcvd.act.buf_len); if (gu_unlikely(GCS_CONN_JOINER == conn->state)) { ret = _check_recv_queue_growth (conn, rcvd.act.buf_len); assert (ret <= 0); if (ret < 0) break; } if (gu_unlikely(send_stop) && (ret = gcs_fc_stop_end(conn))) { gu_error ("gcs_fc_stop() returned %d: %s", ret, strerror(-ret)); break; } } else { assert (GCS_CONN_CLOSED == conn->state); ret = -EBADFD; break; } // gu_info("Received foreign action of type %d, size %d, id=%llu, " // "action %p", rcvd.act.type, rcvd.act.buf_len, // this_act_id, rcvd.act.buf); } else if (conn->my_idx == rcvd.sender_idx) { gu_fatal("Protocol violation: unordered local action not in repl_q:" " { {%p, %zd, %s}, %ld, %lld }.", rcvd.act.buf, rcvd.act.buf_len, gcs_act_type_to_str(rcvd.act.type), rcvd.sender_idx, rcvd.id); assert(0); ret = -ENOTRECOVERABLE; break; } else { gu_fatal ("Protocol violation: unordered remote action: " "{ {%p, %zd, %s}, %ld, %lld }", rcvd.act.buf, rcvd.act.buf_len, gcs_act_type_to_str(rcvd.act.type), rcvd.sender_idx, rcvd.id); assert (0); ret = -ENOTRECOVERABLE; break; } } if (ret > 0) ret = 0; gu_info ("RECV thread exiting %d: %s", ret, strerror(-ret)); return NULL; } /* Opens connection to group */ long gcs_open (gcs_conn_t* conn, const char* channel, const char* url, bool const bootstrap) { long ret = 0; if ((ret = gcs_sm_open(conn->sm))) return ret; // open in case it is closed gu_cond_t tmp_cond; /* TODO: rework when concurrency in SM is allowed */ gu_cond_init (&tmp_cond, NULL); if ((ret = gcs_sm_enter (conn->sm, &tmp_cond, false))) { gu_error("Failed to enter send monitor: %d (%s)", ret, strerror(-ret)); return ret; } if (GCS_CONN_CLOSED == conn->state) { if (!(ret = gcs_core_open (conn->core, channel, url, bootstrap))) { _reset_pkt_size(conn); if (!(ret = gu_thread_create (&conn->recv_thread, NULL, gcs_recv_thread, conn))) { gcs_fifo_lite_open(conn->repl_q); gu_fifo_open(conn->recv_q); gcs_shift_state (conn, GCS_CONN_OPEN); gu_info ("Opened channel '%s'", channel); goto out; } else { gu_error ("Failed to create main receive thread: %ld (%s)", ret, strerror(-ret)); } gcs_core_close (conn->core); } else { gu_error ("Failed to open channel '%s' at '%s': %d (%s)", channel, url, ret, strerror(-ret)); } } else { gu_error ("Bad GCS connection state: %d (%s)", conn->state, gcs_conn_state_str[conn->state]); ret = -EBADFD; } out: gcs_sm_leave (conn->sm); gu_cond_destroy (&tmp_cond); return ret; } /* Closes group connection */ /* After it returns, application should have all time in the world to cancel * and join threads which try to access the handle, before calling gcs_destroy() * on it. */ long gcs_close (gcs_conn_t *conn) { /* all possible races in connection closing should be resolved by * the following call, it is thread-safe */ long ret; if (!(ret = gcs_sm_close (conn->sm)) && !(ret = gcs_core_close (conn->core))) { /* here we synchronize with SELF_LEAVE event caused by gcs_core_close */ if ((ret = gu_thread_join (conn->recv_thread, NULL))) { gu_error ("Failed to join recv_thread(): %d (%s)", -ret, strerror(-ret)); } else { gu_info ("recv_thread() joined."); } /* recv_thread() is supposed to set state to CLOSED when exiting */ assert (GCS_CONN_CLOSED == conn->state); gu_info ("Closing replication queue."); struct gcs_repl_act** act_ptr; /* At this point (state == CLOSED) no new threads should be able to * queue for repl (check gcs_repl()), and recv thread is joined, so no * new actions will be received. Abort threads that are still waiting * in repl queue */ while ((act_ptr = gcs_fifo_lite_get_head (conn->repl_q))) { struct gcs_repl_act* act = *act_ptr; gcs_fifo_lite_pop_head (conn->repl_q); /* This will wake up repl threads in repl_q - * they'll quit on their own, * they don't depend on the conn object after waking */ gu_mutex_lock (&act->wait_mutex); gu_cond_signal (&act->wait_cond); gu_mutex_unlock (&act->wait_mutex); } gcs_fifo_lite_close (conn->repl_q); /* wake all gcs_recv() threads () */ // FIXME: this can block waiting for applicaiton threads to fetch all // items. In certain situations this can block forever. Ticket #113 gu_info ("Closing slave action queue."); gu_fifo_close (conn->recv_q); } return ret; } /* Frees resources associated with GCS connection handle */ long gcs_destroy (gcs_conn_t *conn) { long err; gu_cond_t tmp_cond; gu_cond_init (&tmp_cond, NULL); if ((err = gcs_sm_enter (conn->sm, &tmp_cond, false))) // need an error here { if (GCS_CONN_CLOSED != conn->state) { if (GCS_CONN_CLOSED > conn->state) gu_error ("Attempt to call gcs_destroy() before gcs_close(): " "state = %d", conn->state); gu_cond_destroy (&tmp_cond); return -EBADFD; } /* this should cancel all recv calls */ gu_fifo_destroy (conn->recv_q); gcs_shift_state (conn, GCS_CONN_DESTROYED); //DELETE conn->err = -EBADFD; /* we must unlock the mutex here to allow unfortunate threads * to acquire the lock and give up gracefully */ } else { gcs_sm_leave (conn->sm); gu_cond_destroy (&tmp_cond); err = -EBADFD; return err; } gu_cond_destroy (&tmp_cond); gcs_sm_destroy (conn->sm); if ((err = gcs_fifo_lite_destroy (conn->repl_q))) { gu_debug ("Error destroying repl FIFO: %d (%s)", err, strerror(-err)); return err; } if ((err = gcs_core_destroy (conn->core))) { gu_debug ("Error destroying core: %d (%s)", err, strerror(-err)); return err; } /* This must not last for long */ while (gu_mutex_destroy (&conn->fc_lock)); _cleanup_params (conn); gu_free (conn); return 0; } /* Puts action in the send queue and returns */ long gcs_send (gcs_conn_t* const conn, const void* const act_buf, size_t const act_size, gcs_act_type_t const act_type, bool const scheduled) { if (gu_unlikely(act_size > GCS_MAX_ACT_SIZE)) return -EMSGSIZE; long ret = -ENOTCONN; /*! locking connection here to avoid race with gcs_close() * @note: gcs_repl() and gcs_recv() cannot lock connection * because they block indefinitely waiting for actions */ gu_cond_t tmp_cond; gu_cond_init (&tmp_cond, NULL); if (!(ret = gcs_sm_enter (conn->sm, &tmp_cond, scheduled))) { while ((GCS_CONN_OPEN >= conn->state) && (ret = gcs_core_send (conn->core, act_buf, act_size, act_type)) == -ERESTART); gcs_sm_leave (conn->sm); gu_cond_destroy (&tmp_cond); } return ret; } long gcs_schedule (gcs_conn_t* conn) { return gcs_sm_schedule (conn->sm); } long gcs_interrupt (gcs_conn_t* conn, long handle) { return gcs_sm_interrupt (conn->sm, handle); } gcs_seqno_t gcs_caused(gcs_conn_t* conn) { return gcs_core_caused(conn->core); } /* Puts action in the send queue and returns after it is replicated */ long gcs_repl (gcs_conn_t* conn, //!size > GCS_MAX_ACT_SIZE)) return -EMSGSIZE; long ret; assert (act); assert (act->buf); assert (act->size > 0); act->seqno_l = GCS_SEQNO_ILL; act->seqno_g = GCS_SEQNO_ILL; /* This is good - we don't have to do a copy because we wait */ struct gcs_repl_act repl_act = { .action = act }; gu_mutex_init (&repl_act.wait_mutex, NULL); gu_cond_init (&repl_act.wait_cond, NULL); /* Send action and wait for signal from recv_thread * we need to lock a mutex before we can go wait for signal */ if (!(ret = gu_mutex_lock (&repl_act.wait_mutex))) { // Lock here does the following: // 1. serializes gcs_core_send() access between gcs_repl() and // gcs_send() // 2. avoids race with gcs_close() and gcs_destroy() if (!(ret = gcs_sm_enter (conn->sm, &repl_act.wait_cond, scheduled))) { struct gcs_repl_act** act_ptr; //#ifndef NDEBUG const void* const orig_buf = act->buf; //#endif // some hack here to achieve one if() instead of two: // ret = -EAGAIN part is a workaround for #569 // if (conn->state >= GCS_CONN_CLOSE) or (act_ptr == NULL) // ret will be -ENOTCONN if ((ret = -EAGAIN, conn->upper_limit >= conn->queue_len || act->type != GCS_ACT_TORDERED) && (ret = -ENOTCONN, GCS_CONN_OPEN >= conn->state) && (act_ptr = gcs_fifo_lite_get_tail (conn->repl_q))) { *act_ptr = &repl_act; gcs_fifo_lite_push_tail (conn->repl_q); // Keep on trying until something else comes out while ((ret = gcs_core_send (conn->core, act->buf, act->size, act->type)) == -ERESTART) {} if (ret < 0) { /* remove item from the queue, it will never be delivered */ gu_warn ("Send action {%p, %zd, %s} returned %d (%s)", act->buf, act->size,gcs_act_type_to_str(act->type), ret, strerror(-ret)); if (!gcs_fifo_lite_remove (conn->repl_q)) { gu_fatal ("Failed to remove unsent item from repl_q"); assert(0); ret = -ENOTRECOVERABLE; } } else { assert (ret == (ssize_t)act->size); } } gcs_sm_leave (conn->sm); assert(ret); /* now we can go waiting for action delivery */ if (ret >= 0) { gu_cond_wait (&repl_act.wait_cond, &repl_act.wait_mutex); #ifndef GCS_FOR_GARB assert (act->buf != 0); #else assert (act->buf == 0); #endif /* GCS_FOR_GARB */ if (act->seqno_g < 0) { assert (GCS_SEQNO_ILL == act->seqno_l || GCS_ACT_TORDERED != act->type); if (act->seqno_g == GCS_SEQNO_ILL) { /* action was not replicated for some reason */ assert (orig_buf == act->buf); ret = -EINTR; } else { /* core provided an error code in global seqno */ assert (orig_buf != act->buf); ret = act->seqno_g; act->seqno_g = GCS_SEQNO_ILL; } if (orig_buf != act->buf) // action was allocated in gcache gcs_gcache_free (conn->gcache, act->buf); act->buf = NULL; } } } gu_mutex_unlock (&repl_act.wait_mutex); } gu_mutex_destroy (&repl_act.wait_mutex); gu_cond_destroy (&repl_act.wait_cond); #ifdef GCS_DEBUG_GCS // gu_debug ("\nact_size = %u\nact_type = %u\n" // "act_id = %llu\naction = %p (%s)\n", // act->size, act->type, act->seqno_g, act->buf, act->buf); #endif return ret; } long gcs_request_state_transfer (gcs_conn_t *conn, const void *req, size_t size, const char *donor, gcs_seqno_t *local) { long ret = -ENOMEM; size_t donor_len = strlen(donor) + 1; // include terminating \0 size_t rst_size = size + donor_len; char* rst = gu_malloc (rst_size); *local = GCS_SEQNO_ILL; if (rst) { /* RST format: |donor name|\0|app request| * anything more complex will require a special (de)serializer. * NOTE: this is sender part. Check gcs_group_handle_state_request() * for the receiver part. */ memcpy (rst, donor, donor_len); memcpy (rst + donor_len, req, size); struct gcs_action action = { .buf = rst, .size = rst_size, .type = GCS_ACT_STATE_REQ }; ret = gcs_repl(conn, &action, false); assert (action.buf != rst); gu_free (rst); *local = action.seqno_l; if (ret > 0) { #ifndef GCS_FOR_GARB assert (action.buf != NULL); #else assert (action.buf == NULL); #endif assert (ret == (ssize_t)rst_size); assert (action.seqno_g >= 0); assert (action.seqno_l > 0); gcs_gcache_free (conn->gcache, action.buf); // on joiner global seqno stores donor index // on donor global seqno stores global seqno ret = action.seqno_g; } else { assert (action.buf == NULL); } } return ret; } long gcs_desync (gcs_conn_t* conn, gcs_seqno_t* local) { long ret = gcs_request_state_transfer (conn, "", 1, GCS_DESYNC_REQ, local); if (ret >= 0) { return 0; } else { return ret; } } static inline void GCS_FIFO_POP_HEAD (gcs_conn_t* conn, ssize_t size) { assert (conn->recv_q_size >= size); conn->recv_q_size -= size; gu_fifo_pop_head (conn->recv_q); } /* Returns when an action from another process is received */ long gcs_recv (gcs_conn_t* conn, struct gcs_action* action) { int err; struct gcs_recv_act* recv_act = NULL; assert (action); if ((recv_act = gu_fifo_get_head (conn->recv_q, &err))) { conn->queue_len = gu_fifo_length (conn->recv_q) - 1; bool send_cont = gcs_fc_cont_begin (conn); bool send_sync = gcs_send_sync_begin (conn); action->buf = (void*)recv_act->rcvd.act.buf; action->size = recv_act->rcvd.act.buf_len; action->type = recv_act->rcvd.act.type; action->seqno_g = recv_act->rcvd.id; action->seqno_l = recv_act->local_id; if (gu_unlikely (GCS_ACT_CONF == action->type)) { err = gu_fifo_cancel_gets (conn->recv_q); if (err) { gu_fatal ("Internal logic error: failed to cancel recv_q " "\"gets\": %d (%s). Aborting.", err, strerror(-err)); gu_abort(); } } GCS_FIFO_POP_HEAD (conn, action->size); // release the queue if (gu_unlikely(send_cont) && (err = gcs_fc_cont_end(conn))) { // We have successfully received an action, but failed to send // important control message. What do we do? Inability to send CONT // can block the whole cluster. There are only conn->queue_len - 1 // attempts to do that (that's how many times we'll get here). // Perhaps if the last attempt fails, we should crash. if (conn->queue_len > 0) { gu_warn ("Failed to send CONT message: %d (%s). " "Attempts left: %ld", err, strerror(-err), conn->queue_len); } else { gu_fatal ("Last opportunity to send CONT message failed: " "%d (%s). Aborting to avoid cluster lock-up...", err, strerror(-err)); gcs_close(conn); gu_abort(); } } else if (gu_unlikely(send_sync) && (err = gcs_send_sync_end (conn))) { gu_warn ("Failed to send SYNC message: %d (%s). Will try later.", err, strerror(-err)); } return action->size; } else { action->buf = NULL; action->size = 0; action->type = GCS_ACT_ERROR; action->seqno_g = GCS_SEQNO_ILL; action->seqno_l = GCS_SEQNO_ILL; switch (err) { case -ENODATA: assert (GCS_CONN_CLOSED == conn->state); return GCS_CLOSED_ERROR; default: return err; } } } long gcs_resume_recv (gcs_conn_t* conn) { int ret = GCS_CLOSED_ERROR; ret = gu_fifo_resume_gets (conn->recv_q); if (ret) { if (conn->state < GCS_CONN_CLOSED) { gu_fatal ("Internal logic error: failed to resume \"gets\" on " "recv_q: %d (%s). Aborting.", ret, strerror (-ret)); gcs_close (conn); gu_abort(); } else { ret = GCS_CLOSED_ERROR; } } return ret; } long gcs_wait (gcs_conn_t* conn) { if (gu_likely(GCS_CONN_SYNCED == conn->state)) { return (conn->stop_count > 0 || (conn->queue_len > conn->upper_limit)); } else { switch (conn->state) { case GCS_CONN_OPEN: return -ENOTCONN; case GCS_CONN_CLOSED: case GCS_CONN_DESTROYED: return GCS_CLOSED_ERROR; default: return -EAGAIN; // wait until get sync } } } long gcs_conf_set_pkt_size (gcs_conn_t *conn, long pkt_size) { if (conn->params.max_packet_size == pkt_size) return pkt_size; return gcs_set_pkt_size (conn, pkt_size); } long gcs_set_last_applied (gcs_conn_t* conn, gcs_seqno_t seqno) { gu_cond_t cond; gu_cond_init (&cond, NULL); long ret = gcs_sm_enter (conn->sm, &cond, false); if (!ret) { ret = gcs_core_set_last_applied (conn->core, seqno); gcs_sm_leave (conn->sm); } gu_cond_destroy (&cond); return ret; } long gcs_join (gcs_conn_t* conn, gcs_seqno_t seqno) { conn->join_seqno = seqno; conn->need_to_join = true; return _join (conn, seqno); } void gcs_get_stats (gcs_conn_t* conn, struct gcs_stats* stats) { gu_fifo_stats (conn->recv_q, &stats->recv_q_len, &stats->recv_q_len_avg); stats->recv_q_size = conn->recv_q_size; gcs_sm_stats (conn->sm, &stats->send_q_len, &stats->send_q_len_avg, &stats->fc_paused); stats->fc_sent = conn->stats_fc_sent; conn->stats_fc_sent = 0; stats->fc_received = conn->stats_fc_received; conn->stats_fc_received = 0; } static long _set_fc_limit (gcs_conn_t* conn, const char* value) { long long limit; const char* const endptr = gu_str2ll(value, &limit); if (limit > 0LL && *endptr == '\0') { if (limit > LONG_MAX) limit = LONG_MAX; gu_fifo_lock(conn->recv_q); { if (!gu_mutex_lock (&conn->fc_lock)) { conn->params.fc_base_limit = limit; _set_fc_limits (conn); gu_config_set_int64 (conn->config, GCS_PARAMS_FC_LIMIT, conn->params.fc_base_limit); gu_mutex_unlock (&conn->fc_lock); } else { gu_fatal ("Failed to lock mutex."); abort(); } } gu_fifo_release (conn->recv_q); return 0; } else { return -EINVAL; } } static long _set_fc_factor (gcs_conn_t* conn, const char* value) { double factor; const char* const endptr = gu_str2dbl(value, &factor); if (factor >= 0.0 && factor <= 1.0 && *endptr == '\0') { if (factor == conn->params.fc_resume_factor) return 0; gu_fifo_lock(conn->recv_q); { if (!gu_mutex_lock (&conn->fc_lock)) { conn->params.fc_resume_factor = factor; _set_fc_limits (conn); gu_config_set_double (conn->config, GCS_PARAMS_FC_FACTOR, conn->params.fc_resume_factor); gu_mutex_unlock (&conn->fc_lock); } else { gu_fatal ("Failed to lock mutex."); abort(); } } gu_fifo_release (conn->recv_q); return 0; } else { return -EINVAL; } } static long _set_fc_debug (gcs_conn_t* conn, const char* value) { bool debug; const char* const endptr = gu_str2bool(value, &debug); if (*endptr == '\0') { if (conn->params.fc_debug == debug) return 0; conn->params.fc_debug = debug; gcs_fc_debug (&conn->stfc, debug); gu_config_set_bool (conn->config, GCS_PARAMS_FC_DEBUG, debug); return 0; } else { return -EINVAL; } } static long _set_sync_donor (gcs_conn_t* conn, const char* value) { bool sd; const char* const endptr = gu_str2bool (value, &sd); if (endptr[0] != '\0') return -EINVAL; if (conn->params.sync_donor != sd) { conn->params.sync_donor = sd; conn->max_fc_state = sd ? GCS_CONN_DONOR : GCS_CONN_JOINED; } return 0; } static long _set_pkt_size (gcs_conn_t* conn, const char* value) { long long pkt_size; const char* const endptr = gu_str2ll (value, &pkt_size); if (pkt_size > 0 && *endptr == '\0') { if (pkt_size > LONG_MAX) pkt_size = LONG_MAX; if (conn->params.max_packet_size == pkt_size) return 0; long ret = gcs_set_pkt_size (conn, pkt_size); if (ret >= 0) { ret = 0; gu_config_set_int64(conn->config,GCS_PARAMS_MAX_PKT_SIZE,pkt_size); } return ret; } else { // gu_warn ("Invalid value for %s: '%s'", GCS_PARAMS_PKT_SIZE, value); return -EINVAL; } } static long _set_recv_q_hard_limit (gcs_conn_t* conn, const char* value) { long long limit; const char* const endptr = gu_str2ll (value, &limit); if (limit > 0 && *endptr == '\0') { if (limit > LONG_MAX) limit = LONG_MAX; long long limit_fixed = limit * gcs_fc_hard_limit_fix; if (conn->params.recv_q_hard_limit == limit_fixed) return 0; gu_config_set_int64 (conn->config, GCS_PARAMS_RECV_Q_HARD_LIMIT, limit); conn->params.recv_q_hard_limit = limit_fixed; return 0; } else { return -EINVAL; } } static long _set_recv_q_soft_limit (gcs_conn_t* conn, const char* value) { double dbl; const char* const endptr = gu_str2dbl (value, &dbl); if (dbl >= 0.0 && dbl < 1.0 && *endptr == '\0') { if (dbl == conn->params.recv_q_soft_limit) return 0; gu_config_set_double (conn->config, GCS_PARAMS_RECV_Q_SOFT_LIMIT, dbl); conn->params.recv_q_soft_limit = dbl; return 0; } else { return -EINVAL; } } static long _set_max_throttle (gcs_conn_t* conn, const char* value) { double dbl; const char* const endptr = gu_str2dbl (value, &dbl); if (dbl >= 0.0 && dbl < 1.0 && *endptr == '\0') { if (dbl == conn->params.max_throttle) return 0; gu_config_set_double (conn->config, GCS_PARAMS_MAX_THROTTLE, dbl); conn->params.max_throttle = dbl; return 0; } else { return -EINVAL; } } long gcs_param_set (gcs_conn_t* conn, const char* key, const char *value) { if (!strcmp (key, GCS_PARAMS_FC_LIMIT)) { return _set_fc_limit (conn, value); } else if (!strcmp (key, GCS_PARAMS_FC_FACTOR)) { return _set_fc_factor (conn, value); } else if (!strcmp (key, GCS_PARAMS_FC_DEBUG)) { return _set_fc_debug (conn, value); } else if (!strcmp (key, GCS_PARAMS_SYNC_DONOR)) { return _set_sync_donor (conn, value); } else if (!strcmp (key, GCS_PARAMS_MAX_PKT_SIZE)) { return _set_pkt_size (conn, value); } else if (!strcmp (key, GCS_PARAMS_RECV_Q_HARD_LIMIT)) { return _set_recv_q_hard_limit (conn, value); } else if (!strcmp (key, GCS_PARAMS_RECV_Q_SOFT_LIMIT)) { return _set_recv_q_soft_limit (conn, value); } else if (!strcmp (key, GCS_PARAMS_MAX_THROTTLE)) { return _set_max_throttle (conn, value); } else { return gcs_core_param_set (conn->core, key, value); } } const char* gcs_param_get (gcs_conn_t* conn, const char* key) { gu_warn ("Not implemented: %s", __FUNCTION__); return NULL; } percona-xtradb-cluster-galera/gcs/src/gcs.h0000644000000000000000000003573612247075736021160 0ustar rootroot00000000000000/* * Copyright (C) 2008-2012 Codership Oy * * $Id: gcs.h 3207 2013-08-21 09:08:51Z vlad $ */ /*! * @file gcs.c Public GCS API */ #ifndef _gcs_h_ #define _gcs_h_ #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include "gu_config.h" #include "gcache.h" #include "gu_errno.h" /*! @typedef @brief Sequence number type. */ typedef int64_t gcs_seqno_t; /*! @def @brief Illegal sequence number. Action not serialized. */ static const gcs_seqno_t GCS_SEQNO_ILL = -1; /*! @def @brief Empty state. No actions applied. */ static const gcs_seqno_t GCS_SEQNO_NIL = 0; /*! @def @brief Start of the sequence */ static const gcs_seqno_t GCS_SEQNO_FIRST = 1; /*! @def @brief history UUID length */ #define GCS_UUID_LEN 16 /*! @def @brief maximum supported size of an action (2GB - 1) */ #define GCS_MAX_ACT_SIZE 0x7FFFFFFF /*! Connection handle type */ typedef struct gcs_conn gcs_conn_t; /*! @brief Creates GCS connection handle. * * @param conf gu_config_t* configuration object, can be null. * @param cache pointer to the gcache object. * @param node_name human readable name of the node, can be null. * @param inc_addr address at which application accepts incoming requests. * Used for load balancing, can be null. * @param repl_proto_ver max replicator protocol version. * @param appl_proto_ver max application ptotocol version. * @return pointer to GCS connection handle, NULL in case of failure. */ extern gcs_conn_t* gcs_create (gu_config_t* conf, gcache_t* cache, const char* node_name, const char* inc_addr, int repl_proto_ver, int appl_proto_ver); /*! @brief Initialize group history values (optional). * Serves to provide group history persistence after process restart (in case * these data were saved somewhere on persistent storage or the like). If these * values are provided, it is only a hint for the group, as they might be * outdated. Actual seqno and UUID are returned in GCS_ACT_CONF action (see * below) and are determined by quorum. * * This function must be called before gcs_open() or after gcs_close(). * * @param seqno Sequence number of the application state (last action applied). * Should be negative for undefined state. * @param uuid UUID of the sequence (group ID). * Should be all zeroes for undefined state. * * @return 0 in case of success, -EBUSY if conneciton is already opened, * -EBADFD if connection object is being destroyed. */ extern long gcs_init (gcs_conn_t *conn, gcs_seqno_t seqno, const uint8_t uuid[GCS_UUID_LEN]); /*! @brief Opens connection to group (joins channel). * * @param conn connection object * @param channel a name of the channel to join. It must uniquely identify * the channel. If the channel with such name does not exist, * it is created. Processes that joined the same channel * receive the same actions. * @param url an URL-like string that specifies backend communication * driver in the form "TYPE://ADDRESS?options". For gcomm * backend it can be "gcomm://localhost:4567", for dummy backend * ADDRESS field is ignored. * Currently supported backend types: "dummy", "vsbes", "gcomm" * @param bootstrap bootstrap a new group * * @return negative error code, 0 in case of success. */ extern long gcs_open (gcs_conn_t *conn, const char *channel, const char *url, bool bootstrap); /*! @brief Closes connection to group. * * @param conn connection handle * @return negative error code or 0 in case of success. */ extern long gcs_close (gcs_conn_t *conn); /*! @brief Frees resources associuated with connection handle. * * @param conn connection handle * @return negative error code or 0 in case of success. */ extern long gcs_destroy (gcs_conn_t *conn); /*! @brief Deprecated. Waits until the group catches up. * This call checks if any member of the group (including this one) has a * long slave queue. Should be called before gcs_repl(), gcs_send(). * * @return negative error code, 1 if wait is required, 0 otherwise */ extern long gcs_wait (gcs_conn_t *conn); /*! @typedef @brief Action types. * There is a conceptual difference between "messages" * and "actions". Messages are ELEMENTARY pieces of information * atomically delivered by group communication. They are typically * limited in size to a single IP packet. Events generated by group * communication layer must be delivered as a single message. * * For the purpose of this work "action" is a higher level concept * introduced to overcome the message size limitation. Application * replicates information in actions of ARBITRARY size that are * fragmented into as many messages as needed. As such actions * can be delivered only in primary configuration, when total order * of underlying messages is established. * The best analogy for action/message concept would be word/letter. * * The purpose of GCS library is to hide message handling from application. * Therefore application deals only with "actions". * Application can only send actions of types GCS_ACT_TORDERED, * GCS_ACT_COMMIT_CUT and GCS_ACT_STATE_REQ. * Actions of type GCS_ACT_SYNC, GCS_ACT_CONF are generated by the library. */ typedef enum gcs_act_type { /* ordered actions */ GCS_ACT_TORDERED, //! action representing state change, will be assigned global seqno GCS_ACT_COMMIT_CUT, //! group-wide action commit cut GCS_ACT_STATE_REQ, //! request for state transfer GCS_ACT_CONF, //! new configuration GCS_ACT_JOIN, //! joined group (received all state data) GCS_ACT_SYNC, //! synchronized with group GCS_ACT_FLOW, //! flow control GCS_ACT_SERVICE, //! service action, sent by GCS GCS_ACT_ERROR, //! error happened while receiving the action GCS_ACT_UNKNOWN //! undefined/unknown action type } gcs_act_type_t; /*! String representations of action types */ extern const char* gcs_act_type_to_str(gcs_act_type_t); struct gcs_action { const void* buf; ssize_t size; gcs_seqno_t seqno_g; gcs_seqno_t seqno_l; gcs_act_type_t type; }; /*! @brief Sends an action to group and returns. * A copy of action will be returned through gcs_recv() call, or discarded * in case it is not delivered by group. * For a better means to replicate an action see gcs_repl(). @see gcs_repl() * * @param conn group connection handle * @param act_buf action buffer * @param act_size action size * @param act_type action type * @param scheduled whether the call was scheduled by gcs_schedule() * @return negative error code, action size in case of success * @retval -EINTR thread was interrupted while waiting to enter the monitor */ extern long gcs_send (gcs_conn_t* conn, const void* act_buf, size_t act_size, gcs_act_type_t act_type, bool scheduled); /*! @brief Receives an action from group. * Blocks if no actions are available. Action buffer is allocated by GCS * and must be freed by application when action is no longer needed. * Also sets global and local action IDs. Global action ID uniquely identifies * action in the history of the group and can be used to identify the state * of the application for state snapshot purposes. Local action ID is a * monotonic gapless number sequence starting with 1 which can be used * to serialize access to critical sections. * * @param conn group connection handle * @param action action object * @return negative error code, action size in case of success, * @retval 0 on connection close */ extern long gcs_recv (gcs_conn_t* conn, struct gcs_action* action); /*! @brief Replicates an action. * Sends action to group and blocks until it is received. Upon return global * and local IDs are set. Arguments are the same as in gcs_recv(). * @see gcs_recv() * * @param conn group connection handle * @param action action object * @param scheduled whether the call was preceded by gcs_schedule() * @return negative error code, action size in case of success * @retval -EINTR: thread was interrupted while waiting to enter the monitor */ extern long gcs_repl (gcs_conn_t* conn, struct gcs_action* action, bool scheduled); /*! * @brief Schedules entry to CGS send monitor. * Locks send monitor and should be quickly followed by gcs_repl()/gcs_send() * * @retval 0 - won't queue * @retval >0 - queue handle * @retval -EAGAIN - too many queued threads * @retval -EBADFD - connection is closed */ extern long gcs_schedule (gcs_conn_t* conn); /*! * @brief Interrupt a thread waiting to enter send monitor. * * @param conn GCS connection * @param handle queue handle returned by @func gcs_schedule(). Must be > 0 * * @retval 0 success * @retval -ESRCH no such thread/already interrupted */ extern long gcs_interrupt (gcs_conn_t* conn, long handle); /*! * Resume receivng from group. * * @param conn GCS connection * * @retval 0 success * @retval -EBADFD connection is in closed state */ extern long gcs_resume_recv (gcs_conn_t* conn); /*! * After action with this seqno is applied, this thread is guaranteed to see * all the changes made by the client, even on other nodes. * * @return global sequence number or negative error code */ extern gcs_seqno_t gcs_caused(gcs_conn_t* conn); /*! @brief Sends state transfer request * Broadcasts state transfer request which will be passed to one of the * suitable group members. * * @param conn connection to group * @param req opaque byte array that contains data required for * the state transfer (application dependent) * @param size request size * @param donor desired state transfer donor name. Supply empty string to * choose automatically. * @param seqno response to request was ordered with this seqno. * Must be skipped in local queues. * @return negative error code, index of state transfer donor in case of success * (notably, -EAGAIN means try later, -EHOSTUNREACH means desired donor * is unavailable) */ extern long gcs_request_state_transfer (gcs_conn_t *conn, const void *req, size_t size, const char *donor, gcs_seqno_t *seqno); /*! @brief Turns off flow control on the node. * Effectively desynchronizes the node from the cluster (while the node keeps on * receiving all the actions). Requires gcs_join() to return to normal. * * @param conn connection to group * @param seqno response to request was ordered with this seqno. * Must be skipped in local queues. * @return negative error code, 0 in case of success. */ extern long gcs_desync (gcs_conn_t* conn, gcs_seqno_t* seqno); /*! @brief Informs group on behalf of donor that state stransfer is over. * If status is non-negative, joiner will be considered fully joined to group. * * @param conn opened connection to group * @param status negative error code in case of state transfer failure, * 0 or (optional) seqno corresponding to transferred state. * @return negative error code, 0 in case of success */ extern long gcs_join (gcs_conn_t *conn, gcs_seqno_t status); /////////////////////////////////////////////////////////////////////////////// /* Service functions */ /*! Informs group about the last applied action on this node */ extern long gcs_set_last_applied (gcs_conn_t* conn, gcs_seqno_t seqno); /* GCS Configuration */ /*! sets the key to a given value * * @return 0 in case of success, 1 if key not found or negative error code */ extern long gcs_param_set (gcs_conn_t* conn, const char* key, const char *value); /*! returns the value of the key * * @return NULL if key not found */ extern const char* gcs_param_get (gcs_conn_t* conn, const char* key); /* Logging options */ extern long gcs_conf_set_log_file (FILE *file); extern long gcs_conf_set_log_callback (void (*logger) (int, const char*)); extern long gcs_conf_self_tstamp_on (); extern long gcs_conf_self_tstamp_off (); extern long gcs_conf_debug_on (); extern long gcs_conf_debug_off (); /* Sending options (deprecated, use gcs_param_set instead) */ /* Sets maximum DESIRED network packet size. * For best results should be multiple of MTU */ extern long gcs_conf_set_pkt_size (gcs_conn_t *conn, long pkt_size); #define GCS_DEFAULT_PKT_SIZE 64500 /* 43 Eth. frames to carry max IP packet */ /* * Configuration action */ /*! Possible node states */ typedef enum gcs_node_state { GCS_NODE_STATE_NON_PRIM, /// in non-primary configuration, outdated state GCS_NODE_STATE_PRIM, /// in primary conf, needs state transfer GCS_NODE_STATE_JOINER, /// in primary conf, receiving state transfer GCS_NODE_STATE_DONOR, /// joined, donating state transfer GCS_NODE_STATE_JOINED, /// contains full state GCS_NODE_STATE_SYNCED, /// syncronized with group GCS_NODE_STATE_MAX } gcs_node_state_t; /*! Convert state code to null-terminates string */ extern const char* gcs_node_state_to_str (gcs_node_state_t state); /*! New configuration action */ typedef struct gcs_act_conf { gcs_seqno_t seqno; //! last global seqno applied by this group gcs_seqno_t conf_id; //! configuration ID (-1 if non-primary) uint8_t uuid[GCS_UUID_LEN];/// group UUID long memb_num; //! number of members in configuration long my_idx; //! index of this node in the configuration gcs_node_state_t my_state; //! current node state int repl_proto_ver; //! replicator protocol version to use int appl_proto_ver; //! application protocol version to use char data[1]; /*! member array (null-terminated ID, name, * incoming address) */ } gcs_act_conf_t; struct gcs_stats { double send_q_len_avg; //! average send queue length per send call double recv_q_len_avg; //! average recv queue length per queued action double fc_paused; //! faction of time paused due to flow control size_t recv_q_size; //! current recv queue size long recv_q_len; //! current recv queue length long send_q_len; //! current send queue length long fc_sent; //! flow control stops sent long fc_received; //! flow control stops received }; /*! Fills stats struct and resets stats counters */ extern void gcs_get_stats (gcs_conn_t *conn, struct gcs_stats* stats); /*! A node with this name will be treated as a stateless arbitrator */ #define GCS_ARBITRATOR_NAME "garb" #ifdef __cplusplus } #endif #endif // _gcs_h_ percona-xtradb-cluster-galera/gcs/src/gcs_act.h0000644000000000000000000000101312247075736021764 0ustar rootroot00000000000000/* * Copyright (C) 2008-2011 Codership Oy * * $Id: gcs_act.h 2383 2011-09-24 11:14:06Z alex $ */ #ifndef _gcs_act_h_ #define _gcs_act_h_ #include "gcs.h" struct gcs_act { const void* buf; ssize_t buf_len; gcs_act_type_t type; }; struct gcs_act_rcvd { struct gcs_act act; const void* repl_buf; // pointer to local replicated buffer if any gcs_seqno_t id; // global total order seqno int sender_idx; }; #endif /* _gcs_act_h_ */ percona-xtradb-cluster-galera/gcs/src/gcs_act_proto.c0000644000000000000000000000771612247075736023222 0ustar rootroot00000000000000/* * Copyright (C) 2008-2013 Codership Oy * * $Id: gcs_act_proto.c 2963 2013-02-12 12:56:45Z alex $ */ /* * Interface to action protocol * (to be extended to support protocol versions, currently supports only v0) */ #include #include "gcs_act_proto.h" /* Version 0 header structure bytes: 00 01 07 08 11 12 15 16 19 20 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--- |PV| act_id | act_size | frag_no |AT|reserved| data... +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--- PV - protocol version AT - action type */ static const size_t PROTO_PV_OFFSET = 0; static const size_t PROTO_ACT_ID_OFFSET = 0; static const size_t PROTO_ACT_SIZE_OFFSET = 8; static const size_t PROTO_FRAG_NO_OFFSET = 12; static const size_t PROTO_AT_OFFSET = 16; static const size_t PROTO_DATA_OFFSET = 20; static const gcs_seqno_t PROTO_ACT_ID_MAX = 0x00FFFFFFFFFFFFLL; static const unsigned int PROTO_FRAG_NO_MAX = 0xFFFFFFFF; static const unsigned char PROTO_AT_MAX = 0xFF; static const int PROTO_VERSION = GCS_ACT_PROTO_MAX; #define PROTO_MAX_HDR_SIZE PROTO_DATA_OFFSET // for now /*! Writes header data into actual header of the message. * Remainig fragment buf and length is in frag->frag and frag->frag_len * * @return 0 on success */ long gcs_act_proto_write (gcs_act_frag_t* frag, void* buf, size_t buf_len) { #ifdef GCS_DEBUG_PROTO if ((frag->act_id > PROTO_ACT_ID_MAX) || (frag->act_size > GCS_MAX_ACT_SIZE) || (frag->frag_no > PROTO_FRAG_NO_MAX) || (frag->act_type > PROTO_AT_MAX)) { gu_error ("Exceeded protocol limits: %d(%d), %d(%d), %d(%d), %d(%d)", frag->act_id, PROTO_ACT_ID_MAX, frag->act_size, GCS_MAX_ACT_SIZE, frag->frag_no, PROTO_FRAG_NO_MAX, frag->act_type, PROTO_AT_MAX); return -EOVERFLOW; } if (frag->proto_ver != PROTO_VERSION) return -EPROTO; if (buf_len < PROTO_DATA_OFFSET) return -EMSGSIZE; #endif // assert (frag->act_size <= PROTO_ACT_SIZE_MAX); ((uint64_t*)buf)[0] = gu_be64(frag->act_id); ((uint32_t*)buf)[2] = htogl ((uint32_t)frag->act_size); ((uint32_t*)buf)[3] = htogl (frag->frag_no); ((uint8_t *)buf)[PROTO_PV_OFFSET] = frag->proto_ver; ((uint8_t *)buf)[PROTO_AT_OFFSET] = frag->act_type; frag->frag = (uint8_t*)buf + PROTO_DATA_OFFSET; frag->frag_len = buf_len - PROTO_DATA_OFFSET; return 0; } /*! Reads header data from the actual header of the message * Remainig fragment buf and length is in frag->frag and frag->frag_len * * @return 0 on success */ long gcs_act_proto_read (gcs_act_frag_t* frag, const void* buf, size_t buf_len) { frag->proto_ver = ((uint8_t*)buf)[PROTO_PV_OFFSET]; if (gu_unlikely(buf_len < PROTO_DATA_OFFSET)) { gu_error ("Action message too short: %zu, expected at least %d", buf_len, PROTO_DATA_OFFSET); return -EBADMSG; } if (gu_unlikely(frag->proto_ver > PROTO_VERSION)) { gu_error ("Bad protocol version %d, expected %d", frag->proto_ver, PROTO_VERSION); return -EPROTO; // this fragment should be dropped } ((uint8_t*)buf)[PROTO_PV_OFFSET] = 0x0; frag->act_id = gu_be64(*(uint64_t*)buf); frag->act_size = gtohl (((uint32_t*)buf)[2]); frag->frag_no = gtohl (((uint32_t*)buf)[3]); frag->act_type = ((uint8_t*)buf)[PROTO_AT_OFFSET]; frag->frag = ((uint8_t*)buf) + PROTO_DATA_OFFSET; frag->frag_len = buf_len - PROTO_DATA_OFFSET; /* return 0 or -EMSGSIZE */ return ((frag->act_size > GCS_MAX_ACT_SIZE) * -EMSGSIZE); } /*! Returns protocol header size */ long gcs_act_proto_hdr_size (long version) { if (gu_unlikely(GCS_ACT_PROTO_MAX < version)) return -EPROTONOSUPPORT; if (gu_unlikely(version < 0)) return PROTO_MAX_HDR_SIZE; // safe return PROTO_DATA_OFFSET; } percona-xtradb-cluster-galera/gcs/src/gcs_act_proto.h0000644000000000000000000000353612247075736023223 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_act_proto.h 2272 2011-07-28 23:24:41Z alex $ */ /* * Interface to action protocol * (to be extended to support protocol versions, currently supports only v0) */ #ifndef _gcs_act_proto_h_ #define _gcs_act_proto_h_ #include "gcs.h" // for gcs_seqno_t #include #include typedef uint8_t gcs_proto_t; /*! Supported protocol range (for now only version 0 is supported) */ #define GCS_ACT_PROTO_MAX 0 /*! Internal action fragment data representation */ typedef struct gcs_act_frag { gcs_seqno_t act_id; size_t act_size; const void* frag; // shall override it only once size_t frag_len; unsigned long frag_no; gcs_act_type_t act_type; int proto_ver; } gcs_act_frag_t; /*! Writes header data into actual header of the message. * Remainig fragment buf and length is in frag->frag and frag->frag_len */ extern long gcs_act_proto_write (gcs_act_frag_t* frag, void* buf, size_t buf_len); /*! Reads header data from the actual header of the message * Remainig fragment buf and length is in frag->frag and frag->frag_len */ extern long gcs_act_proto_read (gcs_act_frag_t* frag, const void* buf, size_t buf_len); /*! Increments fragment counter when action remains the same. * * @return non-negative counter value on success */ static inline long gcs_act_proto_inc (void* buf) { register uint32_t frag_no = gtohl(((uint32_t*)buf)[3]) + 1; #ifdef GCS_DEBUG_PROTO if (!frag_no) return -EOVERFLOW; #endif ((uint32_t*)buf)[3] = htogl(frag_no); return frag_no; } /*! Returns protocol header size */ extern long gcs_act_proto_hdr_size (long version); /*! Returns message protocol version */ static inline int gcs_act_proto_ver (void* buf) { return *((uint8_t*)buf); } #endif /* _gcs_act_proto_h_ */ percona-xtradb-cluster-galera/gcs/src/gcs_backend.c0000644000000000000000000000433412247075736022610 0ustar rootroot00000000000000/* * Copyright (C) 2008-2012 Codership Oy * * $Id: gcs_backend.c 3360 2013-11-04 02:58:48Z alex $ */ /*********************************************************/ /* This unit initializes the backend given backend URI */ /*********************************************************/ #include #include #include #include #include "gcs_backend.h" #include "gcs_dummy.h" #ifdef GCS_USE_SPREAD #include "gcs_spread.h" #endif /* GCS_USE_SPREAD */ #ifdef GCS_USE_VS #include "gcs_vs.h" #endif /* GCS_USE_VS */ #ifdef GCS_USE_GCOMM #include "gcs_gcomm.h" #endif /* GCS_USE_GCOMM */ /* Static array describing backend ID - open() pairs */ static struct { const char* id; gcs_backend_create_t create; } const backend[] = { #ifdef GCS_USE_GCOMM { "gcomm", gcs_gcomm_create}, #endif /* GCS_USE_GCOMM */ #ifdef GCS_USE_VS { "vsbes", gcs_vs_create }, #endif /* GCS_USE_VS */ #ifdef GCS_USE_SPREAD { "spread", gcs_spread_create }, #endif /* GCS_USE_SPREAD */ { "dummy", gcs_dummy_create }, { NULL, NULL } // terminating pair }; static const char backend_sep[] = "://"; /* Returns true if backend matches, false otherwise */ static bool backend_type_is (const char* uri, const char* type, const size_t len) { if (len == strlen(type)) { if (!strncmp (uri, type, len)) return true; } return false; } long gcs_backend_init (gcs_backend_t* const bk, const char* const uri, gu_config_t* const conf) { char* sep; assert (NULL != bk); assert (NULL != uri); sep = strstr (uri, backend_sep); if (NULL != sep) { size_t type_len = sep - uri; const char* addr = sep + strlen(backend_sep); long i; /* try to match any of specified backends */ for (i = 0; backend[i].id != NULL; i++) { if (backend_type_is (uri, backend[i].id, type_len)) return backend[i].create (bk, addr, conf); } /* no backends matched */ gu_error ("Backend not supported: %s", uri); return -ESOCKTNOSUPPORT; } gu_error ("Invalid backend URI: %s", uri); return -EINVAL; } percona-xtradb-cluster-galera/gcs/src/gcs_backend.h0000644000000000000000000001257212247075736022620 0ustar rootroot00000000000000/* * Copyright (C) 2008-2012 Codership Oy * * $Id: gcs_backend.h 2923 2012-12-30 11:10:30Z alex $ */ /* * This header defines GC backend interface. * Since we can't know backend context in advance, * we have to use type void*. Kind of unsafe. */ #ifndef _gcs_backend_h_ #define _gcs_backend_h_ #include "gcs.h" #include "gcs_recv_msg.h" #include #include typedef struct gcs_backend_conn gcs_backend_conn_t; typedef struct gcs_backend gcs_backend_t; /* * The macros below are declarations of backend functions * (kind of function signatures) */ /*! Allocates backend context and sets up the backend structure */ #define GCS_BACKEND_CREATE_FN(fn) \ long fn (gcs_backend_t* backend, \ const char* const addr, \ gu_config_t* const cnf) /*! Deallocates backend context */ #define GCS_BACKEND_DESTROY_FN(fn) \ long fn (gcs_backend_t* backend) /*! Puts backend handle into operating state */ #define GCS_BACKEND_OPEN_FN(fn) \ long fn (gcs_backend_t* backend, \ const char* const channel, \ bool const bootstrap) /*! Puts backend handle into non-operating state */ #define GCS_BACKEND_CLOSE_FN(fn) \ long fn (gcs_backend_t* backend) /*! * Send a message from the backend. * * @param backend * a pointer to the backend handle * @param buf * a buffer to copy the message to * @param len * length of the supplied buffer * @param msg_type * type of the message * @return * negative error code in case of error * OR * amount of bytes sent */ #define GCS_BACKEND_SEND_FN(fn) \ long fn (gcs_backend_t* const backend, \ const void* const buf, \ size_t const len, \ gcs_msg_type_t const msg_type) /*! * Receive a message from the backend. * * @param backend * a pointer to the backend object * @param buf * a buffer to copy the message to * @param len * length of the supplied buffer * @param msg_type * type of the message * @param sender_id * unique sender ID in this configuration * @param timeout * absolute timeout date in nanoseconds * @return * negative error code in case of error * OR * the length of the message, so if it is bigger * than len, it has to be reread with a bigger buffer */ #define GCS_BACKEND_RECV_FN(fn) \ long fn (gcs_backend_t* const backend, \ gcs_recv_msg_t* const msg, \ long long const timeout) /* for lack of better place define it here */ static const long GCS_SENDER_NONE = -1; /** When there's no sender */ /*! Returns symbolic name of the backend */ #define GCS_BACKEND_NAME_FN(fn) \ const char* fn (void) /*! * Returns the size of the message such that resulting network packet won't * exceed given value (basically, pkt_size - headers). * * @param backend * backend handle * @param pkt_size * desired size of a network packet * @return * - message size coresponding to the desired network packet size OR * - maximum message size the backend supports if requested packet size * is too big OR * - negative amount by which the packet size must be increased in order * to send at least 1 byte. */ #define GCS_BACKEND_MSG_SIZE_FN(fn) \ long fn (gcs_backend_t* const backend, \ long const pkt_size) /*! * @param backend * backend handle * @param key * parameter name * @param value * parameter value * @return 1 if parameter not recognized, 0 in case of success and negative * error code in case of error */ #define GCS_BACKEND_PARAM_SET_FN(fn) \ long fn (gcs_backend_t* backend, \ const char* key, \ const char* value) /*! * @param backend * backend handle * @param key * parameter name * @return NULL if parameter not recognized */ #define GCS_BACKEND_PARAM_GET_FN(fn) \ const char* fn (gcs_backend_t* backend, \ const char* key) typedef GCS_BACKEND_CREATE_FN ((*gcs_backend_create_t)); typedef GCS_BACKEND_DESTROY_FN ((*gcs_backend_destroy_t)); typedef GCS_BACKEND_OPEN_FN ((*gcs_backend_open_t)); typedef GCS_BACKEND_CLOSE_FN ((*gcs_backend_close_t)); typedef GCS_BACKEND_SEND_FN ((*gcs_backend_send_t)); typedef GCS_BACKEND_RECV_FN ((*gcs_backend_recv_t)); typedef GCS_BACKEND_NAME_FN ((*gcs_backend_name_t)); typedef GCS_BACKEND_MSG_SIZE_FN ((*gcs_backend_msg_size_t)); typedef GCS_BACKEND_PARAM_SET_FN ((*gcs_backend_param_set_t)); typedef GCS_BACKEND_PARAM_GET_FN ((*gcs_backend_param_get_t)); struct gcs_backend { gcs_backend_conn_t* conn; gcs_backend_open_t open; gcs_backend_close_t close; gcs_backend_destroy_t destroy; gcs_backend_send_t send; gcs_backend_recv_t recv; gcs_backend_name_t name; gcs_backend_msg_size_t msg_size; gcs_backend_param_set_t param_set; gcs_backend_param_get_t param_get; }; /*! * Initializes preallocated backend object and opens backend connection * (sort of like 'new') */ long gcs_backend_init (gcs_backend_t* bk, const char* uri, gu_config_t* cnf); #endif /* _gcs_backend_h_ */ percona-xtradb-cluster-galera/gcs/src/gcs_comp_msg.c0000644000000000000000000000675612247075736023037 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_comp_msg.c 2734 2012-03-08 10:47:53Z teemu $ */ /* * Interface to membership messages - implementation * */ #include #include #include #include #define GCS_COMP_MSG_ACCESS #include "gcs_comp_msg.h" static inline long comp_msg_size (long memb_num) { return (sizeof(gcs_comp_msg_t) + memb_num * sizeof(gcs_comp_memb_t)); } /*! Allocates membership object and zeroes it */ gcs_comp_msg_t* gcs_comp_msg_new (bool prim, bool bootstrap, long my_idx, long memb_num) { gcs_comp_msg_t* ret; assert ((memb_num > 0 && my_idx >= 0) || (memb_num == 0 && my_idx == -1)); ret = gu_calloc (1, comp_msg_size(memb_num)); if (NULL != ret) { ret->primary = prim; ret->bootstrap = bootstrap; ret->my_idx = my_idx; ret->memb_num = memb_num; } return ret; } gcs_comp_msg_t* gcs_comp_msg_leave () { return gcs_comp_msg_new (false, false, -1, 0); } /*! Destroys component message */ void gcs_comp_msg_delete (gcs_comp_msg_t* comp) { gu_free (comp); } /*! Returns total size of the component message */ long gcs_comp_msg_size (const gcs_comp_msg_t* comp) { assert (comp); return comp_msg_size (comp->memb_num); } /*! Adds a member to the component message * Returns an index of the member or negative error code */ long gcs_comp_msg_add (gcs_comp_msg_t* comp, const char* id) { size_t id_len; long i; assert (comp); assert (id); /* check id length */ id_len = strlen (id); if (!id_len) return -EINVAL; if (id_len > GCS_COMP_MEMB_ID_MAX_LEN) return -ENAMETOOLONG; long free_slot = -1; /* find the free id slot and check for id uniqueness */ for (i = 0; i < comp->memb_num; i++) { if (0 == comp->memb[i].id[0] && free_slot < 0) free_slot = i; if (0 == strcmp (comp->memb[i].id, id)) return -ENOTUNIQ; } if (free_slot < 0) return -1; memcpy (comp->memb[free_slot].id, id, id_len); return free_slot; } /*! Creates a copy of the component message */ gcs_comp_msg_t* gcs_comp_msg_copy (const gcs_comp_msg_t* comp) { size_t size = gcs_comp_msg_size(comp); gcs_comp_msg_t* ret = gu_malloc (size); if (ret) memcpy (ret, comp, size); return ret; } /*! Returns member ID by index, NULL if none */ const char* gcs_comp_msg_id (const gcs_comp_msg_t* comp, long idx) { if (0 <= idx && idx < comp->memb_num) return comp->memb[idx].id; else return NULL; } /*! Returns member index by ID, -1 if none */ long gcs_comp_msg_idx (const gcs_comp_msg_t* comp, const char* id) { size_t id_len = strlen(id); long idx = comp->memb_num; if (id_len > 0 && id_len <= GCS_COMP_MEMB_ID_MAX_LEN) for (idx = 0; idx < comp->memb_num; idx++) if (0 == strcmp (comp->memb[idx].id, id)) break; if (comp->memb_num == idx) return -1; else return idx; } /*! Returns primary status of the component */ bool gcs_comp_msg_primary (const gcs_comp_msg_t* comp) { return comp->primary; } /*! Retruns bootstrap flag of the component */ bool gcs_comp_msg_bootstrap(const gcs_comp_msg_t* comp) { return comp->bootstrap; } /*! Returns our own index in the membership */ long gcs_comp_msg_self (const gcs_comp_msg_t* comp) { return comp->my_idx; } /*! Returns number of members in the component */ long gcs_comp_msg_num (const gcs_comp_msg_t* comp) { return comp->memb_num; } percona-xtradb-cluster-galera/gcs/src/gcs_comp_msg.h0000644000000000000000000000536712247075736023041 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_comp_msg.h 2745 2012-03-17 00:00:23Z alex $ */ /* * Interface to component messages * */ #ifndef _gcs_component_h_ #define _gcs_component_h_ #include #include #define GCS_COMP_MEMB_ID_MAX_LEN 39 // should accommodate human readable UUID #ifdef GCS_COMP_MSG_ACCESS typedef struct gcs_comp_memb { char id[GCS_COMP_MEMB_ID_MAX_LEN + 1]; /// ID assigned by the backend } gcs_comp_memb_t; typedef struct gcs_comp_msg { bool primary; /// 1 if we have a quorum, 0 if not bool bootstrap; /// 1 if primary was bootstrapped long my_idx; /// this node's index in membership long memb_num; /// number of members in configuration gcs_comp_memb_t memb[1]; /// member array } gcs_comp_msg_t; #else typedef struct gcs_comp_msg gcs_comp_msg_t; #endif /*! Allocates new component message * @param prim whether component is primary or not * @param bootstrap whether prim was bootstrapped * @param my_idx this node index in the membership * @param memb_num number of members in component * @return * allocated message buffer */ extern gcs_comp_msg_t* gcs_comp_msg_new (bool prim, bool bootstrap, long my_idx, long memb_num); /*! Standard empty "leave" component message (to be returned on shutdown) */ extern gcs_comp_msg_t* gcs_comp_msg_leave (); /*! Destroys component message */ extern void gcs_comp_msg_delete (gcs_comp_msg_t* comp); /*! Adds a member to the component message * Returns an index of the member or negative error code: * -1 when membership is full * -ENOTUNIQ when name collides with one that is in membership already * -ENAMETOOLONG wnen memory allocation for new name fails */ extern long gcs_comp_msg_add (gcs_comp_msg_t* comp, const char* id); /*! Returns total size of the component message */ extern long gcs_comp_msg_size (const gcs_comp_msg_t* comp); /*! Creates a copy of the component message */ extern gcs_comp_msg_t* gcs_comp_msg_copy (const gcs_comp_msg_t* comp); /*! Returns member ID by index, NULL if none */ extern const char* gcs_comp_msg_id (const gcs_comp_msg_t* comp, long idx); /*! Returns member index by ID, -1 if none */ extern long gcs_comp_msg_idx (const gcs_comp_msg_t* comp, const char* id); /*! Returns primary status of the component */ extern bool gcs_comp_msg_primary (const gcs_comp_msg_t* comp); /*! Returns bootstrap flag */ extern bool gcs_comp_msg_bootstrap(const gcs_comp_msg_t* comp); /*! Returns our own idx */ extern long gcs_comp_msg_self (const gcs_comp_msg_t* comp); /*! Returns number of members in the component */ extern long gcs_comp_msg_num (const gcs_comp_msg_t* comp); #endif /* _gcs_component_h_ */ percona-xtradb-cluster-galera/gcs/src/gcs_conf.c0000644000000000000000000000120612247075736022141 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_conf.c 458 2008-10-18 15:27:53Z alex $ */ /* Logging options */ #include #include "gcs.h" long gcs_conf_set_log_file (FILE *file) { return gu_conf_set_log_file (file); } long gcs_conf_set_log_callback (void (*logger) (int, const char*)) { return gu_conf_set_log_callback (logger); } long gcs_conf_self_tstamp_on () { return gu_conf_self_tstamp_on (); } long gcs_conf_self_tstamp_off () { return gu_conf_self_tstamp_off (); } long gcs_conf_debug_on () { return gu_conf_debug_on (); } long gcs_conf_debug_off () { return gu_conf_debug_off (); } percona-xtradb-cluster-galera/gcs/src/gcs_core.c0000644000000000000000000012102212247075736022143 0ustar rootroot00000000000000/* * Copyright (C) 2008-2012 Codership Oy * * $Id: gcs_core.c 2923 2012-12-30 11:10:30Z alex $ * * * Implementation of the generic communication layer. * See gcs_core.h */ #include // for mempcpy #include #define GCS_COMP_MSG_ACCESS #include "gcs_core.h" #include "gcs_backend.h" #include "gcs_comp_msg.h" #include "gcs_fifo_lite.h" #include "gcs_group.h" #include "gcs_gcache.h" const size_t CORE_FIFO_LEN = (1 << 10); // 1024 elements (no need to have more) const size_t CORE_INIT_BUF_SIZE = (1 << 16); // 65K - IP packet size typedef enum core_state { CORE_PRIMARY, CORE_EXCHANGE, CORE_NON_PRIMARY, CORE_CLOSED, CORE_DESTROYED } core_state_t; struct gcs_core { gu_config_t* config; gcache_t* cache; /* connection per se */ long prim_comp_no; core_state_t state; /* protocol */ int proto_ver; /* send part */ gu_mutex_t send_lock; // serves 3 purposes: // 1) serializes access to backend send() call // 2) synchronizes with configuration changes // 3) synchronizes with close() call void* send_buf; size_t send_buf_len; gcs_seqno_t send_act_no; /* recv part */ gcs_recv_msg_t recv_msg; /* local action FIFO */ gcs_fifo_lite_t* fifo; /* group context */ gcs_group_t group; /* backend part */ size_t msg_size; gcs_backend_t backend; // message IO context #ifdef GCS_CORE_TESTING gu_lock_step_t ls; // to lock-step in unit tests #endif }; // this is to pass local action info from send to recv thread. typedef struct core_act { gcs_seqno_t sent_act_id; const void* action; size_t action_size; } core_act_t; typedef struct causal_act { gcs_seqno_t* act_id; gu_mutex_t* mtx; gu_cond_t* cond; } causal_act_t; static int const GCS_PROTO_MAX = 0; gcs_core_t* gcs_core_create (gu_config_t* const conf, gcache_t* const cache, const char* const node_name, const char* const inc_addr, int const repl_proto_ver, int const appl_proto_ver) { assert (conf); gcs_core_t* core = GU_CALLOC (1, gcs_core_t); if (NULL != core) { core->config = conf; core->cache = cache; // Need to allocate something, otherwise Spread 3.17.3 freaks out. core->recv_msg.buf = gu_malloc(CORE_INIT_BUF_SIZE); if (core->recv_msg.buf) { core->recv_msg.buf_len = CORE_INIT_BUF_SIZE; core->send_buf = GU_CALLOC(CORE_INIT_BUF_SIZE, char); if (core->send_buf) { core->send_buf_len = CORE_INIT_BUF_SIZE; core->fifo = gcs_fifo_lite_create (CORE_FIFO_LEN, sizeof (core_act_t)); if (core->fifo) { gu_mutex_init (&core->send_lock, NULL); core->proto_ver = -1; // shall be bumped in gcs_group_act_conf() gcs_group_init (&core->group, cache, node_name, inc_addr, GCS_PROTO_MAX, repl_proto_ver, appl_proto_ver); core->state = CORE_CLOSED; core->send_act_no = 1; // 0 == no actions sent #ifdef GCS_CORE_TESTING gu_lock_step_init (&core->ls); #endif return core; // success } gu_free (core->send_buf); } gu_free (core->recv_msg.buf); } gu_free (core); } return NULL; // failure } long gcs_core_init (gcs_core_t* core, gcs_seqno_t seqno, const gu_uuid_t* uuid) { if (core->state == CORE_CLOSED) { return gcs_group_init_history (&core->group, seqno, uuid); } else { gu_error ("State must be CLOSED"); if (core->state < CORE_CLOSED) return -EBUSY; else // DESTROYED return -EBADFD; } } long gcs_core_open (gcs_core_t* core, const char* channel, const char* url, bool const bstrap) { long ret; if (core->state != CORE_CLOSED) { gu_debug ("gcs_core->state isn't CLOSED: %d", core->state); return -EBADFD; } if (core->backend.conn) { assert (core->backend.destroy); core->backend.destroy (&core->backend); memset (&core->backend, 0, sizeof(core->backend)); } gu_debug ("Initializing backend IO layer"); if (!(ret = gcs_backend_init (&core->backend, url, core->config))){ assert (NULL != core->backend.conn); if (!(ret = core->backend.open (&core->backend, channel, bstrap))) { gcs_fifo_lite_open (core->fifo); core->state = CORE_NON_PRIMARY; } else { gu_error ("Failed to open backend connection: %d (%s)", ret, strerror(-ret)); core->backend.destroy (&core->backend); } } else { gu_error ("Failed to initialize backend using '%s': %d (%s)", url, ret, strerror(-ret)); } return ret; } /* Translates different core states into standard errors */ static inline ssize_t core_error (core_state_t state) { switch (state) { case CORE_EXCHANGE: return -EAGAIN; case CORE_NON_PRIMARY: return -ENOTCONN; case CORE_CLOSED: return -ECONNABORTED; case CORE_DESTROYED: return -EBADFD; default: assert(0); return -ENOTRECOVERABLE; } } /*! * Performs an attempt at sending a message (action fragment) with all * required checks while holding a lock, ensuring exclusive access to backend. * * restart flag may be raised if configuration changes and new nodes are * added - that would require all previous members to resend partially sent * actions. */ static inline ssize_t core_msg_send (gcs_core_t* core, const void* msg, size_t msg_len, gcs_msg_type_t msg_type) { ssize_t ret; if (gu_unlikely(0 != gu_mutex_lock (&core->send_lock))) abort(); { if (gu_likely((CORE_PRIMARY == core->state) || (CORE_EXCHANGE == core->state && GCS_MSG_STATE_MSG == msg_type))) { ret = core->backend.send (&core->backend, msg, msg_len, msg_type); if (ret > 0 && ret != (ssize_t)msg_len && GCS_MSG_ACTION != msg_type) { // could not send message in one piece gu_error ("Failed to send complete message of %s type: " "sent %zd out of %zu bytes.", gcs_msg_type_string[msg_type], ret, msg_len); ret = -EMSGSIZE; } } else { ret = core_error (core->state); if (ret >= 0) { gu_fatal ("GCS internal state inconsistency: " "expected error condition."); abort(); // ret = -ENOTRECOVERABLE; } } } gu_mutex_unlock (&core->send_lock); // gu_debug ("returning: %d (%s)", ret, strerror(-ret)); return ret; } /*! * Repeats attempt at sending the message if -EAGAIN was returned * by core_msg_send() */ static inline ssize_t core_msg_send_retry (gcs_core_t* core, const void* buf, size_t buf_len, gcs_msg_type_t type) { ssize_t ret; while ((ret = core_msg_send (core, buf, buf_len, type)) == -EAGAIN) { /* wait for primary configuration - sleep 0.01 sec */ gu_debug ("Backend requested wait"); usleep (10000); } // gu_debug ("returning: %d (%s)", ret, strerror(-ret)); return ret; } ssize_t gcs_core_send (gcs_core_t* const conn, const void* const action, size_t act_size, gcs_act_type_t const act_type) { const char* act = action; ssize_t ret = 0; size_t sent = 0; gcs_act_frag_t frg; size_t send_size; const unsigned char proto_ver = conn->proto_ver; const size_t hdr_size = gcs_act_proto_hdr_size (proto_ver); core_act_t* local_act; assert (act != NULL); assert (act_size > 0); /* * Action header will be replicated with every message. * It may seem like an extra overhead, but it is tiny * so far and simplifies A LOT. */ /* Initialize action constants */ frg.act_size = act_size; frg.act_type = act_type; frg.act_id = conn->send_act_no; /* incremented for every new action */ frg.frag_no = 0; frg.proto_ver = proto_ver; if ((ret = gcs_act_proto_write (&frg, conn->send_buf, conn->send_buf_len))) goto out; if ((local_act = gcs_fifo_lite_get_tail (conn->fifo))) { *local_act = (core_act_t){ conn->send_act_no, act, act_size }; gcs_fifo_lite_push_tail (conn->fifo); } else { ret = core_error (conn->state); gu_error ("Failed to access core FIFO: %d (%s)", ret, strerror (-ret)); goto out; } do { const size_t chunk_size = act_size < frg.frag_len ? act_size : frg.frag_len; /* Here is the only time we have to cast frg.frag */ memcpy ((char*)frg.frag, act, chunk_size); send_size = hdr_size + chunk_size; #ifdef GCS_CORE_TESTING gu_lock_step_wait (&conn->ls); // pause after every fragment gu_info ("Sent %p of size %zu. Total sent: %zu, left: %zu", (char*)conn->send_buf + hdr_size, chunk_size, sent, act_size); #endif ret = core_msg_send_retry (conn, conn->send_buf, send_size, GCS_MSG_ACTION); #ifdef GCS_CORE_TESTING // gu_lock_step_wait (&conn->ls); // pause after every fragment // gu_info ("Sent %p of size %zu, ret: %zd. Total sent: %zu, left: %zu", // conn->send_buf + hdr_size, chunk_size, ret, sent, act_size); #endif if (gu_likely(ret > (ssize_t)hdr_size)) { assert (ret <= (ssize_t)send_size); ret -= hdr_size; sent += ret; act += ret; act_size -= ret; // adjust frag_len, don't copy more than we could send frg.frag_len = ret; } else { if (ret >= 0) { // we managed to send less than a header, fail gu_fatal ("Cannot send message: header is too big"); ret = -ENOTRECOVERABLE; } /* At this point we have an unsent action in local FIFO * and parts of this action already could have been received * by other group members. * (first parts of action might be even received by this node, * so that there is nothing to remove, but we cannot know for sure) * * 1. Action will never be received completely by this node. Hence * action must be removed from fifo on behalf of sending thr.: */ gcs_fifo_lite_remove (conn->fifo); /* 2. Members will have to discard received fragments. * Two reasons could lead us here: new member(s) in configuration * change or broken connection (leave group). In both cases other * members discard fragments */ goto out; } } while (act_size && gcs_act_proto_inc(conn->send_buf)); assert (0 == act_size); /* successfully sent action, increment send counter */ conn->send_act_no++; ret = sent; out: // gu_debug ("returning: %d (%s)", ret, strerror(-ret)); return ret; } /* A helper for gcs_core_recv(). * Deals with fetching complete message from backend * and reallocates recv buf if needed */ static inline long core_msg_recv (gcs_backend_t* backend, gcs_recv_msg_t* recv_msg, long long timeout) { long ret; ret = backend->recv (backend, recv_msg, timeout); while (gu_unlikely(ret > recv_msg->buf_len)) { /* recv_buf too small, reallocate */ /* sometimes - like in case of component message, we may need to * do reallocation 2 times. This should be fixed in backend */ void* msg = gu_realloc (recv_msg->buf, ret); gu_debug ("Reallocating buffer from %d to %d bytes", recv_msg->buf_len, ret); if (msg) { /* try again */ recv_msg->buf = msg; recv_msg->buf_len = ret; ret = backend->recv (backend, recv_msg, timeout); /* should be either an error or an exact match */ assert ((ret < 0) || (ret >= recv_msg->buf_len)); } else { /* realloc unsuccessfull, old recv_buf remains */ gu_error ("Failed to reallocate buffer to %d bytes", ret); ret = -ENOMEM; break; } } if (gu_unlikely(ret < 0)) { gu_debug ("returning %d: %s\n", ret, strerror(-ret)); } return ret; } /*! * Helper for gcs_core_recv(). Handles GCS_MSG_ACTION. * * @return action size, negative error code or 0 to continue. */ static inline ssize_t core_handle_act_msg (gcs_core_t* core, struct gcs_recv_msg* msg, struct gcs_act_rcvd* act) { ssize_t ret = -1; gcs_group_t* group = &core->group; gcs_act_frag_t frg; bool my_msg = (gcs_group_my_idx(group) == msg->sender_idx); assert (GCS_MSG_ACTION == msg->type); if ((CORE_PRIMARY == core->state) || my_msg){//should always handle own msgs if (gu_unlikely(GCS_ACT_PROTO_MAX < gcs_act_proto_ver(msg->buf))) { // this is most likely due to #482 gu_info ("Message with protocol version %d > max supported: %d. " "Need to abort.", gcs_act_proto_ver(msg->buf), GCS_ACT_PROTO_MAX); return -ENOTRECOVERABLE; } ret = gcs_act_proto_read (&frg, msg->buf, msg->size); if (gu_unlikely(ret)) { gu_fatal ("Error parsing action fragment header: %zd (%s).", ret, strerror (-ret)); assert (0); return -ENOTRECOVERABLE; } ret = gcs_group_handle_act_msg (group, &frg, msg, act); if (ret > 0) { /* complete action received */ assert (ret == act->act.buf_len); #ifndef GCS_FOR_GARB assert (NULL != act->act.buf); #else assert (NULL == act->act.buf); // act->act.buf_len = 0; #endif act->sender_idx = msg->sender_idx; if (gu_likely(!my_msg)) { /* foreign action, must be passed from gcs_group */ assert (GCS_ACT_TORDERED != act->act.type || act->id > 0); } else { /* local action, get from FIFO, should be there already */ core_act_t* local_act; gcs_seqno_t sent_act_id; if ((local_act = gcs_fifo_lite_get_head (core->fifo))){ act->repl_buf = local_act->action; act->act.buf_len = local_act->action_size; sent_act_id = local_act->sent_act_id; gcs_fifo_lite_pop_head (core->fifo); assert (NULL != act->repl_buf); /* NOTE! local_act cannot be used after this point */ /* sanity check */ if (gu_unlikely(sent_act_id != frg.act_id)) { gu_fatal ("FIFO violation: expected sent_act_id %lld " "found %lld", sent_act_id, frg.act_id); ret = -ENOTRECOVERABLE; } if (gu_unlikely(act->act.buf_len != ret)) { gu_fatal ("Send/recv action size mismatch: %zd/%zd", act->act.buf_len, ret); ret = -ENOTRECOVERABLE; } } else { gu_fatal ("FIFO violation: queue empty when local action " "received"); ret = -ENOTRECOVERABLE; } assert (act->id < 0 || CORE_PRIMARY == core->state); if (gu_unlikely(CORE_PRIMARY != core->state)) { // there can be a tiny race with gcs_core_close(), // so CORE_CLOSED allows TO delivery. assert (act->id < 0 /*#275|| CORE_CLOSED == core->state*/); if (act->id < 0) act->id = core_error (core->state); } } if (gu_unlikely(GCS_ACT_STATE_REQ == act->act.type && ret > 0)) { #ifdef GCS_FOR_GARB /* ignoring state requests from other nodes (not allocated) */ if (my_msg) { act->act.buf = act->repl_buf; #endif ret = gcs_group_handle_state_request (group, act); assert (ret <= 0 || ret == act->act.buf_len); #ifdef GCS_FOR_GARB if (ret < 0) gu_fatal ("Handling state request failed: %d",ret); act->act.buf = NULL; } else { act->act.buf_len = 0; act->act.type = GCS_ACT_ERROR; act->id = GCS_SEQNO_ILL; act->sender_idx = -1; ret = 0; } #endif } // gu_debug ("Received action: seqno: %lld, sender: %d, size: %d, " // "act: %p", act->id, msg->sender_idx, ret, act->buf); // gu_debug ("%s", (char*) act->buf); } else if (gu_unlikely(ret < 0)){ gu_fatal ("Failed to handle action fragment: %zd (%s)", ret, strerror(-ret)); assert (0); return -ENOTRECOVERABLE; } } else { /* Non-primary conf, foreign message - ignore */ gu_debug ("Action message in non-primary configuration from " "member %d", msg->sender_idx); ret = 0; } #ifndef NDEBUG if (ret <= 0) { assert (GCS_SEQNO_ILL == act->id); assert (GCS_ACT_ERROR == act->act.type); } #endif return ret; } /*! * Helper for gcs_core_recv(). Handles GCS_MSG_LAST. * * @return action size, negative error code or 0 to continue. */ static ssize_t core_handle_last_msg (gcs_core_t* core, struct gcs_recv_msg* msg, struct gcs_act* act) { assert (GCS_MSG_LAST == msg->type); if (gcs_group_is_primary(&core->group)) { gcs_seqno_t commit_cut = gcs_group_handle_last_msg (&core->group, msg); if (commit_cut) { /* commit cut changed */ if ((act->buf = malloc (sizeof (commit_cut)))) { act->type = GCS_ACT_COMMIT_CUT; /* #701 - everything that goes into the action buffer * is expected to be serialized. */ *((gcs_seqno_t*)act->buf) = gcs_seqno_htog(commit_cut); act->buf_len = sizeof(commit_cut); return act->buf_len; } else { gu_fatal ("Out of memory for GCS_ACT_COMMIT_CUT"); return -ENOMEM; } } } else { /* Non-primary - ignore last message */ gu_warn ("Last Applied Action message " "in non-primary configuration from member %d", msg->sender_idx); } return 0; } /*! * Helper for gcs_core_recv(). Handles GCS_MSG_COMPONENT. * * @return action size, negative error code or 0 to continue. */ static ssize_t core_handle_comp_msg (gcs_core_t* core, struct gcs_recv_msg* msg, struct gcs_act* act) { ssize_t ret = 0; gcs_group_t* group = &core->group; assert (GCS_MSG_COMPONENT == msg->type); if (msg->size < (ssize_t)sizeof(gcs_comp_msg_t)) { gu_error ("Malformed component message. Ignoring"); return 0; } ret = gcs_group_handle_comp_msg (group, msg->buf); switch (ret) { case GCS_GROUP_PRIMARY: /* New primary configuration. This happens if: * - this is first node in group OR * - some nodes disappeared no new nodes appeared * No need for state exchange, return new conf_act right away */ if (gu_mutex_lock (&core->send_lock)) abort(); { assert (CORE_EXCHANGE != core->state); if (CORE_NON_PRIMARY == core->state) core->state = CORE_PRIMARY; } gu_mutex_unlock (&core->send_lock); int gcs_proto_ver; ret = gcs_group_act_conf (group, act, &gcs_proto_ver); if (ret < 0) { gu_fatal ("Failed create PRIM CONF action: %d (%s)", ret, strerror (-ret)); assert (0); ret = -ENOTRECOVERABLE; } else { if (0 == gcs_proto_ver) { core->proto_ver = 0; } } assert (ret == act->buf_len); break; case GCS_GROUP_WAIT_STATE_UUID: /* New members, need state exchange. If representative, send UUID */ if (gu_mutex_lock (&core->send_lock)) abort(); { // if state is CLOSED or DESTROYED we don't do anything if (CORE_CLOSED > core->state) { if (0 == gcs_group_my_idx(group)) { // I'm representative gu_uuid_t uuid; gu_uuid_generate (&uuid, NULL, 0); ret = core->backend.send (&core->backend, &uuid, sizeof(uuid), GCS_MSG_STATE_UUID); if (ret < 0) { // if send() failed, it means new configuration change // is on the way. Probably should ignore. gu_warn ("Failed to send state UUID: %d (%s)", ret, strerror (-ret)); } else { gu_info ("STATE_EXCHANGE: sent state UUID: " GU_UUID_FORMAT, GU_UUID_ARGS(&uuid)); } } else { gu_info ("STATE EXCHANGE: Waiting for state UUID."); } core->state = CORE_EXCHANGE; } ret = 0; // no action to return, continue } gu_mutex_unlock (&core->send_lock); break; case GCS_GROUP_NON_PRIMARY: /* Lost primary component */ if (gu_mutex_lock (&core->send_lock)) abort(); { if (core->state < CORE_CLOSED) { ret = gcs_group_act_conf (group, act, &core->proto_ver); if (ret < 0) { gu_fatal ("Failed create NON-PRIM CONF action: %d (%s)", ret, strerror (-ret)); assert (0); ret = -ENOTRECOVERABLE; } if (gcs_group_my_idx(group) == -1) { // self-leave core->state = CORE_CLOSED; } else { // regular non-prim core->state = CORE_NON_PRIMARY; } } else { // ignore in production? assert(0); } } gu_mutex_unlock (&core->send_lock); assert (ret == act->buf_len); break; case GCS_GROUP_WAIT_STATE_MSG: gu_fatal ("Internal error: gcs_group_handle_comp() returned " "WAIT_STATE_MSG. Can't continue."); ret = -ENOTRECOVERABLE; assert(0); default: gu_fatal ("Failed to handle component message: %d (%s)!", ret, strerror (-ret)); assert(0); } return ret; } /*! * Helper for gcs_core_recv(). Handles GCS_MSG_STATE_UUID. * * @return negative error code or 0 to continue. */ static ssize_t core_handle_uuid_msg (gcs_core_t* core, gcs_recv_msg_t* msg) { ssize_t ret = 0; gcs_group_t* group = &core->group; assert (GCS_MSG_STATE_UUID == msg->type); if (GCS_GROUP_WAIT_STATE_UUID == gcs_group_state (group)) { ret = gcs_group_handle_uuid_msg (group, msg); switch (ret) { case GCS_GROUP_WAIT_STATE_MSG: // Need to send state message for state exchange { gcs_state_msg_t* state = gcs_group_get_state (group); if (state) { size_t state_len = gcs_state_msg_len (state); uint8_t state_buf[state_len]; const gu_uuid_t* state_uuid = gcs_state_msg_uuid (state); gcs_state_msg_write (state_buf, state); ret = core_msg_send_retry (core, state_buf, state_len, GCS_MSG_STATE_MSG); if (ret > 0) { gu_info ("STATE EXCHANGE: sent state msg: " GU_UUID_FORMAT, GU_UUID_ARGS(state_uuid)); } else { // This may happen if new configuraiton chage goes on. // What shall we do in this case? Is it unrecoverable? gu_error ("STATE EXCHANGE: failed for: "GU_UUID_FORMAT ": %d (%s)", GU_UUID_ARGS(state_uuid), ret, strerror(-ret)); } gcs_state_msg_destroy (state); } else { gu_fatal ("Failed to allocate state object."); ret = -ENOTRECOVERABLE; } } break; case GCS_GROUP_WAIT_STATE_UUID: // In case of stray state uuid message break; default: assert(ret < 0); gu_error ("Failed to handle state UUID: %d (%s)", ret, strerror (-ret)); } } return ret; } /*! * Helper for gcs_core_recv(). Handles GCS_MSG_STATE_MSG. * * @return action size, negative error code or 0 to continue. */ static ssize_t core_handle_state_msg (gcs_core_t* core, struct gcs_recv_msg* msg, struct gcs_act* act) { ssize_t ret = 0; gcs_group_t* group = &core->group; assert (GCS_MSG_STATE_MSG == msg->type); if (GCS_GROUP_WAIT_STATE_MSG == gcs_group_state (group)) { ret = gcs_group_handle_state_msg (group, msg); switch (ret) { case GCS_GROUP_PRIMARY: case GCS_GROUP_NON_PRIMARY: // state exchange is over, create configuration action if (gu_mutex_lock (&core->send_lock)) abort(); { // if core is closing we do nothing if (CORE_CLOSED > core->state) { assert (CORE_EXCHANGE == core->state); switch (ret) { case GCS_GROUP_PRIMARY: core->state = CORE_PRIMARY; break; case GCS_GROUP_NON_PRIMARY: core->state = CORE_NON_PRIMARY; break; default: assert (0); } } } gu_mutex_unlock (&core->send_lock); ret = gcs_group_act_conf (group, act, &core->proto_ver); if (ret < 0) { gu_fatal ("Failed create CONF action: %d (%s)", ret, strerror (-ret)); assert (0); ret = -ENOTRECOVERABLE; } assert (ret == act->buf_len); break; case GCS_GROUP_WAIT_STATE_MSG: // waiting for more state messages ret = 0; break; default: assert (ret < 0); gu_error ("Failed to handle state message: %d (%s)", ret, strerror (-ret)); } } return ret; } /*! * Some service actions are for internal use and consist of a single message * (FLOW, JOIN, SYNC) * In this case we can simply use msg->buf as an action buffer, since we * can guarantee that we don't deallocate it. Action here is just a wrapper * to deliver message to the upper level. */ static ssize_t core_msg_to_action (gcs_core_t* core, struct gcs_recv_msg* msg, struct gcs_act* act) { ssize_t ret = 0; gcs_group_t* group = &core->group; if (GCS_GROUP_PRIMARY == gcs_group_state (group)) { gcs_act_type_t act_type; switch (msg->type) { case GCS_MSG_FLOW: // most frequent ret = 1; act_type = GCS_ACT_FLOW; break; case GCS_MSG_JOIN: ret = gcs_group_handle_join_msg (group, msg); assert (gcs_group_my_idx(group) == msg->sender_idx || 0 >= ret); if (-ENOTRECOVERABLE == ret) { core->backend.close(&core->backend); // See #165. // There is nobody to pass this error to for graceful shutdown: // application thread is blocked waiting for SST. gu_abort(); } act_type = GCS_ACT_JOIN; break; case GCS_MSG_SYNC: ret = gcs_group_handle_sync_msg (group, msg); act_type = GCS_ACT_SYNC; break; default: gu_error ("Iternal error. Unexpected message type %s from ld%", gcs_msg_type_string[msg->type], msg->sender_idx); assert (0); ret = -EPROTO; } if (ret > 0) { act->type = act_type; act->buf = msg->buf; act->buf_len = msg->size; ret = msg->size; } } else { gu_warn ("%s message from member %ld in non-primary configuration. " "Ignored.", gcs_msg_type_string[msg->type], msg->sender_idx); } return ret; } static long core_msg_causal(gcs_core_t* conn, struct gcs_recv_msg* msg) { causal_act_t* act; if (gu_unlikely(msg->size != sizeof(*act))) { gu_error("invalid causal act len %ld, expected %ld", msg->size, sizeof(*act)); return -EPROTO; } gcs_seqno_t const causal_seqno = GCS_GROUP_PRIMARY == conn->group.state ? conn->group.act_id : GCS_SEQNO_ILL; act = (causal_act_t*)msg->buf; gu_mutex_lock(act->mtx); *act->act_id = causal_seqno; gu_cond_signal(act->cond); gu_mutex_unlock(act->mtx); return msg->size; } /*! Receives action */ ssize_t gcs_core_recv (gcs_core_t* conn, struct gcs_act_rcvd* recv_act, long long timeout) { // struct gcs_act_rcvd recv_act; struct gcs_recv_msg* recv_msg = &conn->recv_msg; ssize_t ret = 0; static struct gcs_act_rcvd zero_act = { .act = { .buf = NULL, .buf_len = 0, .type = GCS_ACT_ERROR }, .repl_buf = NULL, .id = -1, // GCS_SEQNO_ILL .sender_idx = -1 }; *recv_act = zero_act; /* receive messages from group and demultiplex them * until finally some complete action is ready */ do { assert (recv_act->act.buf == NULL); assert (recv_act->act.buf_len == 0); assert (recv_act->act.type == GCS_ACT_ERROR); assert (recv_act->id == GCS_SEQNO_ILL); assert (recv_act->sender_idx == -1); ret = core_msg_recv (&conn->backend, recv_msg, timeout); if (gu_unlikely (ret <= 0)) { goto out; /* backend error while receiving message */ } switch (recv_msg->type) { case GCS_MSG_ACTION: ret = core_handle_act_msg(conn, recv_msg, recv_act); assert (ret == recv_act->act.buf_len || ret <= 0); assert (recv_act->sender_idx >= 0 || ret == 0); break; case GCS_MSG_LAST: ret = core_handle_last_msg(conn, recv_msg, &recv_act->act); assert (ret >= 0); // hang on error in debug mode assert (ret == recv_act->act.buf_len); break; case GCS_MSG_COMPONENT: ret = core_handle_comp_msg (conn, recv_msg, &recv_act->act); assert (ret >= 0); // hang on error in debug mode assert (ret == recv_act->act.buf_len); break; case GCS_MSG_STATE_UUID: ret = core_handle_uuid_msg (conn, recv_msg); assert (ret >= 0); // hang on error in debug mode ret = 0; // continue waiting for state messages break; case GCS_MSG_STATE_MSG: ret = core_handle_state_msg (conn, recv_msg, &recv_act->act); assert (ret >= 0); // hang on error in debug mode assert (ret == recv_act->act.buf_len); break; case GCS_MSG_JOIN: case GCS_MSG_SYNC: case GCS_MSG_FLOW: ret = core_msg_to_action (conn, recv_msg, &recv_act->act); assert (ret == recv_act->act.buf_len || ret <= 0); break; case GCS_MSG_CAUSAL: ret = core_msg_causal(conn, recv_msg); assert(recv_msg->sender_idx == gcs_group_my_idx(&conn->group)); assert(ret == recv_msg->size || ret <= 0); ret = 0; // continue waiting for messages break; default: // this normaly should not happen, shall we bother with // protection? gu_warn ("Received unsupported message type: %d, length: %d, " "sender: %d", recv_msg->type, recv_msg->size, recv_msg->sender_idx); // continue looping } } while (0 == ret); /* end of recv loop */ out: assert (ret || GCS_ACT_ERROR == recv_act->act.type); assert (ret == recv_act->act.buf_len || ret < 0); assert (recv_act->id <= GCS_SEQNO_ILL || recv_act->act.type == GCS_ACT_TORDERED || recv_act->act.type == GCS_ACT_STATE_REQ); // <- dirty hack assert (recv_act->sender_idx >= 0 || recv_act->act.type != GCS_ACT_TORDERED); // gu_debug ("Returning %d", ret); if (ret < 0) { assert (recv_act->id < 0); if (GCS_ACT_TORDERED == recv_act->act.type && recv_act->act.buf) { gcs_gcache_free (conn->cache, recv_act->act.buf); recv_act->act.buf = NULL; } if (-ENOTRECOVERABLE == ret) { conn->backend.close(&conn->backend); gu_abort(); } } return ret; } long gcs_core_close (gcs_core_t* core) { long ret; if (!core) return -EBADFD; if (gu_mutex_lock (&core->send_lock)) return -EBADFD; if (core->state >= CORE_CLOSED) { ret = -EBADFD; } else { ret = core->backend.close (&core->backend); gcs_fifo_lite_close (core->fifo); } gu_mutex_unlock (&core->send_lock); return ret; } long gcs_core_destroy (gcs_core_t* core) { core_act_t* tmp; if (!core) return -EBADFD; if (gu_mutex_lock (&core->send_lock)) return -EBADFD; { if (CORE_CLOSED != core->state) { if (core->state < CORE_CLOSED) gu_error ("Calling destroy() before close()."); gu_mutex_unlock (&core->send_lock); return -EBADFD; } if (core->backend.conn) { gu_debug ("Calling backend.destroy()"); core->backend.destroy (&core->backend); } core->state = CORE_DESTROYED; } gu_mutex_unlock (&core->send_lock); /* at this point all send attempts are isolated */ /* after that we must be able to destroy mutexes */ while (gu_mutex_destroy (&core->send_lock)); /* now noone will interfere */ while ((tmp = gcs_fifo_lite_get_head (core->fifo))) { // whatever is in tmp.action is allocated by app., just forget it. gcs_fifo_lite_pop_head (core->fifo); } gcs_fifo_lite_destroy (core->fifo); gcs_group_free (&core->group); /* free buffers */ gu_free (core->recv_msg.buf); gu_free (core->send_buf); #ifdef GCS_CORE_TESTING gu_lock_step_destroy (&core->ls); #endif gu_free (core); return 0; } long gcs_core_set_pkt_size (gcs_core_t* core, long pkt_size) { long hdr_size, msg_size; uint8_t* new_send_buf = NULL; long ret = 0; if (core->state >= CORE_CLOSED) { gu_error ("Attempt to set packet size on a closed connection."); return -EBADFD; } hdr_size = gcs_act_proto_hdr_size (core->proto_ver); if (hdr_size < 0) return hdr_size; msg_size = core->backend.msg_size (&core->backend, pkt_size); if (msg_size <= hdr_size) { gu_warn ("Requested packet size %d is too small, " "using smallest possible: %d", pkt_size, pkt_size + (hdr_size - msg_size + 1)); msg_size = hdr_size + 1; } gu_info ("Changing maximum packet size to %ld, resulting msg size: %ld", pkt_size, msg_size); ret = msg_size - hdr_size; // message payload if (core->send_buf_len == (size_t)msg_size) return ret; if (gu_mutex_lock (&core->send_lock)) abort(); { if (core->state != CORE_DESTROYED) { new_send_buf = gu_realloc (core->send_buf, msg_size); if (new_send_buf) { core->send_buf = new_send_buf; core->send_buf_len = msg_size; memset (core->send_buf, 0, hdr_size); // to pacify valgrind gu_debug ("Message payload (action fragment size): %ld", ret); } else { ret = -ENOMEM; } } else { ret = -EBADFD; } } gu_mutex_unlock (&core->send_lock); return ret; } static inline long core_send_seqno (gcs_core_t* core, gcs_seqno_t seqno, gcs_msg_type_t msg_type) { gcs_seqno_t const htogs = gcs_seqno_htog (seqno); ssize_t ret = core_msg_send_retry (core, &htogs, sizeof(htogs), msg_type); if (ret > 0) { assert(ret == sizeof(seqno)); ret = 0; } return ret; } long gcs_core_set_last_applied (gcs_core_t* core, gcs_seqno_t seqno) { return core_send_seqno (core, seqno, GCS_MSG_LAST); } long gcs_core_send_join (gcs_core_t* core, gcs_seqno_t seqno) { return core_send_seqno (core, seqno, GCS_MSG_JOIN); } long gcs_core_send_sync (gcs_core_t* core, gcs_seqno_t seqno) { return core_send_seqno (core, seqno, GCS_MSG_SYNC); } long gcs_core_send_fc (gcs_core_t* core, const void* fc, size_t fc_size) { ssize_t ret; ret = core_msg_send_retry (core, fc, fc_size, GCS_MSG_FLOW); if (ret == (ssize_t)fc_size) { ret = 0; } return ret; } gcs_seqno_t gcs_core_caused(gcs_core_t* core) { long ret; gcs_seqno_t act_id = GCS_SEQNO_ILL; gu_mutex_t mtx; gu_cond_t cond; causal_act_t act = {&act_id, &mtx, &cond}; gu_mutex_init (&mtx, NULL); gu_cond_init (&cond, NULL); gu_mutex_lock (&mtx); { ret = core_msg_send_retry (core, &act, sizeof(act), GCS_MSG_CAUSAL); if (ret == sizeof(act)) { gu_cond_wait (&cond, &mtx); } else { assert (ret < 0); act_id = ret; } } gu_mutex_unlock (&mtx); gu_mutex_destroy (&mtx); gu_cond_destroy (&cond); return act_id; } long gcs_core_param_set (gcs_core_t* core, const char* key, const char* value) { if (core->backend.conn) { return core->backend.param_set (&core->backend, key, value); } else { return 1; } } const char* gcs_core_param_get (gcs_core_t* core, const char* key) { if (core->backend.conn) { return core->backend.param_get (&core->backend, key); } else { return NULL; } } #ifdef GCS_CORE_TESTING gcs_backend_t* gcs_core_get_backend (gcs_core_t* core) { return &core->backend; } void gcs_core_send_lock_step (gcs_core_t* core, bool enable) { gu_lock_step_enable (&core->ls, enable); } long gcs_core_send_step (gcs_core_t* core, long timeout_ms) { return gu_lock_step_cont (&core->ls, timeout_ms); } #endif /* GCS_CORE_TESTING */ percona-xtradb-cluster-galera/gcs/src/gcs_core.h0000644000000000000000000001233612247075736022157 0ustar rootroot00000000000000/* * Copyright (C) 2008-2012 Codership Oy * * $Id: gcs_core.h 2923 2012-12-30 11:10:30Z alex $ */ /* * This header defines generic communication layer * which implements basic open/close/send/receive * functions. Its purpose is to implement all * functionality common to all group communication * uses. Currently this amounts to action * fragmentation/defragmentation and invoking backend * functions. * In the course of development it has become clear * that such fuctionality must be collected in a * separate layer. * Application abstraction layer is based on this one * and uses those functions for its own purposes. */ #ifndef _gcs_core_h_ #define _gcs_core_h_ #include #include #include #include "gcs.h" #include "gcs_act.h" #include struct gcs_core; typedef struct gcs_core gcs_core_t; /* * Allocates context resources private to * generic communicaton layer - send/recieve buffers and the like. */ extern gcs_core_t* gcs_core_create (gu_config_t* conf, gcache_t* cache, const char* node_name, const char* inc_addr, int repl_proto_ver, int appl_proto_ver); /* initializes action history (global seqno, group UUID). See gcs.h */ extern long gcs_core_init (gcs_core_t* core, gcs_seqno_t seqno, const gu_uuid_t* uuid); /* * gcs_core_open() opens connection * Return values: * zero - success * negative - error code */ extern long gcs_core_open (gcs_core_t* conn, const char* channel, const char* url, bool bootstrap); /* * gcs_core_close() puts connection in a closed state, * cancelling all ongoing calls. * Return values: * zero - success * negative - error code */ extern long gcs_core_close (gcs_core_t* conn); /* * gcs_core_destroy() frees resources allocated by gcs_core_create() * Return values: * zero - success * negative - error code */ extern long gcs_core_destroy (gcs_core_t* conn); /* * gcs_core_send() atomically sends action to group. * * NOT THREAD SAFE! Access should be serialized. * * Return values: * non-negative - amount of action bytes sent (sans headers) * negative - error code * -EAGAIN - operation should be retried * -ENOTCONN - connection to primary component lost * * NOTE: Successful return code here does not guarantee delivery to group. * The real status of action is determined only in gcs_core_recv() call. */ extern ssize_t gcs_core_send (gcs_core_t* core, const void* action, size_t act_size, gcs_act_type_t act_type); /* * gcs_core_recv() blocks until some action is received from group. * * @param repl_buf ptr to replicated action local buffer (NULL otherwise) * @param timeout absolute timeout date (as in pthread_cond_timedwait()) * * Return values: * non-negative - the size of action received * negative - error code * * @retval -ETIMEDOUT means no messages were received until timeout. * * NOTE: Action status (replicated or not) is carried in act_id. E.g. -ENOTCONN * means connection to primary component was lost while sending, * -ERESTART means that action delivery was interrupted and it must be * resent. */ extern ssize_t gcs_core_recv (gcs_core_t* conn, struct gcs_act_rcvd* recv_act, long long timeout); /* Configuration functions */ /* Sets maximum message size to achieve requested network packet size. * In case of failure returns negative error code, in case of success - * resulting message payload size (size of action fragment) */ extern long gcs_core_set_pkt_size (gcs_core_t* conn, long pkt_size); /* sends this node's last applied value to group */ extern long gcs_core_set_last_applied (gcs_core_t* core, gcs_seqno_t seqno); /* sends status of the ended snapshot (snapshot seqno or error code) */ extern long gcs_core_send_join (gcs_core_t* core, gcs_seqno_t seqno); /* sends SYNC notice, seqno currently has no meaning */ extern long gcs_core_send_sync (gcs_core_t* core, gcs_seqno_t seqno); /* sends flow control message */ extern long gcs_core_send_fc (gcs_core_t* core, const void* fc, size_t fc_size); extern gcs_seqno_t gcs_core_caused(gcs_core_t* core); extern long gcs_core_param_set (gcs_core_t* core, const char* key, const char* value); extern const char* gcs_core_param_get (gcs_core_t* core, const char* key); #ifdef GCS_CORE_TESTING /* gcs_core_send() interface does not allow enough concurrency control to model * various race conditions for unit testing - it is not atomic. The functions * below expose gcs_core unit internals solely for the purpose of testing */ #include "gcs_msg_type.h" #include "gcs_backend.h" extern gcs_backend_t* gcs_core_get_backend (gcs_core_t* core); // switches lock-step mode on/off extern void gcs_core_send_lock_step (gcs_core_t* core, bool enable); // step through action send process (send another fragment). // returns positive number if there was a send thread waiting for it. extern long gcs_core_send_step (gcs_core_t* core, long timeout_ms); #endif /* GCS_CORE_TESTING */ #endif /* _gcs_core_h_ */ percona-xtradb-cluster-galera/gcs/src/gcs_defrag.c0000644000000000000000000001255612247075736022456 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_defrag.c 3111 2013-06-12 13:31:23Z alex $ */ #include #include #include #include "gcs_act_proto.h" #include "gcs_defrag.h" #define DF_ALLOC() \ do { \ df->head = gcs_gcache_malloc (df->cache, df->size); \ \ if(gu_likely(df->head != NULL)) \ df->tail = df->head; \ else { \ gu_error ("Could not allocate memory for new " \ "action of size: %z", df->size); \ assert(0); \ return -ENOMEM; \ } \ } while (0) /*! * Handle action fragment * * Unless a whole action is returned, contents of act is undefined * * In order to optimize branch prediction used gu_likely macros and odered and * nested if/else blocks according to branch probability. * * @return 0 - success, * size of action - success, full action received, * negative - error. * * TODO: this function is too long, figure out a way to factor it into several * smaller ones. Note that it is called for every GCS_MSG_ACTION message * so it should be optimal. */ ssize_t gcs_defrag_handle_frag (gcs_defrag_t* df, const gcs_act_frag_t* frg, struct gcs_act* act, bool local) { if (df->received) { /* another fragment of existing action */ df->frag_no++; /* detect possible error condition */ if (gu_unlikely((df->sent_id != frg->act_id) || (df->frag_no != frg->frag_no))) { if (local && df->reset && (df->sent_id == frg->act_id) && (0 == frg->frag_no)) { /* df->sent_id was aborted halfway and is being taken care of * by the sender thread. Forget about it. * Reinit counters and continue with the new action. * Note that for local actions no memory allocation is made.*/ gu_debug ("Local action %lld reset.", frg->act_id); df->frag_no = 0; df->received = 0; df->tail = df->head; df->reset = false; if (df->size != frg->act_size) { df->size = frg->act_size; #ifndef GCS_FOR_GARB if (df->cache !=NULL) { gcache_free (df->cache, df->head); } else { free ((void*)df->head); } DF_ALLOC(); #endif /* GCS_FOR_GARB */ } } else { gu_error ("Unordered fragment received. Protocol error."); gu_error ("Expected: %llu:%ld, received: %llu:%ld", df->sent_id, df->frag_no, frg->act_id, frg->frag_no); gu_error ("Contents: '%.*s'", frg->frag_len, (char*)frg->frag); df->frag_no--; // revert counter in hope that we get good frag assert(0); return -EPROTO; } } } else { /* new action */ if (gu_likely(0 == frg->frag_no)) { df->size = frg->act_size; df->sent_id = frg->act_id; df->reset = false; #ifndef GCS_FOR_GARB DF_ALLOC(); #else /* we don't store actions locally at all */ df->head = NULL; df->tail = df->head; #endif } else { /* not a first fragment */ if (!local && df->reset) { /* can happen after configuration change, just ignore this message calmly */ gu_debug ("Ignoring fragment %lld:%ld after action reset", frg->act_id, frg->frag_no); return 0; } else { ((char*)frg->frag)[frg->frag_len - 1] = '\0'; gu_error ("Unordered fragment received. Protocol error."); gu_error ("Expected: any:0(first), received: %lld:%ld", frg->act_id, frg->frag_no); gu_error ("Contents: '%s', local: %s, reset: %s", (char*)frg->frag, local ? "yes" : "no", df->reset ? "yes" : "no"); assert(0); return -EPROTO; } } } df->received += frg->frag_len; assert (df->received <= df->size); #ifndef GCS_FOR_GARB assert (df->tail); memcpy (df->tail, frg->frag, frg->frag_len); df->tail += frg->frag_len; #else /* we skip memcpy since have not allocated any buffer */ assert (NULL == df->tail); assert (NULL == df->head); #endif if (df->received == df->size) { act->buf = df->head; act->buf_len = df->received; gcs_defrag_init (df, df->cache); return act->buf_len; } else { return 0; } } percona-xtradb-cluster-galera/gcs/src/gcs_defrag.h0000644000000000000000000000400012247075736022444 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_defrag.h 2383 2011-09-24 11:14:06Z alex $ */ /*! * Receiving action context */ #ifndef _gcs_defrag_h_ #define _gcs_defrag_h_ #include // for memset() #include #include #include "gcs.h" // for gcs_seqno_t et al. #include "gcs_act_proto.h" #include "gcs_act.h" #include "gcs_gcache.h" #include typedef struct gcs_defrag { gcache_t* cache; gcs_seqno_t sent_id; // sent id (unique for a node) uint8_t* head; // head of action buffer uint8_t* tail; // tail of action data size_t size; size_t received; ulong frag_no; // number of fragment received bool reset; } gcs_defrag_t; static inline void gcs_defrag_init (gcs_defrag_t* df, gcache_t* cache) { memset (df, 0, sizeof (*df)); df->cache = cache; df->sent_id = GCS_SEQNO_ILL; } /*! * Handle received action fragment * * @return 0 - success, * size of action - success, full action received, * negative - error. */ extern ssize_t gcs_defrag_handle_frag (gcs_defrag_t* df, const gcs_act_frag_t* frg, struct gcs_act* act, bool local); /*! Deassociate, but don't deallocate action resources */ static inline void gcs_defrag_forget (gcs_defrag_t* df) { gcs_defrag_init (df, df->cache); } /*! Free resources associated with defrag (for lost node cleanup) */ static inline void gcs_defrag_free (gcs_defrag_t* df) { #ifndef GCS_FOR_GARB if (df->head) { gcs_gcache_free (df->cache, df->head); // df->head, df->tail will be zeroed in gcs_defrag_init() below } #else assert(NULL == df->head); #endif gcs_defrag_init (df, df->cache); } /*! Mark current action as reset */ static inline void gcs_defrag_reset (gcs_defrag_t* df) { df->reset = true; } #endif /* _gcs_defrag_h_ */ percona-xtradb-cluster-galera/gcs/src/gcs_dummy.c0000644000000000000000000002233112247075736022351 0ustar rootroot00000000000000/* * Copyright (C) 2008-2012 Codership Oy * * $Id: gcs_dummy.c 2923 2012-12-30 11:10:30Z alex $ */ /* * Dummy backend implementation * */ #include #include #include #include #include #include #include #define GCS_COMP_MSG_ACCESS // for gcs_comp_memb_t #ifndef GCS_DUMMY_TESTING #define GCS_DUMMY_TESTING #endif #include "gcs_dummy.h" typedef struct dummy_msg { gcs_msg_type_t type; ssize_t len; long sender_idx; uint8_t buf[]; } dummy_msg_t; static inline dummy_msg_t* dummy_msg_create (gcs_msg_type_t const type, size_t const len, long const sender, const void* const buf) { dummy_msg_t *msg = NULL; if ((msg = gu_malloc (sizeof(dummy_msg_t) + len))) { memcpy (msg->buf, buf, len); msg->len = len; msg->type = type; msg->sender_idx = sender; } return msg; } static inline long dummy_msg_destroy (dummy_msg_t *msg) { if (msg) { gu_free (msg); } return 0; } typedef enum dummy_state { DUMMY_DESTROYED, DUMMY_CLOSED, DUMMY_NON_PRIM, DUMMY_TRANS, DUMMY_PRIM, } dummy_state_t; typedef struct gcs_backend_conn { gu_fifo_t* gc_q; /* "serializator" */ volatile dummy_state_t state; gcs_seqno_t msg_id; const size_t max_pkt_size; const size_t hdr_size; const size_t max_send_size; long my_idx; long memb_num; gcs_comp_memb_t* memb; } dummy_t; static GCS_BACKEND_DESTROY_FN(dummy_destroy) { dummy_t* dummy = backend->conn; if (!dummy || dummy->state != DUMMY_CLOSED) return -EBADFD; // gu_debug ("Deallocating message queue (serializer)"); gu_fifo_destroy (dummy->gc_q); if (dummy->memb) gu_free (dummy->memb); gu_free (dummy); backend->conn = NULL; return 0; } static GCS_BACKEND_SEND_FN(dummy_send) { int err = 0; dummy_t* dummy = backend->conn; if (gu_unlikely(NULL == dummy)) return -EBADFD; if (gu_likely(DUMMY_PRIM == dummy->state)) { err = gcs_dummy_inject_msg (backend, buf, len, msg_type, backend->conn->my_idx); } else { static long send_error[DUMMY_PRIM] = { -EBADFD, -EBADFD, -ENOTCONN, -EAGAIN }; err = send_error[dummy->state]; } return err; } static GCS_BACKEND_RECV_FN(dummy_recv) { long ret = 0; dummy_t* conn = backend->conn; msg->sender_idx = GCS_SENDER_NONE; msg->type = GCS_MSG_ERROR; assert (conn); /* skip it if we already have popped a message from the queue * in the previous call */ if (gu_likely(DUMMY_CLOSED <= conn->state)) { int err; dummy_msg_t** ptr = gu_fifo_get_head (conn->gc_q, &err); if (gu_likely(ptr != NULL)) { dummy_msg_t* dmsg = *ptr; assert (NULL != dmsg); msg->type = dmsg->type; msg->sender_idx = dmsg->sender_idx; ret = dmsg->len; msg->size = ret; if (gu_likely(dmsg->len <= msg->buf_len)) { gu_fifo_pop_head (conn->gc_q); memcpy (msg->buf, dmsg->buf, dmsg->len); dummy_msg_destroy (dmsg); } else { // supplied recv buffer too short, leave the message in queue memcpy (msg->buf, dmsg->buf, msg->buf_len); gu_fifo_release (conn->gc_q); } } else { ret = -EBADFD; // closing gu_debug ("Returning %d: %s", ret, strerror(-ret)); } } else { ret = -EBADFD; } return ret; } static GCS_BACKEND_NAME_FN(dummy_name) { return "built-in dummy backend"; } static GCS_BACKEND_MSG_SIZE_FN(dummy_msg_size) { const long max_pkt_size = backend->conn->max_pkt_size; if (pkt_size > max_pkt_size) { gu_warn ("Requested packet size: %d, maximum possible packet size: %d", pkt_size, max_pkt_size); return (max_pkt_size - backend->conn->hdr_size); } return (pkt_size - backend->conn->hdr_size); } static GCS_BACKEND_OPEN_FN(dummy_open) { long ret = -ENOMEM; dummy_t* dummy = backend->conn; gcs_comp_msg_t* comp; if (!dummy) { gu_debug ("Backend not initialized"); return -EBADFD; } comp = gcs_comp_msg_new (true, false, 0, 1); if (comp) { ret = gcs_comp_msg_add (comp, "11111111-2222-3333-4444-555555555555"); assert (0 == ret); // we have only one member, index = 0 dummy->state = DUMMY_TRANS; // required by gcs_dummy_set_component() ret = gcs_dummy_set_component (backend, comp); // install new component if (ret >= 0) { // queue the message ret = gcs_comp_msg_size(comp); ret = gcs_dummy_inject_msg (backend, comp, ret, GCS_MSG_COMPONENT, GCS_SENDER_NONE); if (ret > 0) ret = 0; } gcs_comp_msg_delete (comp); } gu_debug ("Opened backend connection: %d (%s)", ret, strerror(-ret)); return ret; } static GCS_BACKEND_CLOSE_FN(dummy_close) { long ret = -ENOMEM; dummy_t* dummy = backend->conn; gcs_comp_msg_t* comp; if (!dummy) return -EBADFD; comp = gcs_comp_msg_leave (); if (comp) { ret = gcs_comp_msg_size(comp); ret = gcs_dummy_inject_msg (backend, comp, ret, GCS_MSG_COMPONENT, GCS_SENDER_NONE); // Here's a race condition - some other thread can send something // after leave message. But caller should guarantee serial access. gu_fifo_close (dummy->gc_q); if (ret > 0) ret = 0; gcs_comp_msg_delete (comp); } dummy->state = DUMMY_CLOSED; return ret; } static GCS_BACKEND_PARAM_SET_FN(dummy_param_set) { return 1; } static GCS_BACKEND_PARAM_GET_FN(dummy_param_get) { return NULL; } static const gcs_backend_t dummy_backend = { .conn = NULL, .open = dummy_open, .close = dummy_close, .destroy = dummy_destroy, .send = dummy_send, .recv = dummy_recv, .name = dummy_name, .msg_size = dummy_msg_size, .param_set = dummy_param_set, .param_get = dummy_param_get }; GCS_BACKEND_CREATE_FN(gcs_dummy_create) { long ret = -ENOMEM; dummy_t* dummy = NULL; if (!(dummy = GU_CALLOC(1, dummy_t))) goto out0; dummy->state = DUMMY_CLOSED; *(size_t*)(&dummy->max_pkt_size) = (size_t) sysconf (_SC_PAGESIZE); *(size_t*)(&dummy->hdr_size) = sizeof(dummy_msg_t); *(size_t*)(&dummy->max_send_size) = dummy->max_pkt_size - dummy->hdr_size; if (!(dummy->gc_q = gu_fifo_create (1 << 16, sizeof(void*)))) goto out1; *backend = dummy_backend; // set methods backend->conn = dummy; // set data return 0; out1: gu_free (dummy); out0: backend->conn = NULL; return ret; } /*! Injects a message in the message queue to produce a desired msg sequence. */ long gcs_dummy_inject_msg (gcs_backend_t* backend, const void* buf, size_t buf_len, gcs_msg_type_t type, long sender_idx) { long ret; size_t send_size = buf_len < backend->conn->max_send_size ? buf_len : backend->conn->max_send_size; dummy_msg_t* msg = dummy_msg_create (type, send_size, sender_idx, buf); if (msg) { dummy_msg_t** ptr = gu_fifo_get_tail (backend->conn->gc_q); if (gu_likely(ptr != NULL)) { *ptr = msg; gu_fifo_push_tail (backend->conn->gc_q); ret = send_size; } else { dummy_msg_destroy (msg); ret = -EBADFD; // closed } } else { ret = -ENOMEM; } return ret; } /*! Sets the new component view. * The same component message should be injected in the queue separately * (see gcs_dummy_inject_msg()) in order to model different race conditions */ long gcs_dummy_set_component (gcs_backend_t* backend, const gcs_comp_msg_t* comp) { dummy_t* dummy = backend->conn; long new_num = gcs_comp_msg_num (comp); long i; assert (dummy->state > DUMMY_CLOSED); if (dummy->memb_num != new_num) { void* tmp = gu_realloc (dummy->memb, new_num * sizeof(gcs_comp_memb_t)); if (NULL == tmp) return -ENOMEM; dummy->memb = tmp; dummy->memb_num = new_num; } for (i = 0; i < dummy->memb_num; i++) { strcpy ((char*)&dummy->memb[i], gcs_comp_msg_id (comp, i)); } dummy->my_idx = gcs_comp_msg_self(comp); dummy->state = gcs_comp_msg_primary(comp) ? DUMMY_PRIM : DUMMY_NON_PRIM; gu_debug ("Setting state to %s", DUMMY_PRIM == dummy->state ? "DUMMY_PRIM" : "DUMMY_NON_PRIM"); return 0; } /*! Is needed to set transitional state */ long gcs_dummy_set_transitional (gcs_backend_t* backend) { backend->conn->state = DUMMY_TRANS; return 0; } percona-xtradb-cluster-galera/gcs/src/gcs_dummy.h0000644000000000000000000000223112247075736022353 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_dummy.h 914 2009-07-13 00:58:05Z alex $ */ /* * Dummy backend specification */ #ifndef _gcs_dummy_h_ #define _gcs_dummy_h_ #include "gcs_backend.h" #include "gcs_comp_msg.h" extern GCS_BACKEND_CREATE_FN (gcs_dummy_create); #ifdef GCS_DUMMY_TESTING /* * What follows is an API for unit testing */ /*! Injects a message in the message queue to produce a desired msg sequence. */ extern long gcs_dummy_inject_msg (gcs_backend_t* backend, const void* msg, size_t len, gcs_msg_type_t type, long sender_idx); /*! Sets the new component view. * The same component message should be injected in the queue separately * (see gcs_dummy_inject_msg()) in order to model different race conditions */ extern long gcs_dummy_set_component (gcs_backend_t* backend, const gcs_comp_msg_t* comp); /*! Is needed to set transitional state */ extern long gcs_dummy_set_transitional (gcs_backend_t* backend); #endif /* GCS_DUMMY_TESTING */ #endif /* _gcs_dummy_h_ */ percona-xtradb-cluster-galera/gcs/src/gcs_fc.c0000644000000000000000000001514512247075736021613 0ustar rootroot00000000000000/* * Copyright (C) 2010 Codership Oy * * $Id$ */ /*! @file This unit contains Flow Control parts deemed worthy to be * taken out of gcs.c */ #include "gcs_fc.h" #include #include double const gcs_fc_hard_limit_fix = 0.9; //! allow for some overhead static double const min_sleep = 0.001; //! minimum sleep period (s) /*! Initializes operational constants before opening connection to group * @return -EINVAL if wrong values are submitted */ int gcs_fc_init (gcs_fc_t* fc, ssize_t hard_limit, // slave queue hard limit double soft_limit, // soft limit as a fraction of hard limit double max_throttle) { assert (fc); if (hard_limit < 0) { gu_error ("Bad value for slave queue hard limit: %zd (should be > 0)", hard_limit); return -EINVAL; } if (soft_limit < 0.0 || soft_limit >= 1.0) { gu_error ("Bad value for slave queue soft limit: %f " "(should belong to [0.0,1.0) )", soft_limit); return -EINVAL; } if (max_throttle < 0.0 || max_throttle >= 1.0) { gu_error ("Bad value for max throttle: %f " "(should belong to [0.0,1.0) )", max_throttle); return -EINVAL; } memset (fc, 0, sizeof(*fc)); fc->hard_limit = hard_limit; fc->soft_limit = fc->hard_limit * soft_limit; fc->max_throttle = max_throttle; return 0; } /*! Reinitializes object at the beginning of state transfer */ void gcs_fc_reset (gcs_fc_t* const fc, ssize_t const queue_size) { assert (fc != NULL); assert (queue_size >= 0); fc->init_size = queue_size; fc->size = fc->init_size; fc->start = gu_time_monotonic(); fc->last_sleep = 0; fc->act_count = 0; fc->max_rate = -1.0; fc->scale = 0.0; fc->offset = 0.0; fc->sleep_count= 0; fc->sleeps = 0.0; } /* * The idea here is that there is no flow control up until slave queue size * reaches soft limit. * After that flow control gradually slows down replication rate by emitting FC * events in order to buy more time for state transfer. * Replication rate goes linearly from normal rate at soft limit to max_throttle * fraction at hard limit, at which point -ENOMEM is returned as replication * becomes prohibitively slow. * * replication * speed * ^ * |--------. <- normal replication rate * | .\ * | . \ * | . \ * | . \ speed = fc->size * fc->scale + fc->offset * | . \ * | . \ * | . \ | * | . \ | * | . \| * | . + <- throttle limit * | . | * | . | * +--------+---------+----> slave queue size * soft hard * limit limit */ /*! Processes a new action added to a slave queue. * @return length of sleep in nanoseconds or negative error code * or GU_TIME_ETERNITY for complete stop */ long long gcs_fc_process (gcs_fc_t* fc, ssize_t act_size) { fc->size += act_size; fc->act_count++; if (fc->size <= fc->soft_limit) { /* normal operation */ if (gu_unlikely(fc->debug > 0 && !(fc->act_count % fc->debug))) { gu_info ("FC: queue size: %zdb (%4.1f%% of soft limit)", fc->size, ((double)fc->size)/fc->soft_limit*100.0); } return 0; } else if (fc->size >= fc->hard_limit) { if (0.0 == fc->max_throttle) { /* we can accept total service outage */ return GU_TIME_ETERNITY; } else { gu_error ("Recv queue hard limit exceded. Can't continue."); return -ENOMEM; } } // else if (!(fc->act_count & 7)) { // do this for every 8th action else { long long end = gu_time_monotonic(); double interval = ((end - fc->start) * 1.0e-9); if (gu_unlikely (0 == fc->last_sleep)) { /* just tripped the soft limit, preparing constants for throttle */ fc->max_rate = (double)(fc->size - fc->init_size) / interval; double s = (1.0 - fc->max_throttle)/(fc->soft_limit-fc->hard_limit); assert (s < 0.0); fc->scale = s * fc->max_rate; fc->offset = (1.0 - s*fc->soft_limit) * fc->max_rate; // calculate time interval from the soft limit interval = interval * (double)(fc->size - fc->soft_limit) / (fc->size - fc->init_size); assert (interval >= 0.0); // Move reference point to soft limit fc->last_sleep = fc->soft_limit; fc->start = end - interval; gu_warn("Soft recv queue limit exceeded, starting replication " "throttle. Measured avg. rate: %f bytes/sec; " "Throttle parameters: scale=%f, offset=%f", fc->max_rate, fc->scale, fc->offset); } /* throttling operation */ double desired_rate = fc->size * fc->scale + fc->offset; // linear decay //double desired_rate = fc->max_rate * fc->max_throttle; // square wave assert (desired_rate <= fc->max_rate); double sleep = (double)(fc->size - fc->last_sleep) / desired_rate - interval; if (gu_unlikely(fc->debug > 0 && !(fc->act_count % fc->debug))) { gu_info ("FC: queue size: %zdb, length: %zd, " "measured rate: %fb/s, desired rate: %fb/s, " "interval: %5.3fs, sleep: %5.4fs. " "Sleeps initiated: %zd, for a total of %6.3fs", fc->size, fc->act_count, ((double)(fc->size - fc->last_sleep))/interval, desired_rate, interval, sleep, fc->sleep_count, fc->sleeps); fc->sleep_count = 0; fc->sleeps = 0.0; } if (gu_likely(sleep < min_sleep)) { #if 0 gu_info ("Skipping sleep: desired_rate = %f, sleep = %f (%f), " "interval = %f, fc->scale = %f, fc->offset = %f, " "fc->size = %zd", desired_rate, sleep, min_sleep, interval, fc->scale, fc->offset, fc->size); #endif return 0; } fc->last_sleep = fc->size; fc->start = end; fc->sleep_count++; fc->sleeps += sleep; return (1000000000LL * sleep); } return 0; } void gcs_fc_debug (gcs_fc_t* fc, long debug_level) { fc->debug = debug_level; } percona-xtradb-cluster-galera/gcs/src/gcs_fc.h0000644000000000000000000000367612247075736021626 0ustar rootroot00000000000000/* * Copyright (C) 2010 Codership Oy * * $Id$ */ /*! @file This unit contains Flow Control parts deemed worthy to be * taken out of gcs.c */ #ifndef _gcs_fc_h_ #define _gcs_fc_h_ #include #include #include typedef struct gcs_fc { ssize_t hard_limit; // hard limit for slave queue size ssize_t soft_limit; // soft limit for slave queue size, after it FC kicks in double max_throttle; // limit on how much we can throttle replication ssize_t init_size; // initial queue size ssize_t size; // current queue size ssize_t last_sleep; // queue size when last sleep happened ssize_t act_count; // action count double max_rate; // normal replication data rate (byte/s) double scale; // data rate scale factor double offset; // data rate offset (rate = scale*size + offset) long long start; // beginning of the time interval (nanosec, monotonic) long debug; // how often to print debug messages, 0 - never ssize_t sleep_count; double sleeps; } gcs_fc_t; extern double const gcs_fc_hard_limit_fix; //! allow for some overhead /*! Initializes operational constants before oprning connection to group */ extern int gcs_fc_init (gcs_fc_t* fc, ssize_t hard_limit, // hard limit double soft_limit, // soft limit as a fraction of hard limit double max_throttle); /*! Reinitializes object at the beginning of state transfer */ extern void gcs_fc_reset (gcs_fc_t* fc, ssize_t queue_size); /*! Processes a new action added to a slave queue. * @return nanoseconds to sleep or -ENOMEM in case of hitting * hard limit or GU_TIME_ETERNITY to pause forever */ extern long long gcs_fc_process (gcs_fc_t* fc, ssize_t act_size); /*! Print debug info every debug_level'th call to gcs_fc_process. */ extern void gcs_fc_debug (gcs_fc_t* fc, long debug_level); #endif /* _gcs_fc_h_ */ percona-xtradb-cluster-galera/gcs/src/gcs_fifo_lite.c0000644000000000000000000001030412247075736023153 0ustar rootroot00000000000000/* * Copyright (C) 2008-2011 Codership Oy * * $Id: gcs_fifo_lite.c 2272 2011-07-28 23:24:41Z alex $ * * FIFO "class" customized for particular purpose * (here I decided to sacrifice generality for efficiency). * Implements simple fixed size "mallocless" FIFO. * Except gcs_fifo_create() there are two types of fifo * access methods - protected and unprotected. Unprotected * methods assume that calling routines implement their own * protection, and thus are simplified for speed. */ #include #include "gcs_fifo_lite.h" /* Creates FIFO object. Since it practically consists of array of (void*), * the length can be chosen arbitrarily high - to minimize the risk * of overflow situation. */ gcs_fifo_lite_t* gcs_fifo_lite_create (size_t length, size_t item_size) { gcs_fifo_lite_t* ret = NULL; uint64_t l = 1; /* check limits */ if (length < 1 || item_size < 1) return NULL; /* Find real length. It must be power of 2*/ while (l < length) l = l << 1; if (l * item_size > (uint64_t)GU_LONG_MAX) { gu_error ("Resulting FIFO size %lld exceeds signed limit: %lld", (long long)(l*item_size), (long long)GU_LONG_MAX); return NULL; } ret = GU_CALLOC (1, gcs_fifo_lite_t); if (ret) { ret->length = l; ret->item_size = item_size; ret->mask = ret->length - 1; ret->closed = true; ret->queue = gu_malloc (ret->length * item_size); if (ret->queue) { gu_mutex_init (&ret->lock, NULL); gu_cond_init (&ret->put_cond, NULL); gu_cond_init (&ret->get_cond, NULL); /* everything else must be initialized to 0 by calloc */ } else { gu_free (ret); ret = NULL; } } return ret; } void gcs_fifo_lite_close (gcs_fifo_lite_t* fifo) { GCS_FIFO_LITE_LOCK; if (fifo->closed) { gu_error ("Trying to close a closed FIFO"); assert(0); } else { fifo->closed = true; // wake whoever is waiting fifo->put_wait = 0; gu_cond_broadcast (&fifo->put_cond); fifo->get_wait = 0; gu_cond_broadcast (&fifo->get_cond); } gu_mutex_unlock (&fifo->lock); } void gcs_fifo_lite_open (gcs_fifo_lite_t* fifo) { GCS_FIFO_LITE_LOCK; if (!fifo->closed) { gu_error ("Trying to open an open FIFO."); assert(0); } else { fifo->closed = false; } gu_mutex_unlock(&fifo->lock); } long gcs_fifo_lite_destroy (gcs_fifo_lite_t* f) { if (f) { if (gu_mutex_lock (&f->lock)) { abort(); } if (f->destroyed) { gu_mutex_unlock (&f->lock); return -EALREADY; } f->closed = true; f->destroyed = true; /* get rid of "put" threads waiting for lock or signal */ while (pthread_cond_destroy (&f->put_cond)) { if (f->put_wait <= 0) { gu_fatal ("Can't destroy condition while nobody's waiting"); abort(); } f->put_wait = 0; gu_cond_broadcast (&f->put_cond); } while (f->used) { /* there are some items in FIFO - and that means * no gcs_fifo_lite_safe_get() is waiting on condition */ gu_mutex_unlock (&f->lock); /* let them get remaining items from FIFO, * we don't know how to deallocate them ourselves. * unfortunately this may take some time */ usleep (10000); /* sleep a bit to avoid busy loop */ gu_mutex_lock (&f->lock); } f->length = 0; /* now all we have - "get" threads waiting for lock or signal */ while (pthread_cond_destroy (&f->get_cond)) { if (f->get_wait <= 0) { gu_fatal ("Can't destroy condition while nobody's waiting"); abort(); } f->get_wait = 0; gu_cond_broadcast (&f->get_cond); } /* at this point there are only functions waiting for lock */ gu_mutex_unlock (&f->lock); while (gu_mutex_destroy (&f->lock)) { /* this should be fast provided safe get and safe put are * wtitten correctly. They should immediately freak out. */ gu_mutex_lock (&f->lock); gu_mutex_unlock (&f->lock); } /* now nobody's waiting for anything */ gu_free (f->queue); gu_free (f); return 0; } return -EINVAL; } percona-xtradb-cluster-galera/gcs/src/gcs_fifo_lite.h0000644000000000000000000001142212247075736023162 0ustar rootroot00000000000000/* * Copyright (C) 2008-2011 Codership Oy * * $Id: gcs_fifo_lite.h 2272 2011-07-28 23:24:41Z alex $ * * FIFO "class" customized for particular purpose * (here I decided to sacrifice generality for efficiency). * Implements fixed size "mallocless" FIFO (read "ring buffer"). * Except gcs_fifo_create() there are two types of fifo * access methods - protected and unprotected. Unprotected * methods assume that calling routines implement their own * protection, and thus are simplified for speed. */ #ifndef _GCS_FIFO_LITE_H_ #define _GCS_FIFO_LITE_H_ #include #include #include #include #include #include #include "gcs.h" typedef struct gcs_fifo_lite { long length; ulong item_size; ulong mask; ulong head; ulong tail; long used; bool closed; bool destroyed; long put_wait; long get_wait; gu_cond_t put_cond; gu_cond_t get_cond; gu_mutex_t lock; void* queue; } gcs_fifo_lite_t; /* Creates FIFO object. Since it practically consists of array of (void*), * the length can be chosen arbitrarily high - to minimize the risk * of overflow situation. */ gcs_fifo_lite_t* gcs_fifo_lite_create (size_t length, size_t item_size); void gcs_fifo_lite_close (gcs_fifo_lite_t* fifo); void gcs_fifo_lite_open (gcs_fifo_lite_t* fifo); long gcs_fifo_lite_destroy (gcs_fifo_lite_t* fifo); static inline void* _gcs_fifo_lite_tail (gcs_fifo_lite_t* f) { return ((char*)f->queue + f->tail * f->item_size); } static inline void* _gcs_fifo_lite_head (gcs_fifo_lite_t* f) { return ((char*)f->queue + f->head * f->item_size); } #define GCS_FIFO_LITE_LOCK \ if (gu_unlikely (gu_mutex_lock (&fifo->lock))) { \ gu_fatal ("Mutex lock failed."); \ abort(); \ } /*! If FIFO is not full, returns pointer to the tail item and locks FIFO, * otherwise blocks. Or returns NULL if FIFO is closed. */ static inline void* gcs_fifo_lite_get_tail (gcs_fifo_lite_t* fifo) { register void* ret = NULL; GCS_FIFO_LITE_LOCK; while (!fifo->closed && fifo->used >= fifo->length) { fifo->put_wait++; gu_cond_wait (&fifo->put_cond, &fifo->lock); } if (gu_likely(!fifo->closed)) { assert (fifo->used < fifo->length); ret = _gcs_fifo_lite_tail (fifo); } else { gu_mutex_unlock (&fifo->lock); } return ret; } /*! Advances FIFO tail and unlocks FIFO */ static inline void gcs_fifo_lite_push_tail (gcs_fifo_lite_t* fifo) { fifo->tail = (fifo->tail + 1) & fifo->mask; fifo->used++; assert (fifo->used <= fifo->length); if (fifo->get_wait > 0) { fifo->get_wait--; gu_cond_signal (&fifo->get_cond); } gu_mutex_unlock (&fifo->lock); } /*! If FIFO is not empty, returns pointer to the head item and locks FIFO, * or returns NULL if FIFO is empty. Blocking behaviour disabled since * it is not needed in GCS: recv_thread should never block. */ static inline void* gcs_fifo_lite_get_head (gcs_fifo_lite_t* fifo) { register void* ret = NULL; GCS_FIFO_LITE_LOCK; /* Uncomment this for blocking behaviour while (!fifo->closed && 0 == fifo->used) { fifo->get_wait++; gu_cond_wait (&fifo->get_cond, &fifo->lock); } */ if (gu_likely(fifo->used > 0)) { ret = _gcs_fifo_lite_head (fifo); } else { gu_mutex_unlock (&fifo->lock); } return ret; } /*! Advances FIFO head and unlocks FIFO */ static inline void gcs_fifo_lite_pop_head (gcs_fifo_lite_t* fifo) { fifo->head = (fifo->head + 1) & fifo->mask; fifo->used--; assert (fifo->used != -1); if (fifo->put_wait > 0) { fifo->put_wait--; gu_cond_signal (&fifo->put_cond); } gu_mutex_unlock (&fifo->lock); } /*! Unlocks FIFO */ static inline long gcs_fifo_lite_release (gcs_fifo_lite_t* fifo) { return (gu_mutex_unlock (&fifo->lock)); } /*! Removes item from tail, returns true if success */ static inline bool gcs_fifo_lite_remove (gcs_fifo_lite_t* const fifo) { register bool ret = false; assert (fifo); GCS_FIFO_LITE_LOCK; if (fifo->used) { fifo->tail = (fifo->tail - 1) & fifo->mask; fifo->used--; ret = true; if (fifo->put_wait > 0) { fifo->put_wait--; gu_cond_signal (&fifo->put_cond); } } gu_mutex_unlock (&fifo->lock); return ret; } static inline bool gcs_fifo_lite_not_full (const gcs_fifo_lite_t* const fifo) { return (fifo->used < fifo->length); } #endif /* _GCS_FIFO_LITE_H_ */ percona-xtradb-cluster-galera/gcs/src/gcs_gcache.h0000644000000000000000000000110012247075736022424 0ustar rootroot00000000000000/* * Copyright (C) 2011 Codership Oy * * $Id$ */ #ifndef _gcs_gcache_h_ #define _gcs_gcache_h_ #include static inline void* gcs_gcache_malloc (gcache_t* gcache, size_t size) { if (gu_likely(gcache != NULL)) return gcache_malloc (gcache, size); else return malloc (size); } static inline void gcs_gcache_free (gcache_t* gcache, const void* buf) { #ifndef GCS_FOR_GARB if (gu_likely (gcache != NULL)) gcache_free (gcache, buf); else #endif free ((void*)buf); } #endif /* _gcs_gcache_h_ */ percona-xtradb-cluster-galera/gcs/src/gcs_gcomm.cpp0000644000000000000000000004774512247075736022700 0ustar rootroot00000000000000/* * Copyright (C) 2009-2012 Codership Oy */ /*! * @file GComm GCS Backend implementation * * @todo Figure out if there is lock-free way to handle RecvBuf * push/pop operations. * */ extern "C" { #include "gcs_gcomm.h" } // We access data comp msg struct directly extern "C" { #define GCS_COMP_MSG_ACCESS 1 #include "gcs_comp_msg.h" } #include #include "gu_backtrace.hpp" #include "gcomm/transport.hpp" #include "gcomm/util.hpp" #ifdef PROFILE_GCS_GCOMM #define GCOMM_PROFILE 1 #else #undef GCOMM_PROFILE #endif // PROFILE_GCS_GCOMM #include "profile.hpp" #include using namespace std; using namespace gu; using namespace gu::prodcons; using namespace gu::datetime; using namespace gcomm; using namespace prof; class RecvBufData { public: RecvBufData(const size_t source_idx, const Datagram& dgram, const ProtoUpMeta& um) : source_idx_(source_idx), dgram_ (dgram), um_ (um) { } size_t get_source_idx() const { return source_idx_; } const Datagram& get_dgram() const { return dgram_; } const ProtoUpMeta& get_um() const { return um_; } private: size_t source_idx_; Datagram dgram_; ProtoUpMeta um_; }; #if defined(GALERA_USE_BOOST_POOL_ALLOC) #include typedef deque > #else typedef deque #endif /* GALERA_USE_BOOST_POOL_ALLOC */ RecvBufQueue; class RecvBuf { private: class Waiting { public: Waiting (bool& w) : w_(w) { w_ = true; } ~Waiting() { w_ = false; } private: bool& w_; }; public: RecvBuf() : mutex_(), cond_(), queue_(), waiting_(false) { } void push_back(const RecvBufData& p) { Lock lock(mutex_); queue_.push_back(p); if (waiting_ == true) { cond_.signal(); } } const RecvBufData& front(const Date& timeout) { Lock lock(mutex_); while (queue_.empty()) { Waiting w(waiting_); if (gu_likely (timeout == GU_TIME_ETERNITY)) { lock.wait(cond_); } else { lock.wait(cond_, timeout); } } assert (false == waiting_); return queue_.front(); } void pop_front() { Lock lock(mutex_); assert(queue_.empty() == false); queue_.pop_front(); } private: Mutex mutex_; Cond cond_; RecvBufQueue queue_; bool waiting_; }; class MsgData : public MessageData { public: MsgData(const byte_t* data, const size_t data_size, const gcs_msg_type_t msg_type) : data_ (data), data_size_(data_size), msg_type_ (msg_type) { } const byte_t* get_data() const { return data_; } size_t get_data_size() const { return data_size_; } gcs_msg_type_t get_msg_type() const { return msg_type_; } public: MsgData(const MsgData&); void operator=(const MsgData&); const byte_t* data_; size_t data_size_; gcs_msg_type_t msg_type_; }; class GCommConn : public Consumer, public Toplay { public: GCommConn(const URI& u, gu::Config& cnf) : Toplay(cnf), conf_(cnf), uuid_(), thd_(), uri_(u), net_(Protonet::create(conf_)), tp_(0), mutex_(), refcnt_(0), terminated_(false), error_(0), recv_buf_(), current_view_(), prof_("gcs_gcomm") { log_info << "backend: " << net_->type(); } ~GCommConn() { delete net_; } const UUID& get_uuid() const { return uuid_; } static void* run_fn(void* arg) { static_cast(arg)->run(); return 0; } void connect(const string& channel, bool const bootstrap) { if (tp_ != 0) { gu_throw_fatal << "backend connection already open"; } uri_.set_option("gmcast.group", channel); tp_ = Transport::create(*net_, uri_); gcomm::connect(tp_, this); if (bootstrap) { log_info << "gcomm: bootstrapping new group '" << channel << '\''; } else { string peer; URI::AuthorityList::const_iterator i, i_next; for (i = uri_.get_authority_list().begin(); i != uri_.get_authority_list().end(); ++i) { i_next = i; ++i_next; string host; string port; try { host = i->host(); } catch (NotSet&) { } try { port = i->port(); } catch (NotSet&) { } peer += host != "" ? host + ":" + port : ""; if (i_next != uri_.get_authority_list().end()) { peer += ","; } } log_info << "gcomm: connecting to group '" << channel << "', peer '" << peer << "'"; } tp_->connect(bootstrap); uuid_ = tp_->uuid(); int err; if ((err = pthread_create(&thd_, 0, &run_fn, this)) != 0) { gu_throw_error(err); } log_info << "gcomm: connected"; } void close() { if (tp_ == 0) { log_warn << "gcomm: backend already closed"; return; } log_info << "gcomm: terminating thread"; terminate(); log_info << "gcomm: joining thread"; pthread_join(thd_, 0); log_info << "gcomm: closing backend"; tp_->close(error_ != 0); gcomm::disconnect(tp_, this); delete tp_; tp_ = 0; const Message* msg; while ((msg = get_next_msg()) != 0) { return_ack(Message(&msg->get_producer(), 0, -ECONNABORTED)); } log_info << "gcomm: closed"; log_debug << prof_; } void run(); void notify() { net_->interrupt(); } void terminate() { Lock lock(mutex_); terminated_ = true; net_->interrupt(); } void handle_up (const void* id, const Datagram& dg, const ProtoUpMeta& um); void queue_and_wait(const Message& msg, Message* ack); RecvBuf& get_recv_buf() { return recv_buf_; } size_t get_mtu() const { if (tp_ == 0) { gu_throw_fatal << "GCommConn::get_mtu(): " << "backend connection not open"; } return tp_->mtu(); } Protonet& get_pnet() { return *net_; } gu::Config& get_conf() { return conf_; } int get_error() const { return error_; } class Ref { public: Ref(gcs_backend_t* ptr, bool unset = false) : conn_(0) { if (ptr->conn != 0) { conn_ = reinterpret_cast(ptr->conn)->ref(unset); if (unset == true) { ptr->conn = 0; } } } ~Ref() { if (conn_ != 0) { conn_->unref(); } } GCommConn* get() { return conn_; } private: Ref(const Ref&); void operator=(const Ref&); GCommConn* conn_; }; private: GCommConn(const GCommConn&); void operator=(const GCommConn&); GCommConn* ref(const bool unsetting) { return this; } void unref() { } gu::Config& conf_; UUID uuid_; pthread_t thd_; URI uri_; Protonet* net_; Transport* tp_; Mutex mutex_; size_t refcnt_; bool terminated_; int error_; RecvBuf recv_buf_; View current_view_; Profile prof_; }; void GCommConn::handle_up(const void* id, const Datagram& dg, const ProtoUpMeta& um) { if (um.err_no() != 0) { error_ = um.err_no(); recv_buf_.push_back(RecvBufData(numeric_limits::max(), dg, um)); } else if (um.has_view() == true) { current_view_ = um.view(); recv_buf_.push_back(RecvBufData(numeric_limits::max(), dg, um)); if (current_view_.is_empty()) { log_debug << "handle_up: self leave"; } } else { size_t idx(0); for (NodeList::const_iterator i = current_view_.members().begin(); i != current_view_.members().end(); ++i) { if (NodeList::key(i) == um.source()) { profile_enter(prof_); recv_buf_.push_back(RecvBufData(idx, dg, um)); profile_leave(prof_); break; } ++idx; } assert(idx < current_view_.members().size()); } } void GCommConn::queue_and_wait(const Message& msg, Message* ack) { { Lock lock(mutex_); if (terminated_ == true) { *ack = Message(&msg.get_producer(), 0, -ECONNABORTED); return; } } profile_enter(prof_); Consumer::queue_and_wait(msg, ack); profile_leave(prof_); } void GCommConn::run() { while (true) { { Lock lock(mutex_); if (terminated_ == true) { break; } } try { net_->event_loop(Sec); } catch (gu::Exception& e) { log_error << "exception from gcomm, backend must be restarted:" << e.what(); // Commented out due to Backtrace() not producing proper // backtraces. // log_info << "attempting to get backtrace:"; // Backtrace().print(std::cerr); gcomm::Critical crit(get_pnet()); handle_up(0, Datagram(), ProtoUpMeta(UUID::nil(), ViewId(V_NON_PRIM), 0, 0xff, O_DROP, -1, e.get_errno())); break; } #if 0 // Disabled catching unknown exceptions due to Backtrace() not // producing proper backtraces. We let the application crash // and deal with diagnostics. catch (...) { log_error << "unknow exception from gcomm, backend must be restarted"; log_info << "attempting to get backtrace:"; Backtrace().print(std::cerr); gcomm::Critical crit(get_pnet()); handle_up(0, Datagram(), ProtoUpMeta(UUID::nil(), ViewId(V_NON_PRIM), 0, 0xff, O_DROP, -1, gu::Exception::E_UNSPEC)); break; } #endif } } //////////////////////////////////////////////////////////////////////////// // // Backend interface implementation // //////////////////////////////////////////////////////////////////////////// static GCS_BACKEND_MSG_SIZE_FN(gcomm_msg_size) { GCommConn::Ref ref(backend); if (ref.get() == 0) { return -1; } return ref.get()->get_mtu(); } static GCS_BACKEND_SEND_FN(gcomm_send) { GCommConn::Ref ref(backend); if (gu_unlikely(ref.get() == 0)) { return -EBADFD; } GCommConn& conn(*ref.get()); Datagram dg( SharedBuffer( new Buffer(reinterpret_cast(buf), reinterpret_cast(buf) + len))); gcomm::Critical crit(conn.get_pnet()); if (gu_unlikely(conn.get_error() != 0)) { return -ECONNABORTED; } int err = conn.send_down( dg, ProtoDownMeta(msg_type, msg_type == GCS_MSG_CAUSAL ? O_LOCAL_CAUSAL : O_SAFE)); return (err == 0 ? len : -err); } static void fill_cmp_msg(const View& view, const UUID& my_uuid, gcs_comp_msg_t* cm) { size_t n(0); for (NodeList::const_iterator i = view.members().begin(); i != view.members().end(); ++i) { const UUID& uuid(NodeList::key(i)); log_debug << "member: " << n << " uuid: " << uuid; // (void)snprintf(cm->memb[n].id, GCS_COMP_MEMB_ID_MAX_LEN, "%s", // uuid._str().c_str()); long ret = gcs_comp_msg_add (cm, uuid._str().c_str()); if (ret < 0) { gu_throw_error(-ret) << "Failed to add member '" << uuid << "' to component message."; } if (uuid == my_uuid) { log_debug << "my index " << n; cm->my_idx = n; } ++n; } } static GCS_BACKEND_RECV_FN(gcomm_recv) { GCommConn::Ref ref(backend); if (gu_unlikely(ref.get() == 0)) return -EBADFD; try { GCommConn& conn(*ref.get()); RecvBuf& recv_buf(conn.get_recv_buf()); const RecvBufData& d(recv_buf.front(timeout)); msg->sender_idx = d.get_source_idx(); const Datagram& dg(d.get_dgram()); const ProtoUpMeta& um(d.get_um()); if (gu_likely(dg.len() != 0)) { assert(dg.len() > dg.offset()); const byte_t* b(gcomm::begin(dg)); const ssize_t pload_len(gcomm::available(dg)); msg->size = pload_len; if (gu_likely(pload_len <= msg->buf_len)) { memcpy(msg->buf, b, pload_len); msg->type = static_cast(um.user_type()); recv_buf.pop_front(); } else { msg->type = GCS_MSG_ERROR; } } else if (um.err_no() != 0) { gcs_comp_msg_t* cm(gcs_comp_msg_leave()); const ssize_t cm_size(gcs_comp_msg_size(cm)); msg->size = cm_size; if (gu_likely(cm_size <= msg->buf_len)) { memcpy(msg->buf, cm, cm_size); recv_buf.pop_front(); msg->type = GCS_MSG_COMPONENT; } else { msg->type = GCS_MSG_ERROR; } gcs_comp_msg_delete(cm); } else { assert(um.has_view() == true); const View& view(um.view()); assert(view.type() == V_PRIM || view.type() == V_NON_PRIM); gcs_comp_msg_t* cm(gcs_comp_msg_new(view.type() == V_PRIM, view.is_bootstrap(), view.is_empty() ? -1 : 0, view.members().size())); const ssize_t cm_size(gcs_comp_msg_size(cm)); if (cm->my_idx == -1) { log_debug << "gcomm recv: self leave"; } msg->size = cm_size; if (gu_likely(cm_size <= msg->buf_len)) { fill_cmp_msg(view, conn.get_uuid(), cm); memcpy(msg->buf, cm, cm_size); recv_buf.pop_front(); msg->type = GCS_MSG_COMPONENT; } else { msg->type = GCS_MSG_ERROR; } gcs_comp_msg_delete(cm); } return msg->size; } catch (Exception& e) { long err = e.get_errno(); if (ETIMEDOUT != err) { log_error << e.what(); } return -err; } } static GCS_BACKEND_NAME_FN(gcomm_name) { static const char *name = "gcomm"; return name; } static GCS_BACKEND_OPEN_FN(gcomm_open) { GCommConn::Ref ref(backend); if (ref.get() == 0) { return -EBADFD; } GCommConn& conn(*ref.get()); try { conn.connect(channel, bootstrap); } catch (Exception& e) { log_error << "failed to open gcomm backend connection: " << e.get_errno() << ": " << e.what(); return -e.get_errno(); } return 0; } static GCS_BACKEND_CLOSE_FN(gcomm_close) { GCommConn::Ref ref(backend); if (ref.get() == 0) { return -EBADFD; } GCommConn& conn(*ref.get()); try { conn.close(); } catch (Exception& e) { log_error << "failed to close gcomm backend connection: " << e.get_errno() << ": " << e.what(); gcomm::Critical crit(conn.get_pnet()); conn.handle_up(0, Datagram(), ProtoUpMeta(UUID::nil(), ViewId(V_NON_PRIM), 0, 0xff, O_DROP, -1, e.get_errno())); // #661: Pretend that closing was successful, backend should be // in unusable state anyway. This allows gcs to finish shutdown // sequence properly. } return 0; } static GCS_BACKEND_DESTROY_FN(gcomm_destroy) { GCommConn::Ref ref(backend, true); if (ref.get() == 0) { log_warn << "could not get reference to backend conn"; return -EBADFD; } GCommConn* conn(ref.get()); try { delete conn; } catch (Exception& e) { log_warn << "conn destroy failed: " << e.get_errno(); return -e.get_errno(); } return 0; } static GCS_BACKEND_PARAM_SET_FN(gcomm_param_set) { GCommConn::Ref ref(backend); if (ref.get() == 0) { return -EBADFD; } GCommConn& conn(*ref.get()); try { gcomm::Critical crit(conn.get_pnet()); if (gu_unlikely(conn.get_error() != 0)) { return -ECONNABORTED; } if (conn.get_pnet().set_param(key, value) == false) { log_debug << "param " << key << " not recognized"; return 1; } else { return 0; } } catch (gu::Exception& e) { log_warn << "error setting param " << key << " to value " << value << ": " << e.what(); return -e.get_errno(); } catch (gu::NotFound& nf) { log_warn << "error setting param " << key << " to value " << value; return -EINVAL; } catch (gu::NotSet& nf) { log_warn << "error setting param " << key << " to value " << value; return -EINVAL; } catch (...) { log_fatal << "gcomm param set: caught unknown exception"; return -ENOTRECOVERABLE; } } static GCS_BACKEND_PARAM_GET_FN(gcomm_param_get) { return NULL; } GCS_BACKEND_CREATE_FN(gcs_gcomm_create) { GCommConn* conn(0); if (!cnf) { log_error << "Null config object passed to constructor."; return -EINVAL; } try { gu::URI uri(std::string("pc://") + addr); gu::Config& conf(*reinterpret_cast(cnf)); conn = new GCommConn(uri, conf); } catch (Exception& e) { log_error << "failed to create gcomm backend connection: " << e.get_errno() << ": " << e.what(); return -e.get_errno(); } backend->open = gcomm_open; backend->close = gcomm_close; backend->destroy = gcomm_destroy; backend->send = gcomm_send; backend->recv = gcomm_recv; backend->name = gcomm_name; backend->msg_size = gcomm_msg_size; backend->param_set = gcomm_param_set; backend->param_get = gcomm_param_get; backend->conn = reinterpret_cast(conn); return 0; } percona-xtradb-cluster-galera/gcs/src/gcs_gcomm.h0000644000000000000000000000033312247075736022323 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id$ */ #ifndef _gcs_gcomm_h_ #define _gcs_gcomm_h_ #include "gcs_backend.h" extern GCS_BACKEND_CREATE_FN(gcs_gcomm_create); #endif /* _gcs_vs_h_ */ percona-xtradb-cluster-galera/gcs/src/gcs_group.c0000644000000000000000000011340612247075736022356 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_group.c 3284 2013-09-14 10:40:05Z alex $ */ #include "gcs_group.h" #include "gcs_group.h" #include "gcs_gcache.h" #include "gcs_priv.h" #include const char* gcs_group_state_str[GCS_GROUP_STATE_MAX] = { "NON_PRIMARY", "WAIT_STATE_UUID", "WAIT_STATE_MSG", "PRIMARY" }; long gcs_group_init (gcs_group_t* group, gcache_t* const cache, const char* node_name, const char* inc_addr, gcs_proto_t const gcs_proto_ver, int const repl_proto_ver, int const appl_proto_ver) { // here we also create default node instance. group->cache = cache; group->act_id = GCS_SEQNO_ILL; group->conf_id = GCS_SEQNO_ILL; group->state_uuid = GU_UUID_NIL; group->group_uuid = GU_UUID_NIL; group->num = 1; // this must be removed (#474) group->my_idx = 0; // this must be -1 (#474) group->my_name = strdup(node_name ? node_name : NODE_NO_NAME); group->my_address = strdup(inc_addr ? inc_addr : NODE_NO_ADDR); group->state = GCS_GROUP_NON_PRIMARY; group->last_applied = GCS_SEQNO_ILL; // mark for recalculation group->last_node = -1; group->frag_reset = true; // just in case group->nodes = GU_CALLOC(group->num, gcs_node_t); // this must be removed (#474) if (!group->nodes) return -ENOMEM; // this should be removed (#474) /// this should be removed (#474) gcs_node_init (&group->nodes[group->my_idx], group->cache, NODE_NO_ID, group->my_name, group->my_address, gcs_proto_ver, repl_proto_ver, appl_proto_ver); group->prim_uuid = GU_UUID_NIL; group->prim_seqno = GCS_SEQNO_ILL; group->prim_num = 0; group->prim_state = GCS_NODE_STATE_NON_PRIM; *(gcs_proto_t*)&group->gcs_proto_ver = gcs_proto_ver; *(int*)&group->repl_proto_ver = repl_proto_ver; *(int*)&group->appl_proto_ver = appl_proto_ver; group->quorum = GCS_QUORUM_NON_PRIMARY; group->last_applied_proto_ver = -1; return 0; } long gcs_group_init_history (gcs_group_t* group, gcs_seqno_t seqno, const gu_uuid_t* uuid) { bool negative_seqno = (seqno < 0); bool nil_uuid = !gu_uuid_compare (uuid, &GU_UUID_NIL); if (negative_seqno && !nil_uuid) { gu_error ("Non-nil history UUID with negative seqno (%lld) makes " "no sense.", (long long) seqno); return -EINVAL; } else if (!negative_seqno && nil_uuid) { gu_error ("Non-negative state seqno requires non-nil history UUID."); return -EINVAL; } group->act_id = seqno; group->group_uuid = *uuid; return 0; } /* Initialize nodes array from component message */ static inline gcs_node_t* group_nodes_init (const gcs_group_t* group, const gcs_comp_msg_t* comp) { const long my_idx = gcs_comp_msg_self (comp); const long nodes_num = gcs_comp_msg_num (comp); gcs_node_t* ret = GU_CALLOC (nodes_num, gcs_node_t); long i; if (ret) { for (i = 0; i < nodes_num; i++) { if (my_idx != i) { gcs_node_init (&ret[i], group->cache, gcs_comp_msg_id (comp, i), NULL, NULL, -1, -1, -1); } else { // this node gcs_node_init (&ret[i], group->cache, gcs_comp_msg_id (comp, i), group->my_name, group->my_address, group->gcs_proto_ver, group->repl_proto_ver, group->appl_proto_ver); } } } else { gu_error ("Could not allocate %ld x %z bytes", nodes_num, sizeof(gcs_node_t)); } return ret; } /* Free nodes array */ static void group_nodes_free (gcs_group_t* group) { register int i; /* cleanup after disappeared members */ for (i = 0; i < group->num; i++) { gcs_node_free (&group->nodes[i]); } if (group->nodes) gu_free (group->nodes); group->nodes = NULL; group->num = 0; group->my_idx = -1; } void gcs_group_free (gcs_group_t* group) { if (group->my_name) free ((char*)group->my_name); if (group->my_address) free ((char*)group->my_address); group_nodes_free (group); } /* Reset nodes array without breaking the statistics */ static inline void group_nodes_reset (gcs_group_t* group) { register long i; /* reset recv_acts at the nodes */ for (i = 0; i < group->num; i++) { if (i != group->my_idx) { gcs_node_reset (&group->nodes[i]); } else { gcs_node_reset_local (&group->nodes[i]); } } group->frag_reset = true; } /* Find node with the smallest last_applied */ static inline void group_redo_last_applied (gcs_group_t* group) { long n; long last_node = -1; gu_seqno_t last_applied = GU_LONG_LONG_MAX; for (n = 0; n < group->num; n++) { const gcs_node_t* const node = &group->nodes[n]; gcs_seqno_t const seqno = node->last_applied; bool count = node->count_last_applied; if (gu_unlikely (0 == group->last_applied_proto_ver)) { /* @note: this may be removed after quorum v1 is phased out */ count = (GCS_NODE_STATE_SYNCED == node->status || GCS_NODE_STATE_DONOR == node->status); } // gu_debug ("last_applied[%ld]: %lld", n, seqno); /* NOTE: It is crucial for consistency that last_applied algorithm * is absolutely identical on all nodes. Therefore for the * generality sake and future compatibility we have to assume * non-blocking donor. * GCS_BLOCKING_DONOR should never be defined unless in some * very custom builds. Commenting it out for safety sake. */ //#ifndef GCS_BLOCKING_DONOR if (count //#else // if ((GCS_NODE_STATE_SYNCED == node->status) /* ignore donor */ //#endif && (seqno < last_applied)) { assert (seqno >= 0); last_applied = seqno; last_node = n; } // extra diagnostic, ignore //else if (!count) { gu_warn("not counting %d", n); } } if (gu_likely (last_node >= 0)) { group->last_applied = last_applied; group->last_node = last_node; } } static void group_go_non_primary (gcs_group_t* group) { if (group->my_idx >= 0) { assert(group->num > 0); assert(group->nodes); group->nodes[group->my_idx].status = GCS_NODE_STATE_NON_PRIM; //@todo: Perhaps the same has to be applied to the rest of the nodes[]? } else { assert(-1 == group->my_idx); assert(0 == group->num); assert(NULL == group->nodes); } group->state = GCS_GROUP_NON_PRIMARY; group->conf_id = GCS_SEQNO_ILL; // what else? Do we want to change anything about the node here? } static const char group_empty_id[GCS_COMP_MEMB_ID_MAX_LEN + 1] = { 0, }; static void group_check_donor (gcs_group_t* group) { gcs_node_state_t const my_state = group->nodes[group->my_idx].status; const char* const donor_id = group->nodes[group->my_idx].donor; if (GCS_NODE_STATE_JOINER == my_state && memcmp (donor_id, group_empty_id, sizeof(group_empty_id))) { long i; for (i = 0; i < group->num; i++) { if (i != group->my_idx && !memcmp (donor_id, group->nodes[i].id, sizeof (group->nodes[i].id))) return; } gu_warn ("Donor %s is no longer in the group. State transfer cannot " "be completed, need to abort. Aborting...", donor_id); gu_abort(); } return; } /*! Processes state messages and sets group parameters accordingly */ static void group_post_state_exchange (gcs_group_t* group) { const gcs_state_msg_t* states[group->num]; gcs_state_quorum_t* quorum = &group->quorum; bool new_exchange = gu_uuid_compare (&group->state_uuid, &GU_UUID_NIL); long i; /* Collect state messages from nodes. */ /* Looping here every time is suboptimal, but simply counting state messages * is not straightforward too: nodes may disappear, so the final count may * include messages from the disappeared nodes. * Let's put it this way: looping here is reliable and not that expensive.*/ for (i = 0; i < group->num; i++) { states[i] = group->nodes[i].state_msg; if (NULL == states[i] || (new_exchange && gu_uuid_compare (&group->state_uuid, gcs_state_msg_uuid(states[i])))) return; // not all states from THIS state exch. received, wait } gu_debug ("STATE EXCHANGE: "GU_UUID_FORMAT" complete.", GU_UUID_ARGS(&group->state_uuid)); gcs_state_msg_get_quorum (states, group->num, quorum); if (quorum->version >= 0) { if (quorum->version < 2) { group->last_applied_proto_ver = 0; } else { group->last_applied_proto_ver = 1; } } else { gu_fatal ("Negative quorum version: %d", quorum->version); abort(); } // Update each node state based on quorum outcome: // is it up to date, does it need SST and stuff for (i = 0; i < group->num; i++) { gcs_node_update_status (&group->nodes[i], quorum); } if (quorum->primary) { // primary configuration if (new_exchange) { // new state exchange happened group->state = GCS_GROUP_PRIMARY; group->act_id = quorum->act_id; group->conf_id = quorum->conf_id + 1; group->group_uuid = quorum->group_uuid; group->prim_uuid = group->state_uuid; group->state_uuid = GU_UUID_NIL; } else { // no state exchange happend, processing old state messages assert (GCS_GROUP_PRIMARY == group->state); group->conf_id++; } group->prim_seqno = group->conf_id; group->prim_num = 0; for (i = 0; i < group->num; i++) { group->prim_num += gcs_node_is_joined (group->nodes[i].status); } assert (group->prim_num > 0); } else { // non-primary configuration group_go_non_primary (group); } gu_info ("Quorum results:" "\n\tversion = %u," "\n\tcomponent = %s," "\n\tconf_id = %lld," "\n\tmembers = %d/%d (joined/total)," "\n\tact_id = %lld," "\n\tlast_appl. = %lld," "\n\tprotocols = %d/%d/%d (gcs/repl/appl)," "\n\tgroup UUID = "GU_UUID_FORMAT, quorum->version, quorum->primary ? "PRIMARY" : "NON-PRIMARY", quorum->conf_id, group->prim_num, group->num, quorum->act_id, group->last_applied, quorum->gcs_proto_ver, quorum->repl_proto_ver, quorum->appl_proto_ver, GU_UUID_ARGS(&quorum->group_uuid)); group_check_donor(group); } // does basic sanity check of the component message (in response to #145) static void group_check_comp_msg (bool prim, long my_idx, long members) { if (my_idx >= 0) { if (my_idx < members) return; } else { if (!prim && (0 == members)) return; } gu_fatal ("Malformed component message from backend: " "%s, idx = %ld, members = %ld", prim ? "PRIMARY" : "NON-PRIMARY", my_idx, members); assert (0); gu_abort (); } gcs_group_state_t gcs_group_handle_comp_msg (gcs_group_t* group, const gcs_comp_msg_t* comp) { long new_idx, old_idx; gcs_node_t* new_nodes = NULL; ulong new_memb = 0; const bool prim_comp = gcs_comp_msg_primary (comp); const bool bootstrap = gcs_comp_msg_bootstrap(comp); const long new_my_idx = gcs_comp_msg_self (comp); const long new_nodes_num = gcs_comp_msg_num (comp); group_check_comp_msg (prim_comp, new_my_idx, new_nodes_num); if (new_my_idx >= 0) { gu_info ("New COMPONENT: primary = %s, bootstrap = %s, my_idx = %ld, " "memb_num = %ld", prim_comp ? "yes" : "no", bootstrap ? "yes" : "no", new_my_idx, new_nodes_num); new_nodes = group_nodes_init (group, comp); if (!new_nodes) { gu_fatal ("Could not allocate memory for %ld-node component.", gcs_comp_msg_num (comp)); assert(0); return -ENOMEM; } if (GCS_GROUP_PRIMARY == group->state) { gu_debug ("#281: Saving %s over %s", gcs_node_state_to_str(group->nodes[group->my_idx].status), gcs_node_state_to_str(group->prim_state)); group->prim_state = group->nodes[group->my_idx].status; } } else { // Self-leave message gu_info ("Received self-leave message."); assert (0 == new_nodes_num); assert (!prim_comp); } if (prim_comp) { /* Got PRIMARY COMPONENT - Hooray! */ assert (new_my_idx >= 0); if (group->state == GCS_GROUP_PRIMARY) { /* we come from previous primary configuration, relax */ } else if (bootstrap) { /* Is there need to initialize something else in this case? */ group->nodes[group->my_idx].bootstrap = true; } else { const bool first_component = #ifndef GCS_CORE_TESTING (1 == group->num) && !strcmp (NODE_NO_ID, group->nodes[0].id); #else (1 == group->num); #endif if (1 == new_nodes_num && first_component) { /* bootstrap new configuration */ assert (GCS_GROUP_NON_PRIMARY == group->state); assert (1 == group->num); assert (0 == group->my_idx); // This bootstraps initial primary component for state exchange gu_uuid_generate (&group->prim_uuid, NULL, 0); group->prim_seqno = 0; group->prim_num = 1; group->state = GCS_GROUP_PRIMARY; if (group->act_id < 0) { // no history provided: start a new one group->act_id = GCS_SEQNO_NIL; gu_uuid_generate (&group->group_uuid, NULL, 0); gu_info ("Starting new group from scratch: "GU_UUID_FORMAT, GU_UUID_ARGS(&group->group_uuid)); } // the following should be removed under #474 group->nodes[0].status = GCS_NODE_STATE_JOINED; /* initialize node ID to the one given by the backend - this way * we'll be recognized as coming from prev. conf. in node array * remap below */ strncpy ((char*)group->nodes[0].id, new_nodes[0].id, sizeof (new_nodes[0].id) - 1); } } } else { group_go_non_primary (group); } /* Remap old node array to new one to preserve action continuity */ for (new_idx = 0; new_idx < new_nodes_num; new_idx++) { /* find member index in old component by unique member id */ for (old_idx = 0; old_idx < group->num; old_idx++) { // just scan through old group if (!strcmp(group->nodes[old_idx].id, new_nodes[new_idx].id)) { /* the node was in previous configuration with us */ /* move node context to new node array */ gcs_node_move (&new_nodes[new_idx], &group->nodes[old_idx]); break; } } /* if wasn't found in new configuration, new member - * need to do state exchange */ new_memb |= (old_idx == group->num); } /* free old nodes array */ group_nodes_free (group); group->my_idx = new_my_idx; group->num = new_nodes_num; group->nodes = new_nodes; if (gcs_comp_msg_primary(comp) || bootstrap) { /* TODO: for now pretend that we always have new nodes and perform * state exchange because old states can carry outdated node status. * (also protocol voting needs to be redone) * However this means aborting ongoing actions. Find a way to avoid * this extra state exchange. Generate new state messages on behalf * of other nodes? see #238 */ new_memb = true; /* if new nodes joined, reset ongoing actions and state messages */ if (new_memb) { group_nodes_reset (group); group->state = GCS_GROUP_WAIT_STATE_UUID; group->state_uuid = GU_UUID_NIL; // prepare for state exchange } else { if (GCS_GROUP_PRIMARY == group->state) { /* since we don't have any new nodes since last PRIMARY, we skip state exchange */ group_post_state_exchange (group); } } group_redo_last_applied (group); } return group->state; } gcs_group_state_t gcs_group_handle_uuid_msg (gcs_group_t* group, const gcs_recv_msg_t* msg) { assert (msg->size == sizeof(gu_uuid_t)); if (GCS_GROUP_WAIT_STATE_UUID == group->state && 0 == msg->sender_idx /* check that it is from the representative */) { group->state_uuid = *(gu_uuid_t*)msg->buf; group->state = GCS_GROUP_WAIT_STATE_MSG; } else { gu_warn ("Stray state UUID msg: "GU_UUID_FORMAT " from node %ld (%s), current group state %s", GU_UUID_ARGS((gu_uuid_t*)msg->buf), msg->sender_idx, group->nodes[msg->sender_idx].name, gcs_group_state_str[group->state]); } return group->state; } static void group_print_state_debug(gcs_state_msg_t* state) { size_t str_len = 1024; char state_str[str_len]; gcs_state_msg_snprintf (state_str, str_len, state); gu_info ("%s", state_str); } gcs_group_state_t gcs_group_handle_state_msg (gcs_group_t* group, const gcs_recv_msg_t* msg) { if (GCS_GROUP_WAIT_STATE_MSG == group->state) { gcs_state_msg_t* state = gcs_state_msg_read (msg->buf, msg->size); if (state) { const gu_uuid_t* state_uuid = gcs_state_msg_uuid (state); if (!gu_uuid_compare(&group->state_uuid, state_uuid)) { gu_info ("STATE EXCHANGE: got state msg: "GU_UUID_FORMAT " from %ld (%s)", GU_UUID_ARGS(state_uuid), msg->sender_idx, gcs_state_msg_name(state)); if (gu_log_debug) group_print_state_debug(state); gcs_node_record_state (&group->nodes[msg->sender_idx], state); group_post_state_exchange (group); } else { gu_debug ("STATE EXCHANGE: stray state msg: "GU_UUID_FORMAT " from node %ld (%s), current state UUID: " GU_UUID_FORMAT, GU_UUID_ARGS(state_uuid), msg->sender_idx, gcs_state_msg_name(state), GU_UUID_ARGS(&group->state_uuid)); if (gu_log_debug) group_print_state_debug(state); gcs_state_msg_destroy (state); } } else { gu_warn ("Could not parse state message from node %d", msg->sender_idx, group->nodes[msg->sender_idx].name); } } return group->state; } /*! Returns new last applied value if it has changes, 0 otherwise */ gcs_seqno_t gcs_group_handle_last_msg (gcs_group_t* group, const gcs_recv_msg_t* msg) { gcs_seqno_t seqno; assert (GCS_MSG_LAST == msg->type); assert (sizeof(gcs_seqno_t) == msg->size); seqno = gcs_seqno_gtoh(*(gcs_seqno_t*)(msg->buf)); // This assert is too restrictive. It requires application to send // last applied messages while holding TO, otherwise there's a race // between threads. // assert (seqno >= group->last_applied); gcs_node_set_last_applied (&group->nodes[msg->sender_idx], seqno); if (msg->sender_idx == group->last_node && seqno > group->last_applied) { /* node that was responsible for the last value, has changed it. * need to recompute it */ gcs_seqno_t old_val = group->last_applied; group_redo_last_applied (group); if (old_val < group->last_applied) { gu_debug ("New COMMIT CUT %lld after %lld from %d", (long long)group->last_applied, (long long)seqno, msg->sender_idx); return group->last_applied; } } return 0; } /*! return true if this node is the sender to notify the calling thread of * success */ long gcs_group_handle_join_msg (gcs_group_t* group, const gcs_recv_msg_t* msg) { long const sender_idx = msg->sender_idx; gcs_node_t* sender = &group->nodes[sender_idx]; assert (GCS_MSG_JOIN == msg->type); // TODO: define an explicit type for the join message, like gcs_join_msg_t assert (msg->size == sizeof(gcs_seqno_t)); if (GCS_NODE_STATE_DONOR == sender->status || GCS_NODE_STATE_JOINER == sender->status) { long j; gcs_seqno_t seqno = gcs_seqno_gtoh(*(gcs_seqno_t*)msg->buf); gcs_node_t* peer = NULL; const char* peer_id = NULL; const char* peer_name = "left the group"; long peer_idx = -1; bool from_donor = false; const char* st_dir = NULL; // state transfer direction symbol if (GCS_NODE_STATE_DONOR == sender->status) { peer_id = sender->joiner; from_donor = true; st_dir = "to"; assert (group->last_applied_proto_ver >= 0); if (0 == group->last_applied_proto_ver) { /* #454 - we don't switch to JOINED here, * instead going straignt to SYNCED */ } else { assert(sender->count_last_applied); sender->status = GCS_NODE_STATE_JOINED; } } else { peer_id = sender->donor; st_dir = "from"; if (group->quorum.version < 2) { // #591 remove after quorum v1 is phased out sender->status = GCS_NODE_STATE_JOINED; group->prim_num++; } else { if (seqno >= 0) { sender->status = GCS_NODE_STATE_JOINED; group->prim_num++; } else { sender->status = GCS_NODE_STATE_PRIM; } } } // Try to find peer. for (j = 0; j < group->num; j++) { // #483 if (j == sender_idx) continue; if (!memcmp(peer_id, group->nodes[j].id, sizeof (group->nodes[j].id))) { peer_idx = j; peer = &group->nodes[peer_idx]; peer_name = peer->name; break; } } if (j == group->num) { gu_warn ("Could not find peer: %s", peer_id); } if (seqno < 0) { gu_warn ("%ld (%s): State transfer %s %ld (%s) failed: %d (%s)", sender_idx, sender->name, st_dir, peer_idx, peer_name, (int)seqno, strerror((int)-seqno)); if (from_donor && peer_idx == group->my_idx && GCS_NODE_STATE_JOINER == group->nodes[peer_idx].status) { // this node will be waiting for SST forever. If it has only // one recv thread there is no (generic) way to wake it up. gu_fatal ("Will never receive state. Need to abort."); // return to core to shutdown the backend before aborting return -ENOTRECOVERABLE; } if (group->quorum.version < 2 && !from_donor && // #591 sender_idx == group->my_idx) { // remove after quorum v1 is phased out gu_fatal ("Faield to receive state. Need to abort."); return -ENOTRECOVERABLE; } } else { if (sender_idx == peer_idx) { gu_info ("Node %ld (%s) resyncs itself to group", sender_idx, sender->name); } else { gu_info ("%ld (%s): State transfer %s %ld (%s) complete.", sender_idx, sender->name, st_dir, peer_idx, peer_name); } } } else { if (GCS_NODE_STATE_PRIM == sender->status) { gu_warn ("Rejecting JOIN message from %ld (%s): new State Transfer" " required.", sender_idx, sender->name); } else { // should we freak out and throw an error? gu_warn ("Protocol violation. JOIN message sender %ld (%s) is not " "in state transfer (%s). Message ignored.", sender_idx, sender->name, gcs_node_state_to_str(sender->status)); } return 0; } return (sender_idx == group->my_idx); } long gcs_group_handle_sync_msg (gcs_group_t* group, const gcs_recv_msg_t* msg) { long sender_idx = msg->sender_idx; gcs_node_t* sender = &group->nodes[sender_idx]; assert (GCS_MSG_SYNC == msg->type); if (GCS_NODE_STATE_JOINED == sender->status || /* #454 - at this layer we jump directly from DONOR to SYNCED */ (0 == group->last_applied_proto_ver && GCS_NODE_STATE_DONOR == sender->status)) { sender->status = GCS_NODE_STATE_SYNCED; sender->count_last_applied = true; group_redo_last_applied (group);//from now on this node must be counted gu_info ("Member %ld (%s) synced with group.", sender_idx, sender->name); return (sender_idx == group->my_idx); } else { if (GCS_NODE_STATE_SYNCED != sender->status) { gu_warn ("SYNC message sender from non-JOINED %ld (%s). Ignored.", msg->sender_idx, sender->name); } else { gu_debug ("Redundant SYNC message from %ld (%s).", msg->sender_idx, sender->name); } return 0; } } static int group_find_node_by_state (gcs_group_t* group, gcs_node_state_t status) { int idx; for (idx = 0; idx < group->num; idx++) { gcs_node_t* node = &group->nodes[idx]; if (status == node->status && strcmp (node->name, GCS_ARBITRATOR_NAME)) // avoid arbitrator return idx; } return -EAGAIN; } static int group_find_node_by_name (gcs_group_t* const group, int const joiner_idx, const char* const name, int const name_len, gcs_node_state_t const status) { int idx; for (idx = 0; idx < group->num; idx++) { gcs_node_t* node = &group->nodes[idx]; if (!strncmp(node->name, name, name_len)) { if (joiner_idx == idx) { return -EHOSTDOWN; } else if (node->status >= status) { return idx; } else if (node->status >= GCS_NODE_STATE_JOINER) { /* will eventually become SYNCED */ return -EAGAIN; } else { /* technically we could return -EDEADLK here, but as long as * it is not -EAGAIN, it does not matter. If the node is in a * PRIMARY state, it is as good as not found. */ break; } } } return -EHOSTUNREACH; } /* Calls group_find_node_by_name() for each name in comma-separated list, * falls back to group_find_node_by_state() if name (or list) is empty. */ static int group_for_each_donor_in_string (gcs_group_t* const group, int const joiner_idx, const char* const str, int const str_len, gcs_node_state_t const status) { assert (str != NULL); const char* begin = str; const char* end; int err = -EHOSTDOWN; /* worst error */ do { end = strchr(begin, ','); int len; if (NULL == end) { len = str_len - (begin - str); } else { len = end - begin; } assert (len >= 0); int const idx = len > 0 ? /* consider empty name as "any" */ group_find_node_by_name (group, joiner_idx, begin, len, status) : /* err == -EAGAIN here means that at least one of the nodes in the * list will be available later, so don't try others. */ (err == -EAGAIN ? err : group_find_node_by_state(group, status)); if (idx >= 0) return idx; /* once we hit -EAGAIN, don't try to change error code: this means * that at least one of the nodes in the list will become available. */ if (-EAGAIN != err) err = idx; begin = end + 1; /* skip comma */ } while (end != NULL); return err; } /*! * Selects and returns the index of state transfer donor, if available. * Updates donor and joiner status if state transfer is possible * * @return * donor index or negative error code: * -EHOSTUNREACH if reqiested donor is not available * -EAGAIN if there were no nodes in the proper state. */ static int group_select_donor (gcs_group_t* group, long const joiner_idx, const char* const donor_string, bool const desync) { static gcs_node_state_t const min_donor_state = GCS_NODE_STATE_SYNCED; int donor_idx; int const donor_len = strlen(donor_string); bool const required_donor = (donor_len > 0); if (desync) { /* sender wants to become "donor" itself */ assert(donor_len > 0); gcs_node_state_t const st = group->nodes[joiner_idx].status; if (st >= min_donor_state) donor_idx = joiner_idx; else donor_idx = -EAGAIN; } else { /* if donor_string is empty, it will fallback to find_node_by_state() */ donor_idx = group_for_each_donor_in_string (group, joiner_idx, donor_string, donor_len, min_donor_state); } if (donor_idx >= 0) { gcs_node_t* const joiner = &group->nodes[joiner_idx]; gcs_node_t* const donor = &group->nodes[donor_idx]; assert(donor_idx != joiner_idx || desync); if (desync) { gu_info ("Node %d (%s) desyncs itself from group", donor_idx, donor->name); } else { gu_info ("Node %d (%s) requested state transfer from '%s'. " "Selected %d (%s)(%s) as donor.", joiner_idx,joiner->name, required_donor ?donor_string:"*any*",donor_idx,donor->name, gcs_node_state_to_str(donor->status)); } // reserve donor, confirm joiner (! assignment order is significant !) joiner->status = GCS_NODE_STATE_JOINER; donor->status = GCS_NODE_STATE_DONOR; memcpy (donor->joiner, joiner->id, GCS_COMP_MEMB_ID_MAX_LEN+1); memcpy (joiner->donor, donor->id, GCS_COMP_MEMB_ID_MAX_LEN+1); } else { #if 0 gu_warn ("Node %d (%s) requested state transfer from '%s', " "but it is impossible to select State Transfer donor: %s", joiner_idx, group->nodes[joiner_idx].name, required_donor ? donor_name : "*any*", strerror (-donor_idx)); #endif } return donor_idx; } /* Cleanup ignored state request */ void gcs_group_ignore_action (gcs_group_t* group, struct gcs_act_rcvd* act) { if (act->act.type <= GCS_ACT_STATE_REQ) { gcs_gcache_free (group->cache, act->act.buf); } act->act.buf = NULL; act->act.buf_len = 0; act->act.type = GCS_ACT_ERROR; act->sender_idx = -1; assert (GCS_SEQNO_ILL == act->id); } static bool group_desync_request (const char* const donor) { return (strlen (GCS_DESYNC_REQ) == strlen(donor) && !strcmp(GCS_DESYNC_REQ, donor)); } /* NOTE: check gcs_request_state_transfer() for sender part. */ /*! Returns 0 if request is ignored, request size if it should be passed up */ long gcs_group_handle_state_request (gcs_group_t* group, struct gcs_act_rcvd* act) { // pass only to sender and to one potential donor const char* donor_name = act->act.buf; size_t donor_name_len = strlen(donor_name); long donor_idx = -1; long const joiner_idx = act->sender_idx; const char* joiner_name = group->nodes[joiner_idx].name; gcs_node_state_t joiner_status = group->nodes[joiner_idx].status; bool const desync = group_desync_request (donor_name); assert (GCS_ACT_STATE_REQ == act->act.type); if (joiner_status != GCS_NODE_STATE_PRIM && !desync) { const char* joiner_status_string = gcs_node_state_to_str(joiner_status); if (group->my_idx == joiner_idx) { gu_error ("Requesting state transfer while in %s. " "Ignoring.", joiner_status_string); act->id = -ECANCELED; return act->act.buf_len; } else { gu_error ("Node %ld (%s) requested state transfer, " "but its state is %s. Ignoring.", joiner_idx, joiner_name, joiner_status_string); gcs_group_ignore_action (group, act); return 0; } } donor_idx = group_select_donor(group, joiner_idx, donor_name, desync); assert (donor_idx != joiner_idx || desync || donor_idx < 0); assert (donor_idx == joiner_idx || !desync || donor_idx < 0); if (group->my_idx != joiner_idx && group->my_idx != donor_idx) { // if neither DONOR nor JOINER, ignore request gcs_group_ignore_action (group, act); return 0; } else if (group->my_idx == donor_idx) { act->act.buf_len -= donor_name_len + 1; memmove (*(void**)&act->act.buf, ((char*)act->act.buf) + donor_name_len + 1, act->act.buf_len); // now action starts with request, like it was supplied by application, // see gcs_request_state_transfer() } // Return index of donor (or error) in the seqno field to sender. // It will be used to detect error conditions (no availabale donor, // donor crashed and the like). // This may be ugly, well, any ideas? act->id = donor_idx; return act->act.buf_len; } static ssize_t group_memb_record_size (gcs_group_t* group) { ssize_t ret = 0; long idx; for (idx = 0; idx < group->num; idx++) { ret += strlen(group->nodes[idx].id) + 1; ret += strlen(group->nodes[idx].name) + 1; ret += strlen(group->nodes[idx].inc_addr) + 1; } return ret; } /* Creates new configuration action */ ssize_t gcs_group_act_conf (gcs_group_t* group, struct gcs_act* act, int* gcs_proto_ver) { if (*gcs_proto_ver < group->quorum.gcs_proto_ver) *gcs_proto_ver = group->quorum.gcs_proto_ver; // only go up, see #482 else if (group->quorum.gcs_proto_ver >= 0 && group->quorum.gcs_proto_ver < *gcs_proto_ver) { gu_warn ("Refusing GCS protocol version downgrade from %d to %d", *gcs_proto_ver, group->quorum.gcs_proto_ver); } ssize_t conf_size = sizeof(gcs_act_conf_t) + group_memb_record_size(group); gcs_act_conf_t* conf = malloc (conf_size); if (conf) { long idx; conf->seqno = group->act_id; conf->conf_id = group->conf_id; conf->memb_num = group->num; conf->my_idx = group->my_idx; conf->repl_proto_ver = group->quorum.repl_proto_ver; conf->appl_proto_ver = group->quorum.appl_proto_ver; memcpy (conf->uuid, &group->group_uuid, sizeof (gu_uuid_t)); if (group->num) { assert (conf->my_idx >= 0); conf->my_state = group->nodes[group->my_idx].status; char* ptr = &conf->data[0]; for (idx = 0; idx < group->num; idx++) { strcpy (ptr, group->nodes[idx].id); ptr += strlen(ptr) + 1; strcpy (ptr, group->nodes[idx].name); ptr += strlen(ptr) + 1; strcpy (ptr, group->nodes[idx].inc_addr); ptr += strlen(ptr) + 1; } } else { // self leave message assert (conf->conf_id < 0); assert (conf->my_idx < 0); conf->my_state = GCS_NODE_STATE_NON_PRIM; } act->buf = conf; act->buf_len = conf_size; act->type = GCS_ACT_CONF; return conf_size; } else { return -ENOMEM; } } // for future use in fake state exchange (in unit tests et.al. See #237, #238) static gcs_state_msg_t* group_get_node_state (gcs_group_t* group, long node_idx) { const gcs_node_t* node = &group->nodes[node_idx]; uint8_t flags = 0; if (0 == node_idx) flags |= GCS_STATE_FREP; if (node->count_last_applied) flags |= GCS_STATE_FCLA; if (node->bootstrap) flags |= GCS_STATE_FBOOTSTRAP; return gcs_state_msg_create ( &group->state_uuid, &group->group_uuid, &group->prim_uuid, group->prim_seqno, group->act_id, group->prim_num, group->prim_state, node->status, node->name, node->inc_addr, node->gcs_proto_ver, node->repl_proto_ver, node->appl_proto_ver, flags ); } /*! Returns state message object for this node */ gcs_state_msg_t* gcs_group_get_state (gcs_group_t* group) { return group_get_node_state (group, group->my_idx); } percona-xtradb-cluster-galera/gcs/src/gcs_group.h0000644000000000000000000001526012247075736022362 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_group.h 2562 2011-11-28 03:21:13Z alex $ */ /* * This header defines node specific context we need to maintain */ #ifndef _gcs_group_h_ #define _gcs_group_h_ #include #include #include "gcs_node.h" #include "gcs_recv_msg.h" #include "gcs_seqno.h" #include "gcs_state_msg.h" #include typedef enum gcs_group_state { GCS_GROUP_NON_PRIMARY, GCS_GROUP_WAIT_STATE_UUID, GCS_GROUP_WAIT_STATE_MSG, GCS_GROUP_PRIMARY, GCS_GROUP_STATE_MAX } gcs_group_state_t; extern const char* gcs_group_state_str[]; typedef struct gcs_group { gcache_t* cache; gcs_seqno_t act_id; // current(last) action seqno gcs_seqno_t conf_id; // current configuration seqno gu_uuid_t state_uuid; // state exchange id gu_uuid_t group_uuid; // group UUID long num; // number of nodes long my_idx; // my index in the group const char* my_name; const char* my_address; gcs_group_state_t state; // group state: PRIMARY | NON_PRIMARY gcs_seqno_t last_applied; // last_applied action group-wide long last_node; // node that reported last_applied bool frag_reset; // indicate that fragmentation was reset gcs_node_t* nodes; // array of node contexts /* values from the last primary component */ gu_uuid_t prim_uuid; gu_seqno_t prim_seqno; long prim_num; gcs_node_state_t prim_state; /* max supported protocols */ gcs_proto_t const gcs_proto_ver; int const repl_proto_ver; int const appl_proto_ver; gcs_state_quorum_t quorum; int last_applied_proto_ver; } gcs_group_t; /*! * Initialize group at startup */ extern long gcs_group_init (gcs_group_t* group, gcache_t* cache, const char* node_name, ///< can be null const char* inc_addr, ///< can be null gcs_proto_t gcs_proto_ver, int repl_proto_ver, int appl_proto_ver); /*! * Initialize group action history parameters. See gcs.h */ extern long gcs_group_init_history (gcs_group_t* group, gcs_seqno_t seqno, const gu_uuid_t* uuid); /*! * Free group resources */ extern void gcs_group_free (gcs_group_t* group); /*! Forget the action if it is not to be delivered */ extern void gcs_group_ignore_action (gcs_group_t* group, struct gcs_act_rcvd* rcvd); /*! * Handles component message - installs new membership, * cleans old one. * * @return * group state in case of success or * negative error code. */ extern gcs_group_state_t gcs_group_handle_comp_msg (gcs_group_t* group, const gcs_comp_msg_t* msg); extern gcs_group_state_t gcs_group_handle_uuid_msg (gcs_group_t* group, const gcs_recv_msg_t* msg); extern gcs_group_state_t gcs_group_handle_state_msg (gcs_group_t* group, const gcs_recv_msg_t* msg); extern gcs_seqno_t gcs_group_handle_last_msg (gcs_group_t* group, const gcs_recv_msg_t* msg); /*! @return 0 for success, 1 for (success && i_am_sender) * or negative error code */ extern long gcs_group_handle_join_msg (gcs_group_t* group, const gcs_recv_msg_t* msg); /*! @return 0 for success, 1 for (success && i_am_sender) * or negative error code */ extern long gcs_group_handle_sync_msg (gcs_group_t* group, const gcs_recv_msg_t* msg); /*! @return 0 if request is ignored, request size if it should be passed up */ extern long gcs_group_handle_state_request (gcs_group_t* group, struct gcs_act_rcvd* act); /*! * Handles action message. Is called often - therefore, inlined * * @return negative - error code, 0 - continue, positive - complete action */ static inline ssize_t gcs_group_handle_act_msg (gcs_group_t* const group, const gcs_act_frag_t* const frg, const gcs_recv_msg_t* const msg, struct gcs_act_rcvd* const rcvd) { long const sender_idx = msg->sender_idx; bool const local = (sender_idx == group->my_idx); ssize_t ret; assert (GCS_MSG_ACTION == msg->type); assert (sender_idx < group->num); assert (frg->act_id > 0); assert (frg->act_size > 0); // clear reset flag if set by own first fragment after reset flag was set group->frag_reset = (group->frag_reset && (!local || (0 != frg->frag_no))); ret = gcs_node_handle_act_frag (&group->nodes[sender_idx], frg, &rcvd->act, local); if (ret > 0) { assert (ret == rcvd->act.buf_len); rcvd->act.type = frg->act_type; if (gu_likely(GCS_ACT_TORDERED == rcvd->act.type && GCS_GROUP_PRIMARY == group->state && group->nodes[sender_idx].status >= GCS_NODE_STATE_DONOR && !(group->frag_reset && local))) { /* Common situation - * increment and assign act_id only for totally ordered actions * and only in PRIM (skip messages while in state exchange) */ rcvd->id = ++group->act_id; } else if (GCS_ACT_TORDERED == rcvd->act.type) { /* Rare situations */ if (local) { /* Let the sender know that it failed */ rcvd->id = -ERESTART; #ifndef NDEBUG gu_info ("Returning -ERESTART for TORDERED action: group->state" " = %s, sender->status = %s, frag_reset = %s", gcs_group_state_str[group->state], gcs_node_state_to_str(group->nodes[sender_idx].status), group->frag_reset ? "true" : "false"); #endif } else { /* Just ignore it */ ret = 0; gcs_group_ignore_action (group, rcvd); } } } return ret; } static inline gcs_group_state_t gcs_group_state (gcs_group_t* group) { return group->state; } static inline bool gcs_group_is_primary (gcs_group_t* group) { return (GCS_GROUP_PRIMARY == group->state); } static inline long gcs_group_my_idx (gcs_group_t* group) { return group->my_idx; } /*! * Creates new configuration action * @param group group handle * @param act GCS action object * @param proto protocol version gcs should use for this configuration */ extern ssize_t gcs_group_act_conf (gcs_group_t* group, struct gcs_act* act, int* proto); /*! Returns state object for state message */ extern gcs_state_msg_t* gcs_group_get_state (gcs_group_t* group); #endif /* _gcs_group_h_ */ percona-xtradb-cluster-galera/gcs/src/gcs_msg_type.c0000644000000000000000000000052512247075736023046 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_msg_type.c 477 2008-11-08 22:50:34Z alex $ */ #include "gcs_msg_type.h" const char* gcs_msg_type_string[GCS_MSG_MAX] = { "ERROR", "ACTION", "LAST", "COMPONENT", "STATE_UUID", "STATE_MSG", "JOIN", "SYNC", "FLOW", "CAUSAL" }; percona-xtradb-cluster-galera/gcs/src/gcs_msg_type.h0000644000000000000000000000261512247075736023055 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_msg_type.h 477 2008-11-08 22:50:34Z alex $ */ /* * Message types. */ #ifndef _gcs_msg_type_h_ #define _gcs_msg_type_h_ // NOTE! When changing this enumaration, make sure to change // gcs_msg_type_string[] in gcs_msg_type.c typedef enum gcs_msg_type { GCS_MSG_ERROR, // error happened when recv() GCS_MSG_ACTION, // action fragment GCS_MSG_LAST, // report about last applied action GCS_MSG_COMPONENT, // new component GCS_MSG_STATE_UUID,// state exchange UUID message GCS_MSG_STATE_MSG, // state exchange message GCS_MSG_JOIN, // massage saying that the node completed state transfer GCS_MSG_SYNC, // message saying that the node has synced with group GCS_MSG_FLOW, // flow control message GCS_MSG_CAUSAL, // causality token GCS_MSG_MAX } gcs_msg_type_t; extern const char* gcs_msg_type_string[GCS_MSG_MAX]; /* Types of private actions - should not care, * must be defined and used by the application */ /* Types of regular configuration mesages (both PRIM/NON_PRIM) */ typedef enum gcs_reg_type { GCS_REG_JOIN, // caused by member JOIN GCS_REG_LEAVE, // caused by member LEAVE GCS_REG_DISCONNECT, // caused by member DISCONNECT GCS_REG_NETWORK // caused by NETWORK failure? } gcs_reg_type_t; #endif // _gcs_message_h_ percona-xtradb-cluster-galera/gcs/src/gcs_node.c0000644000000000000000000001546312247075736022153 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_node.c 2734 2012-03-08 10:47:53Z teemu $ */ #include #include "gcs_node.h" /*! Initialize node context */ void gcs_node_init (gcs_node_t* const node, gcache_t* cache, const char* const id, const char* const name, const char* const inc_addr, int const gcs_proto_ver, int const repl_proto_ver, int const appl_proto_ver) { assert(strlen(id) > 0); assert(strlen(id) < sizeof(node->id)); memset (node, 0, sizeof (gcs_node_t)); strncpy ((char*)node->id, id, sizeof(node->id) - 1); node->bootstrap = false; node->status = GCS_NODE_STATE_NON_PRIM; node->name = strdup (name ? name : NODE_NO_NAME); node->inc_addr = strdup (inc_addr ? inc_addr : NODE_NO_ADDR); gcs_defrag_init (&node->app, cache); // GCS_ACT_TORDERED goes only here gcs_defrag_init (&node->oob, NULL); node->gcs_proto_ver = gcs_proto_ver; node->repl_proto_ver = repl_proto_ver; node->appl_proto_ver = appl_proto_ver; } /*! Move data from one node object to another */ void gcs_node_move (gcs_node_t* dst, gcs_node_t* src) { if (dst->name) free ((char*)dst->name); if (dst->inc_addr) free ((char*)dst->inc_addr); if (dst->state_msg) gcs_state_msg_destroy ((gcs_state_msg_t*)dst->state_msg); memcpy (dst, src, sizeof (gcs_node_t)); gcs_defrag_forget (&src->app); gcs_defrag_forget (&src->oob); src->name = NULL; src->inc_addr = NULL; src->state_msg = NULL; } /*! Mark node's buffers as reset (local node only) */ void gcs_node_reset_local (gcs_node_t* node) { gcs_defrag_reset (&node->app); gcs_defrag_reset (&node->oob); } /*! Reset node's receive buffers */ void gcs_node_reset (gcs_node_t* node) { gcs_defrag_free (&node->app); gcs_defrag_free (&node->oob); gcs_node_reset_local (node); } /*! Deallocate resources associated with the node object */ void gcs_node_free (gcs_node_t* node) { gcs_node_reset (node); if (node->name) { free ((char*)node->name); // was strdup'ed node->name = NULL; } if (node->inc_addr) { free ((char*)node->inc_addr); // was strdup'ed node->inc_addr = NULL; } if (node->state_msg) { gcs_state_msg_destroy ((gcs_state_msg_t*)node->state_msg); node->state_msg = NULL; } } /*! Record state message from the node */ void gcs_node_record_state (gcs_node_t* node, gcs_state_msg_t* state_msg) { if (node->state_msg) { gcs_state_msg_destroy ((gcs_state_msg_t*)node->state_msg); } node->state_msg = state_msg; // copy relevant stuff from state msg into node node->status = gcs_state_msg_current_state (state_msg); gcs_state_msg_get_proto_ver (state_msg, &node->gcs_proto_ver, &node->repl_proto_ver, &node->appl_proto_ver); if (node->name) free ((char*)node->name); node->name = strdup (gcs_state_msg_name (state_msg)); if (node->inc_addr) free ((char*)node->inc_addr); node->inc_addr = strdup (gcs_state_msg_inc_addr (state_msg)); } /*! Update node status according to quorum decisions */ void gcs_node_update_status (gcs_node_t* node, const gcs_state_quorum_t* quorum) { if (quorum->primary) { const gu_uuid_t* node_group_uuid = gcs_state_msg_group_uuid ( node->state_msg); const gu_uuid_t* quorum_group_uuid = &quorum->group_uuid; // TODO: what to do when quorum.proto is not supported by this node? if (!gu_uuid_compare (node_group_uuid, quorum_group_uuid)) { // node was a part of this group gcs_seqno_t node_act_id = gcs_state_msg_received (node->state_msg); if (node_act_id == quorum->act_id) { const gcs_node_state_t last_prim_state = gcs_state_msg_prim_state (node->state_msg); if (GCS_NODE_STATE_NON_PRIM == last_prim_state) { // the node just joined, but already is up to date: node->status = GCS_NODE_STATE_JOINED; gu_debug ("#281 Setting %s state to %s", node->name, gcs_node_state_to_str(node->status)); } else { // Keep node state from the previous primary comp. node->status = last_prim_state; gu_debug ("#281,#298 Carry over last prim state for %s: %s", node->name, gcs_node_state_to_str(node->status)); } } else { // gap in sequence numbers, needs a snapshot, demote status if (node->status > GCS_NODE_STATE_PRIM) { gu_info ("'%s' demoted %s->PRIMARY due to gap in history: " "%lld - %lld", node->name, gcs_node_state_to_str(node->status), node_act_id, quorum->act_id); } node->status = GCS_NODE_STATE_PRIM; } } else { // node joins completely different group, clear all status if (node->status > GCS_NODE_STATE_PRIM) { gu_info ("'%s' has a different history, demoted %s->PRIMARY", node->name, gcs_node_state_to_str(node->status)); } node->status = GCS_NODE_STATE_PRIM; } switch (node->status) { case GCS_NODE_STATE_DONOR: case GCS_NODE_STATE_SYNCED: node->count_last_applied = true; break; case GCS_NODE_STATE_JOINED: node->count_last_applied =(gcs_state_msg_flags (node->state_msg) & GCS_STATE_FCLA); break; case GCS_NODE_STATE_JOINER: case GCS_NODE_STATE_PRIM: node->count_last_applied = false; break; case GCS_NODE_STATE_NON_PRIM: case GCS_NODE_STATE_MAX: gu_fatal ("Internal logic error: state %d in " "primary configuration. Aborting.", node->status); abort(); break; } } else { /* Probably don't want to change anything here, quorum was a failure * anyway. This could be due to this being transient component, lacking * joined nodes from the configuraiton. May be next component will be * better. * * UPDATE (28.06.2011): as #477 shows, we need some consistency here: */ node->status = GCS_NODE_STATE_NON_PRIM; } /* Clear bootstrap flag so that it does not get carried to * subsequent configuration changes. */ node->bootstrap = false; } percona-xtradb-cluster-galera/gcs/src/gcs_node.h0000644000000000000000000000772412247075736022161 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_node.h 2847 2012-08-26 16:50:21Z alex $ */ /*! * Node context */ #ifndef _gcs_node_h_ #define _gcs_node_h_ #include #include "gcs.h" #include "gcs_defrag.h" #include "gcs_comp_msg.h" #include "gcs_state_msg.h" #include #define NODE_NO_ID "undefined" #define NODE_NO_NAME "unspecified" #define NODE_NO_ADDR "unspecified" struct gcs_node { gcs_seqno_t last_applied; // last applied action on that node bool count_last_applied; // should it be counted bool bootstrap; // is part of prim comp bootstrap process // long queue_len; // action queue length on that node gcs_node_state_t status; // node status int gcs_proto_ver;// supported protocol versions int repl_proto_ver;// int appl_proto_ver; gcs_defrag_t app; // defragmenter for application actions gcs_defrag_t oob; // defragmenter for out-of-band service acts. // globally unique id from a component message const char id[GCS_COMP_MEMB_ID_MAX_LEN + 1]; // to track snapshot status char joiner[GCS_COMP_MEMB_ID_MAX_LEN + 1]; char donor [GCS_COMP_MEMB_ID_MAX_LEN + 1]; const char* name; // human-given name const char* inc_addr; // incoming address - for load balancer const gcs_state_msg_t* state_msg;// state message }; typedef struct gcs_node gcs_node_t; /*! Initialize node context */ extern void gcs_node_init (gcs_node_t* node, gcache_t* gcache, const char* id, const char* name, ///< can be null const char* inc_addr, ///< can be null int gcs_proto_ver, int repl_proto_ver, int appl_proto_ver); /*! Move data from one node object to another */ extern void gcs_node_move (gcs_node_t* dest, gcs_node_t* src); /*! Deallocate resources associated with the node object */ extern void gcs_node_free (gcs_node_t* node); /*! Reset node's receive buffers */ extern void gcs_node_reset (gcs_node_t* node); /*! Mark node's buffers as reset, but don't do it actually (local node only) */ extern void gcs_node_reset_local (gcs_node_t* node); /*! * Handles action message. Is called often - therefore, inlined * * @return */ static inline ssize_t gcs_node_handle_act_frag (gcs_node_t* node, const gcs_act_frag_t* frg, struct gcs_act* act, bool local) { if (gu_likely(GCS_ACT_SERVICE != frg->act_type)) { return gcs_defrag_handle_frag (&node->app, frg, act, local); } else if (GCS_ACT_SERVICE == frg->act_type) { return gcs_defrag_handle_frag (&node->oob, frg, act, local); } else { gu_warn ("Unrecognised action type: %d", frg->act_type); assert(0); return -EPROTO; } } static inline void gcs_node_set_last_applied (gcs_node_t* node, gcs_seqno_t seqno) { if (gu_unlikely(seqno < node->last_applied)) { gu_warn ("Received bogus LAST message: %lld, from node %s, " "expected >= %lld. Ignoring.", seqno, node->id, node->last_applied); } else { node->last_applied = seqno; } } static inline gcs_seqno_t gcs_node_get_last_applied (gcs_node_t* node) { return node->last_applied; } /*! Record state message from the node */ extern void gcs_node_record_state (gcs_node_t* node, gcs_state_msg_t* state); /*! Update node status according to quorum decisions */ extern void gcs_node_update_status (gcs_node_t* node, const gcs_state_quorum_t* quorum); static inline gcs_node_state_t gcs_node_get_status (gcs_node_t* node) { return node->status; } static inline bool gcs_node_is_joined (gcs_node_state_t st) { return (st >= GCS_NODE_STATE_DONOR); } #endif /* _gcs_node_h_ */ percona-xtradb-cluster-galera/gcs/src/gcs_params.c0000644000000000000000000001520212247075736022500 0ustar rootroot00000000000000/* * Copyright (C) 2010-2012 Codership Oy * * $Id: gcs_params.c 2887 2012-10-24 16:32:16Z alex $ */ #include "gcs_params.h" #include #include const char* const GCS_PARAMS_FC_FACTOR = "gcs.fc_factor"; const char* const GCS_PARAMS_FC_LIMIT = "gcs.fc_limit"; const char* const GCS_PARAMS_FC_MASTER_SLAVE = "gcs.fc_master_slave"; const char* const GCS_PARAMS_FC_DEBUG = "gcs.fc_debug"; const char* const GCS_PARAMS_SYNC_DONOR = "gcs.sync_donor"; const char* const GCS_PARAMS_MAX_PKT_SIZE = "gcs.max_packet_size"; const char* const GCS_PARAMS_RECV_Q_HARD_LIMIT = "gcs.recv_q_hard_limit"; const char* const GCS_PARAMS_RECV_Q_SOFT_LIMIT = "gcs.recv_q_soft_limit"; const char* const GCS_PARAMS_MAX_THROTTLE = "gcs.max_throttle"; static double const GCS_PARAMS_DEFAULT_FC_FACTOR = 1.0; static long const GCS_PARAMS_DEFAULT_FC_LIMIT = 16; static bool const GCS_PARAMS_DEFAULT_FC_MASTER_SLAVE = false; static long const GCS_PARAMS_DEFAULT_FC_DEBUG = 0; static bool const GCS_PARAMS_DEFAULT_SYNC_DONOR = false; static long const GCS_PARAMS_DEFAULT_MAX_PKT_SIZE = 64500; static ssize_t const GCS_PARAMS_DEFAULT_RECV_Q_HARD_LIMIT = SSIZE_MAX; static double const GCS_PARAMS_DEFAULT_RECV_Q_SOFT_LIMIT = 0.25; static double const GCS_PARAMS_DEFAULT_MAX_THROTTLE = 0.25; static long params_init_bool (gu_config_t* conf, const char* const name, bool const def_val, bool* const var) { bool val; long rc = gu_config_get_bool(conf, name, &val); if (rc < 0) { /* Cannot parse parameter value */ gu_error ("Bad %s value", name); return rc; } else if (rc > 0) { /* Parameter value not set, use default */ val = def_val; gu_config_set_bool (conf, name, val); } *var = val; return 0; } static long params_init_long (gu_config_t* conf, const char* const name, long const def_val, long min_val, long max_val, long* const var) { int64_t val; long rc = gu_config_get_int64(conf, name, &val); if (rc < 0) { /* Cannot parse parameter value */ gu_error ("Bad %s value", name); return rc; } else if (rc > 0) { /* Parameter value not set, use default */ val = def_val; gu_config_set_int64 (conf, name, val); } else { /* Found parameter value */ if (max_val == min_val) { max_val = LONG_MAX; min_val = LONG_MIN; } if (val < min_val || val > max_val) { gu_error ("%s value out of range [%ld, %ld]: %"PRIi64, name, min_val, max_val, val); return -EINVAL; } } *var = val; return 0; } static long params_init_int64 (gu_config_t* conf, const char* const name, int64_t const def_val, int64_t const min_val, int64_t const max_val, int64_t* const var) { int64_t val; long rc = gu_config_get_int64(conf, name, &val); if (rc < 0) { /* Cannot parse parameter value */ gu_error ("Bad %s value", name); return rc; } else if (rc > 0) { /* Parameter value not set, use default */ val = def_val; gu_config_set_int64 (conf, name, val); } else { /* Found parameter value */ if ((min_val != max_val) && (val < min_val || val > max_val)) { gu_error ("%s value out of range [%"PRIi64", %"PRIi64"]: %"PRIi64, name, min_val, max_val, val); return -EINVAL; } } *var = val; return 0; } static long params_init_double (gu_config_t* conf, const char* const name, double const def_val, double const min_val, double const max_val, double* const var) { double val; long rc = gu_config_get_double(conf, name, &val); if (rc < 0) { /* Cannot parse parameter value */ gu_error ("Bad %s value", name); return rc; } else if (rc > 0) { /* Parameter value not set, use default */ val = def_val; gu_config_set_double (conf, name, val); } else { /* Found parameter value */ if ((min_val != max_val) && (val < min_val || val > max_val)) { gu_error ("%s value out of range [%f, %f]: %f", name, min_val, max_val, val); return -EINVAL; } } *var = val; return 0; } long gcs_params_init (struct gcs_params* params, gu_config_t* config) { long ret; if ((ret = params_init_long (config, GCS_PARAMS_FC_LIMIT, GCS_PARAMS_DEFAULT_FC_LIMIT, 0, LONG_MAX, ¶ms->fc_base_limit))) return ret; if ((ret = params_init_long (config, GCS_PARAMS_FC_DEBUG, GCS_PARAMS_DEFAULT_FC_DEBUG, 0, LONG_MAX, ¶ms->fc_debug))) return ret; if ((ret = params_init_long (config, GCS_PARAMS_MAX_PKT_SIZE, GCS_PARAMS_DEFAULT_MAX_PKT_SIZE,0,LONG_MAX, ¶ms->max_packet_size))) return ret; if ((ret = params_init_double (config, GCS_PARAMS_FC_FACTOR, GCS_PARAMS_DEFAULT_FC_FACTOR, 0.0, 1.0, ¶ms->fc_resume_factor))) return ret; if ((ret = params_init_double (config, GCS_PARAMS_RECV_Q_SOFT_LIMIT, GCS_PARAMS_DEFAULT_RECV_Q_SOFT_LIMIT, 0.0, 1.0 - 1.e-9, ¶ms->recv_q_soft_limit))) return ret; if ((ret = params_init_double (config, GCS_PARAMS_MAX_THROTTLE, GCS_PARAMS_DEFAULT_MAX_THROTTLE, 0.0, 1.0 - 1.e-9, ¶ms->max_throttle))) return ret; int64_t tmp; if ((ret = params_init_int64 (config, GCS_PARAMS_RECV_Q_HARD_LIMIT, GCS_PARAMS_DEFAULT_RECV_Q_HARD_LIMIT, 0, 0, &tmp))) return ret; params->recv_q_hard_limit = tmp * 0.9; // allow for some meta overhead if ((ret = params_init_bool (config, GCS_PARAMS_FC_MASTER_SLAVE, GCS_PARAMS_DEFAULT_FC_MASTER_SLAVE, ¶ms->fc_master_slave))) return ret; if ((ret = params_init_bool (config, GCS_PARAMS_SYNC_DONOR, GCS_PARAMS_DEFAULT_SYNC_DONOR, ¶ms->sync_donor))) return ret; return 0; } percona-xtradb-cluster-galera/gcs/src/gcs_params.h0000644000000000000000000000226112247075736022506 0ustar rootroot00000000000000/* * Copyright (C) 2010 Codership Oy * * $Id: gcs_params.h 2779 2012-05-08 20:09:15Z alex $ */ #ifndef _gcs_params_h_ #define _gcs_params_h_ #include "galerautils.h" struct gcs_params { double fc_resume_factor; double recv_q_soft_limit; double max_throttle; ssize_t recv_q_hard_limit; long fc_base_limit; long max_packet_size; long fc_debug; bool fc_master_slave; bool sync_donor; }; extern const char* const GCS_PARAMS_FC_FACTOR; extern const char* const GCS_PARAMS_FC_LIMIT; extern const char* const GCS_PARAMS_FC_MASTER_SLAVE; extern const char* const GCS_PARAMS_FC_DEBUG; extern const char* const GCS_PARAMS_SYNC_DONOR; extern const char* const GCS_PARAMS_MAX_PKT_SIZE; extern const char* const GCS_PARAMS_RECV_Q_HARD_LIMIT; extern const char* const GCS_PARAMS_RECV_Q_SOFT_LIMIT; extern const char* const GCS_PARAMS_MAX_THROTTLE; /*! Initializes parameters from config or defaults (and updates config) * * @return 0 in case of success, * -EINVAL if some values were set incorrectly in config */ extern long gcs_params_init (struct gcs_params* params, gu_config_t* config); #endif /* _gcs_params_h_ */ percona-xtradb-cluster-galera/gcs/src/gcs_priv.h0000644000000000000000000000041012247075736022175 0ustar rootroot00000000000000/* * Copyright (C) 2011 Codership Oy * * $Id$ */ /*! * @file gcs_priv.h Global declarations private to GCS */ #ifndef _gcs_priv_h_ #define _gcs_priv_h_ #include "gcs.h" #define GCS_DESYNC_REQ "self-desync" #endif /* _gcs_priv_h_ */ percona-xtradb-cluster-galera/gcs/src/gcs_recv_msg.h0000644000000000000000000000070312247075736023027 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_recv_msg.h 2272 2011-07-28 23:24:41Z alex $ */ /*! * Receiving message context */ #ifndef _gcs_recv_msg_h_ #define _gcs_recv_msg_h_ #include "gcs_msg_type.h" typedef struct gcs_recv_msg { void* buf; long buf_len; long size; long sender_idx; gcs_msg_type_t type; } gcs_recv_msg_t; #endif /* _gcs_recv_msg_h_ */ percona-xtradb-cluster-galera/gcs/src/gcs_seqno.h0000644000000000000000000000071712247075736022354 0ustar rootroot00000000000000/* * Copyright (C) 2008-2012 Codership Oy * * $Id: gcs_seqno.h 2830 2012-06-23 12:49:47Z alex $ */ /* * Operations on seqno. */ #ifndef _gcs_seqno_h_ #define _gcs_seqno_h_ #include "galerautils.h" #include "gcs.h" #define gcs_seqno_le(x) ((gcs_seqno_t)gu_le64(x)) #define gcs_seqno_be(x) ((gcs_seqno_t)gu_be64(x)) #define gcs_seqno_htog(x) ((gcs_seqno_t)htog64(x)) #define gcs_seqno_gtoh gcs_seqno_htog #endif /* _gcs_seqno_h_ */ percona-xtradb-cluster-galera/gcs/src/gcs_sm.c0000644000000000000000000000771612247075736021647 0ustar rootroot00000000000000/* * Copyright (C) 2010 Codership Oy * * $Id: gcs_sm.c 2553 2011-11-25 14:18:07Z alex $ */ /*! * @file GCS Send Monitor. To ensure fair (FIFO) access to gcs_core_send() */ #include "gcs_sm.h" #include static void sm_init_stats (gcs_sm_stats_t* stats) { stats->sample_start = gu_time_monotonic(); stats->pause_start = 0; stats->paused_for = 0; stats->send_q_samples = 0; stats->send_q_len = 0; } gcs_sm_t* gcs_sm_create (long len, long n) { if ((len < 2 /* 2 is minimum */) || (len & (len - 1))) { gu_error ("Monitor length parameter is not a power of 2: %ld", len); return NULL; } if (n < 1) { gu_error ("Invalid monitor concurrency parameter: %ld", n); return NULL; } size_t sm_size = sizeof(gcs_sm_t) + len * sizeof(((gcs_sm_t*)(0))->wait_q[0]); gcs_sm_t* sm = gu_malloc(sm_size); if (sm) { sm_init_stats (&sm->stats); gu_mutex_init (&sm->lock, NULL); #ifdef GCS_SM_GRAB_RELEASE gu_cond_init (&sm->cond, NULL); sm->cond_wait = 0; #endif /* GCS_SM_GRAB_RELEASE */ sm->wait_q_len = len; sm->wait_q_mask = sm->wait_q_len - 1; sm->wait_q_head = 1; sm->wait_q_tail = 0; sm->users = 0; sm->entered = 0; sm->ret = 0; #ifdef GCS_SM_CONCURRENCY sm->cc = n; // concurrency param. #endif /* GCS_SM_CONCURRENCY */ sm->pause = false; memset (sm->wait_q, 0, sm->wait_q_len * sizeof(sm->wait_q[0])); } return sm; } long gcs_sm_close (gcs_sm_t* sm) { gu_info ("Closing send monitor..."); if (gu_unlikely(gu_mutex_lock (&sm->lock))) abort(); sm->ret = -EBADFD; if (sm->pause) _gcs_sm_continue_common (sm); gu_cond_t cond; gu_cond_init (&cond, NULL); // in case the queue is full while (sm->users >= (long)sm->wait_q_len) { gu_mutex_unlock (&sm->lock); usleep(1000); gu_mutex_lock (&sm->lock); } while (sm->users > 0) { // wait for cleared queue sm->users++; GCS_SM_INCREMENT(sm->wait_q_tail); _gcs_sm_enqueue_common (sm, &cond); sm->users--; GCS_SM_INCREMENT(sm->wait_q_head); } gu_cond_destroy (&cond); gu_mutex_unlock (&sm->lock); gu_info ("Closed send monitor."); return 0; } long gcs_sm_open (gcs_sm_t* sm) { long ret = -1; if (gu_unlikely(gu_mutex_lock (&sm->lock))) abort(); if (-EBADFD == sm->ret) /* closed */ { sm->ret = 0; } ret = sm->ret; gu_mutex_unlock (&sm->lock); if (ret) { gu_error ("Can't open send monitor: wrong state %d", ret); } return ret; } void gcs_sm_destroy (gcs_sm_t* sm) { gu_mutex_destroy(&sm->lock); gu_free (sm); } void gcs_sm_stats (gcs_sm_t* sm, long* q_len, double* q_len_avg, double* paused_for) { gcs_sm_stats_t tmp; long long now; bool paused; if (gu_unlikely(gu_mutex_lock (&sm->lock))) abort(); *q_len = sm->users; tmp = sm->stats; now = gu_time_monotonic(); paused = sm->pause; sm->stats.sample_start = now; sm->stats.pause_start = now; // if we are in paused state this is true // and if not - gcs_sm_pause() will correct it sm->stats.paused_for = 0; sm->stats.send_q_samples = 0; sm->stats.send_q_len = 0; gu_mutex_unlock (&sm->lock); if (paused) { // taking sample in a middle of a pause tmp.paused_for += now - tmp.pause_start; } if (tmp.paused_for >= 0) { *paused_for = ((double)tmp.paused_for) / (now - tmp.sample_start); } else { *paused_for = -1.0; } if (tmp.send_q_len >= 0 && tmp.send_q_samples >= 0){ if (tmp.send_q_samples > 0) { *q_len_avg = ((double)tmp.send_q_len) / tmp.send_q_samples; } else { *q_len_avg = 0.0; } } else { *q_len_avg = -1.0; } } percona-xtradb-cluster-galera/gcs/src/gcs_sm.h0000644000000000000000000002400312247075736021640 0ustar rootroot00000000000000/* * Copyright (C) 2010 Codership Oy * * $Id: gcs_sm.h 2553 2011-11-25 14:18:07Z alex $ */ /*! * @file GCS Send Monitor. To ensure fair (FIFO) access to gcs_core_send() */ #ifndef _gcs_sm_h_ #define _gcs_sm_h_ #include #include #ifdef GCS_SM_CONCURRENCY #define GCS_SM_CC sm->cc #else #define GCS_SM_CC 1 #endif /* GCS_SM_CONCURRENCY */ typedef struct gcs_sm_user { gu_cond_t* cond; bool wait; } gcs_sm_user_t; typedef struct gcs_sm_stats { long long sample_start; long long pause_start; long long paused_for; long send_q_samples; long send_q_len; } gcs_sm_stats_t; typedef struct gcs_sm { gcs_sm_stats_t stats; gu_mutex_t lock; #ifdef GCS_SM_GRAB_RELEASE gu_cond_t cond; long cond_wait; #endif /* GCS_SM_GRAB_RELEASE */ unsigned long wait_q_len; unsigned long wait_q_mask; unsigned long wait_q_head; unsigned long wait_q_tail; long users; long entered; long ret; #ifdef GCS_SM_CONCURRENCY long cc; #endif /* GCS_SM_CONCURRENCY */ bool pause; gcs_sm_user_t wait_q[]; } gcs_sm_t; /*! * Creates send monitor * * @param len size of the monitor, should be a power of 2 * @param n concurrency parameter (how many users can enter at the same time) */ extern gcs_sm_t* gcs_sm_create (long len, long n); /*! * Closes monitor for entering and makes all users to exit with error. * (entered users are not affected). Blocks until everybody exits */ extern long gcs_sm_close (gcs_sm_t* sm); /*! * (Re)opens monitor for entering. */ extern long gcs_sm_open (gcs_sm_t* sm); /*! * Deallocates resources associated with the monitor */ extern void gcs_sm_destroy (gcs_sm_t* sm); #define GCS_SM_INCREMENT(cursor) (cursor = ((cursor + 1) & sm->wait_q_mask)) static inline void _gcs_sm_wake_up_next (gcs_sm_t* sm) { long woken = sm->entered; assert (woken >= 0); assert (woken <= GCS_SM_CC); while (woken < GCS_SM_CC && sm->users > 0) { if (gu_likely(sm->wait_q[sm->wait_q_head].wait)) { assert (NULL != sm->wait_q[sm->wait_q_head].cond); // gu_debug ("Waking up: %lu", sm->wait_q_head); gu_cond_signal (sm->wait_q[sm->wait_q_head].cond); woken++; } else { /* skip interrupted */ assert (NULL == sm->wait_q[sm->wait_q_head].cond); gu_debug ("Skipping interrupted: %lu", sm->wait_q_head); sm->users--; GCS_SM_INCREMENT(sm->wait_q_head); } } assert (woken <= GCS_SM_CC); assert (sm->users >= 0); } /* wake up whoever might be waiting there */ static inline void _gcs_sm_wake_up_waiters (gcs_sm_t* sm) { #ifdef GCS_SM_GRAB_RELEASE if (gu_unlikely(sm->cond_wait)) { assert (sm->cond_wait > 0); sm->cond_wait--; gu_cond_signal (&sm->cond); } else #endif /* GCS_SM_GRAB_RELEASE */ if (!sm->pause) { _gcs_sm_wake_up_next(sm); } else { /* gcs_sm_continue() will do the rest */ } } static inline void _gcs_sm_leave_common (gcs_sm_t* sm) { assert (sm->entered < GCS_SM_CC); assert (sm->users > 0); sm->users--; assert (false == sm->wait_q[sm->wait_q_head].wait); assert (NULL == sm->wait_q[sm->wait_q_head].cond); GCS_SM_INCREMENT(sm->wait_q_head); _gcs_sm_wake_up_waiters (sm); } static inline bool _gcs_sm_enqueue_common (gcs_sm_t* sm, gu_cond_t* cond) { unsigned long tail = sm->wait_q_tail; sm->wait_q[tail].cond = cond; sm->wait_q[tail].wait = true; gu_cond_wait (cond, &sm->lock); assert(tail == sm->wait_q_head || false == sm->wait_q[tail].wait); assert(sm->wait_q[tail].cond == cond || false == sm->wait_q[tail].wait); sm->wait_q[tail].cond = NULL; register bool ret = sm->wait_q[tail].wait; sm->wait_q[tail].wait = false; return ret; } #ifdef GCS_SM_CONCURRENCY #define GCS_SM_HAS_TO_WAIT \ (sm->users > (sm->entered + 1) || sm->entered >= GCS_SM_CC || sm->pause) #else #define GCS_SM_HAS_TO_WAIT (sm->users > 1 || sm->pause) #endif /* GCS_SM_CONCURRENCY */ /*! * Synchronize with entry order to the monitor. Must be always followed by * gcs_sm_enter(sm, cond, true) * * @retval -EAGAIN - out of space * @retval -EBADFD - monitor closed * @retval >= 0 queue handle */ static inline long gcs_sm_schedule (gcs_sm_t* sm) { if (gu_unlikely(gu_mutex_lock (&sm->lock))) abort(); long ret = sm->ret; if (gu_likely((sm->users < (long)sm->wait_q_len) && (0 == ret))) { sm->users++; GCS_SM_INCREMENT(sm->wait_q_tail); /* even if we don't queue, cursor * needs to be advanced */ sm->stats.send_q_samples++; if (GCS_SM_HAS_TO_WAIT) { ret = sm->wait_q_tail + 1; // waiter handle /* here we want to distinguish between FC pause and real queue */ sm->stats.send_q_len += sm->users - 1; } return ret; // success } else if (0 == ret) { assert (sm->users == (long)sm->wait_q_len); ret = -EAGAIN; } assert(ret < 0); gu_mutex_unlock (&sm->lock); return ret; } /*! * Enter send monitor critical section * * @param sm send monitor object * @param cond condition to signal to wake up thread in case of wait * * @retval -EAGAIN - out of space * @retval -EBADFD - monitor closed * @retval -EINTR - was interrupted by another thread * @retval 0 - successfully entered */ static inline long gcs_sm_enter (gcs_sm_t* sm, gu_cond_t* cond, bool scheduled) { long ret = 0; /* if scheduled and no queue */ if (gu_likely (scheduled || (ret = gcs_sm_schedule(sm)) >= 0)) { if (GCS_SM_HAS_TO_WAIT) { if (gu_likely(_gcs_sm_enqueue_common (sm, cond))) { ret = sm->ret; } else { ret = -EINTR; } } assert (ret <= 0); if (gu_likely(0 == ret)) { assert(sm->users > 0); assert(sm->entered < GCS_SM_CC); sm->entered++; } else { if (gu_likely(-EINTR == ret)) { /* was interrupted, will be handled by someone else */ } else { /* monitor is closed, wake up others */ assert(sm->users > 0); _gcs_sm_leave_common(sm); } } gu_mutex_unlock (&sm->lock); } return ret; } static inline void gcs_sm_leave (gcs_sm_t* sm) { if (gu_unlikely(gu_mutex_lock (&sm->lock))) abort(); sm->entered--; assert(sm->entered >= 0); _gcs_sm_leave_common(sm); gu_mutex_unlock (&sm->lock); } static inline void gcs_sm_pause (gcs_sm_t* sm) { if (gu_unlikely(gu_mutex_lock (&sm->lock))) abort(); /* don't pause closed monitor */ if (gu_likely(0 == sm->ret) && !sm->pause) { sm->stats.pause_start = gu_time_monotonic(); sm->pause = true; } gu_mutex_unlock (&sm->lock); } static inline void _gcs_sm_continue_common (gcs_sm_t* sm) { sm->pause = false; _gcs_sm_wake_up_next(sm); /* wake up next waiter if any */ } static inline void gcs_sm_continue (gcs_sm_t* sm) { if (gu_unlikely(gu_mutex_lock (&sm->lock))) abort(); if (gu_likely(sm->pause)) { _gcs_sm_continue_common (sm); sm->stats.paused_for += gu_time_monotonic() - sm->stats.pause_start; } else { gu_debug ("Trying to continue unpaused monitor"); } gu_mutex_unlock (&sm->lock); } /*! * Interrupts waiter identified by handle (returned by gcs_sm_schedule()) * * @retval 0 - success * @retval -ESRCH - waiter is not in the queue. For practical purposes * it is impossible to discern already interrupted waiter and * the waiter that has entered the monitor */ static inline long gcs_sm_interrupt (gcs_sm_t* sm, long handle) { assert (handle > 0); long ret; if (gu_unlikely(gu_mutex_lock (&sm->lock))) abort(); handle--; if (gu_likely(sm->wait_q[handle].wait)) { assert (sm->wait_q[handle].cond != NULL); sm->wait_q[handle].wait = false; gu_cond_signal (sm->wait_q[handle].cond); sm->wait_q[handle].cond = NULL; ret = 0; if (!sm->pause && handle == (long)sm->wait_q_head) { /* gcs_sm_interrupt() was called right after the waiter was * signaled by gcs_sm_continue() or gcs_sm_leave() but before * the waiter has woken up. Wake up the next waiter */ _gcs_sm_wake_up_next(sm); } } else { ret = -ESRCH; } gu_mutex_unlock (&sm->lock); return ret; } /*! * Each call to this function resets stats and starts new sampling interval * * @param q_len current send queue length * @param q_len_avg set to an average number of preceding users seen by each * new one (not including itself) (-1 if stats overflown) * @param paused_for set to a fraction of time which monitor spent in a paused * state (-1 if stats overflown) */ extern void gcs_sm_stats (gcs_sm_t* sm, long* q_len, double* q_len_avg, double* paused_for); #ifdef GCS_SM_GRAB_RELEASE /*! Grabs sm object for out-of-order access * @return 0 or negative error code */ static inline long gcs_sm_grab (gcs_sm_t* sm) { long ret; if (gu_unlikely(gu_mutex_lock (&sm->lock))) abort(); while (!(ret = sm->ret) && sm->entered >= GCS_SM_CC) { sm->cond_wait++; gu_cond_wait (&sm->cond, &sm->lock); } if (ret) { assert (ret < 0); _gcs_sm_wake_up_waiters (sm); } else { assert (sm->entered < GCS_SM_CC); sm->entered++; } gu_mutex_unlock (&sm->lock); return ret; } /*! Releases sm object after gcs_sm_grab() */ static inline void gcs_sm_release (gcs_sm_t* sm) { if (gu_unlikely(gu_mutex_lock (&sm->lock))) abort(); sm->entered--; _gcs_sm_wake_up_waiters (sm); gu_mutex_unlock (&sm->lock); } #endif /* GCS_SM_GRAB_RELEASE */ #endif /* _gcs_sm_h_ */ percona-xtradb-cluster-galera/gcs/src/gcs_spread.c0000644000000000000000000004650112247075736022501 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_spread.c 3221 2013-08-23 14:44:02Z vlad $ */ /*****************************************/ /* Implementation of Spread GC backend */ /*****************************************/ #include #include #include #include #include #include #include "gcs_spread.h" #include "gcs_comp_msg.h" #define SPREAD_MAX_GROUPS 256 #if (GCS_COMP_MEMB_ID_MAX_LEN < MAX_GROUP_NAME) #error "GCS_COMP_MEMB_ID_MAX_LEN is smaller than Spread's MAX_GROUP_NAME" #error "This can make creation of component message impossible." #endif typedef struct string_array { int32 max_strings; int32 num_strings; char strings[0][MAX_GROUP_NAME]; } string_array_t; static string_array_t* string_array_alloc (const long n) { string_array_t *ret = NULL; ret = gu_malloc (sizeof (string_array_t) + n * MAX_GROUP_NAME); if (ret) { ret->max_strings = n; ret->num_strings = 0; } return ret; } static void string_array_free (string_array_t *a) { gu_free (a); } typedef enum spread_config { SPREAD_REGULAR, SPREAD_TRANSITIONAL } spread_config_t; typedef struct gcs_backend_conn { char *socket; char *channel; char *priv_name; char *priv_group; char *sender; long msg_type; long my_id; /* process ID returned with REG_MEMB message */ long config_id; // long memb_num; string_array_t *memb; string_array_t *groups; gcs_comp_msg_t *comp_msg; spread_config_t config; /* type of configuration: regular or trans */ mailbox mbox; } spread_t; /* this function converts socket address from conventional * "addr:port" notation to Spread's "port@addr" notation */ static long gcs_to_spread_socket (const char const *socket, char **sp_socket) { char *colon = strrchr (socket, ':'); size_t addr_len = colon - socket; size_t port_len = strlen (socket) - addr_len - 1; char *sps = NULL; if (!colon) return -EADDRNOTAVAIL; sps = (char *) strdup (socket); if (!sps) return -ENOMEM; memcpy (sps, colon+1, port_len); memcpy (sps + port_len + 1, socket, addr_len); sps[port_len] = '@'; *sp_socket = sps; return 0; } static const char* spread_default_socket = "localhost:4803"; static long spread_create (spread_t** spread, const char* socket) { long err = 0; spread_t *sp = GU_CALLOC (1, spread_t); *spread = NULL; if (!sp) { err = -ENOMEM; goto out0; } if (NULL == socket || strlen(socket) == 0) socket = spread_default_socket; err = gcs_to_spread_socket (socket, &sp->socket); if (err < 0) { goto out1; } sp->priv_name = GU_CALLOC (MAX_PRIVATE_NAME, char); if (!sp->priv_name) { err = -ENOMEM; goto out3; } sp->priv_group = GU_CALLOC (MAX_GROUP_NAME, char); if (!sp->priv_group) { err = -ENOMEM; goto out4; } sp->sender = GU_CALLOC (MAX_GROUP_NAME, char); if (!sp->sender) { err = -ENOMEM; goto out5; } sp->groups = string_array_alloc (SPREAD_MAX_GROUPS); if (!sp->groups) { err = -ENOMEM; goto out6; } sp->memb = string_array_alloc (SPREAD_MAX_GROUPS); if (!sp->memb) { err = -ENOMEM; goto out7; } sp->config = SPREAD_TRANSITIONAL; sp->config_id = -1; sp->comp_msg = NULL; gu_debug ("sp->priv_group: %p", sp->priv_group); *spread = sp; return err; out7: string_array_free (sp->groups); out6: gu_free (sp->sender); out5: gu_free (sp->priv_group); out4: gu_free (sp->priv_name); out3: free (sp->socket); out1: gu_free (sp); out0: return err; } /* Compiles a string of MAX_PRIVATE_NAME characters out of a supplied string and a number, returns -1 if digits overflow */ long spread_priv_name (char *name, const char *string, long n) { /* must make sure that it does not overflow MAX_PRIVATE_NAME */ long max_digit = 2; long max_string = MAX_PRIVATE_NAME - max_digit; long len = snprintf (name, max_string + 1, "%s", string); if (len > max_string) len = max_string; // truncated gu_debug ("len = %d, max_string = %d, MAX_PRIVATE_NAME = %d\n", len, (int)max_string, MAX_PRIVATE_NAME); len = snprintf (name + len, max_digit + 1, "_%d", (int)n); if (len > max_digit) return -1; // overflow return 0; } static GCS_BACKEND_CLOSE_FN(spread_close) { long err = 0; spread_t *spread = backend->conn; if (!spread) return -EBADFD; err = SP_leave (spread->mbox, spread->channel); if (err) { switch (err) { case ILLEGAL_GROUP: return -EADDRNOTAVAIL; case ILLEGAL_SESSION: return -ENOTCONN; case CONNECTION_CLOSED: return -ECONNRESET; default: return -EOPNOTSUPP; } } else { return 0; } } static GCS_BACKEND_DESTROY_FN(spread_destroy) { long err = 0; spread_t *spread = backend->conn; if (!spread) return -EBADFD; err = SP_disconnect (spread->mbox); if (spread->memb) string_array_free (spread->memb); if (spread->groups) string_array_free (spread->groups); if (spread->sender) gu_free (spread->sender); if (spread->priv_name) gu_free (spread->priv_name); if (spread->priv_group) gu_free (spread->priv_group); if (spread->channel) free (spread->channel); // obtained by strdup() if (spread->socket) free (spread->socket); if (spread->comp_msg) gcs_comp_msg_delete(spread->comp_msg); gu_free (spread); backend->conn = NULL; if (err) { switch (err) { case ILLEGAL_GROUP: return -EADDRNOTAVAIL; case ILLEGAL_SESSION: return -ENOTCONN; case CONNECTION_CLOSED: return -ECONNRESET; default: return -EOPNOTSUPP; } } else { return 0; } } static GCS_BACKEND_SEND_FN(spread_send) { long ret = 0; spread_t *spread = backend->conn; if (SPREAD_TRANSITIONAL == spread->config) return -EAGAIN; /* can it be that not all of the message is sent? */ ret = SP_multicast (spread->mbox, // mailbox SAFE_MESS, // service type spread->channel, // destination group (short)msg_type, // message from application len, // message length (const char*)buf // message buffer ); if (ret != len) { if (ret > 0) return -ECONNRESET; /* Failed to send the whole message */ switch (ret) { case ILLEGAL_SESSION: return -ENOTCONN; case CONNECTION_CLOSED: return -ECONNRESET; default: return -EOPNOTSUPP; } } #ifdef GCS_DEBUG_SPREAD // gu_debug ("spread_send: message sent: %p, len: %d\n", buf, ret); #endif return ret; } /* Substitutes old member array for new (taken from groups), * creates new groups buffer. */ static inline long spread_update_memb (spread_t* spread) { string_array_t* new_groups = string_array_alloc (SPREAD_MAX_GROUPS); if (!new_groups) return -ENOMEM; string_array_free (spread->memb); spread->memb = spread->groups; spread->groups = new_groups; return 0; } /* Temporarily this is done by simple iteration through the whole list. * for a cluster of 2-3 nodes this is probably most optimal. * But it clearly needs to be improved. */ static inline long spread_sender_id (const spread_t* const spread, const char* const sender_name) { long id; for (id = 0; id < spread->memb->num_strings; id++) { if (!strncmp(sender_name, spread->memb->strings[id], MAX_GROUP_NAME)) return id; } return GCS_SENDER_NONE; } static gcs_comp_msg_t* spread_comp_create (long my_id, long config_id, long memb_num, char names[][MAX_GROUP_NAME]) { gcs_comp_msg_t* comp = gcs_comp_msg_new (memb_num > 0, my_id, memb_num); long ret = -ENOMEM; if (comp) { long i; for (i = 0; i < memb_num; i++) { ret = gcs_comp_msg_add (comp, names[i]); if (ret != i) { gcs_comp_msg_delete (comp); goto fatal; } } gu_debug ("Created a component message of length %d.", gcs_comp_msg_size(comp)); return comp; } fatal: gu_fatal ("Failed to allocate component message: %s", strerror(-ret)); return NULL; } /* This function actually finalizes component message delivery: * it makes sure that the caller will receive the message and only then * changes handle state (spread->config)*/ static long spread_comp_deliver (spread_t* spread, void* buf, long len, gcs_msg_type_t* msg_type) { long ret; assert (spread->comp_msg); ret = gcs_comp_msg_size (spread->comp_msg); if (ret <= len) { memcpy (buf, spread->comp_msg, ret); spread->config = SPREAD_REGULAR; gcs_comp_msg_delete (spread->comp_msg); spread->comp_msg = NULL; *msg_type = GCS_MSG_COMPONENT; gu_debug ("Component message delivered (length %ld)", ret); } else { // provided buffer is too small for a message: // simply return required size } return ret; } static GCS_BACKEND_RECV_FN(spread_recv) { long ret = 0; spread_t *spread = backend->conn; service serv_type; int16 mess_type; int32 endian_mismatch; /* in case of premature exit */ *sender_idx = GCS_SENDER_NONE; *msg_type = GCS_MSG_ERROR; if (spread->comp_msg) { /* undelivered regular component message */ return spread_comp_deliver (spread, buf, len, msg_type); } if (!len) { // Spread does not seem to tolerate 0-sized buffer return 4096; } while (1) /* Loop while we don't receive the right message */ { ret = SP_receive (spread->mbox, // mailbox/connection &serv_type, // service type: // REGULAR_MESS/MEMBERSHIP_MESS spread->sender, // private group name of a sender spread->groups->max_strings, &spread->groups->num_strings, spread->groups->strings, &mess_type, // app. defined message type &endian_mismatch, len, // maximum message length (char*)buf // message buffer ); // gcs_log ("gcs_spread_recv: SP_receive returned\n"); // gcs_log ("endian_mismatch = %d\n", endian_mismatch); // /* seems there is a bug in either libsp or spread daemon */ // if (spread->groups->num_strings < 0 && ret > 0) // ret = GROUPS_TOO_SHORT; /* First, handle errors */ if (ret < 0) { switch (ret) { case BUFFER_TOO_SHORT: { if (Is_membership_mess (serv_type)) { // Ignore this error as membership messages don't fill // the buffer. Spread seems to have a bug - it returns // BUFFER_TOO_SHORT if you pass zero-length buffer for it. gu_debug ("BUFFER_TOO_SHORT in membership message."); ret = 0; break; } /* return required buffer size to caller */ gu_debug ("Error in SP_receive: BUFFER_TOO_SHORT"); gu_debug ("Supplied buffer len: %d, required: %d", len, (int) -endian_mismatch); gu_debug ("Message type: %d, sender: %d", mess_type, spread_sender_id (spread, spread->sender)); return -endian_mismatch; } case GROUPS_TOO_SHORT: { /* reallocate groups */ size_t num_groups = -spread->groups->num_strings; gu_warn ("Error in SP_receive: GROUPS_TOO_SHORT. " "Expect failure."); string_array_free (spread->groups); spread->groups = string_array_alloc (num_groups); if (!spread->groups) return -ENOMEM; /* try again */ continue; } case ILLEGAL_SESSION: gu_debug ("Error in SP_receive: ILLEGAL_SESSION"); return -ECONNABORTED; case CONNECTION_CLOSED: gu_debug ("Error in SP_receive: CONNECTION_CLOSED"); return -ECONNABORTED; case ILLEGAL_MESSAGE: gu_debug ("Error in SP_receive: ILLEGAL_MESSAGE"); continue; // wait for a legal one? default: gu_fatal ("unknown error = %d", ret); return -ENOTRECOVERABLE; } } /* At this point message was successfully received * and stored in buffer. */ if (Is_regular_mess (serv_type)) { // gu_debug ("received REGULAR message of type %d\n", // mess_type); assert (endian_mismatch >= 0); /* BUFFER_TOO_SMALL * must be handled before */ if (endian_mismatch) { gu_debug ("Spread returned ENDIAN_MISMATCH. Ignored."); } *msg_type = mess_type; *sender_idx = spread_sender_id (spread, spread->sender); assert (*sender_idx >= 0); assert (*sender_idx < spread->memb->num_strings); break; } else if (Is_membership_mess (serv_type)) { if (strncmp (spread->channel, spread->sender, MAX_GROUP_NAME)) continue; // wrong group/channel if (Is_transition_mess (serv_type)) { spread->config = SPREAD_TRANSITIONAL; gu_info ("Received TRANSITIONAL message"); continue; } else if (Is_reg_memb_mess (serv_type)) { //assert (spread->groups->num_strings > 0); spread->my_id = mess_type; gu_info ("Received REGULAR MEMBERSHIP " "in group \'%s\' with %d(%d) members " "where I'm member %d\n", spread->sender, spread->groups->num_strings, spread->groups->max_strings, spread->my_id); spread->config_id++; gu_debug ("Configuration number: %d", spread->config_id); spread->comp_msg = spread_comp_create (spread->my_id, spread->config_id, spread->groups->num_strings, spread->groups->strings); if (!spread->comp_msg) return -ENOTRECOVERABLE; /* Update membership info */ if ((ret = spread_update_memb(spread))) return ret; if (Is_caused_join_mess (serv_type)) { gu_info ("due to JOIN"); } else if (Is_caused_leave_mess (serv_type)) { gu_info ("due to LEAVE"); } else if (Is_caused_disconnect_mess (serv_type)) { gu_info ("due to DISCONNECT"); } else if (Is_caused_network_mess (serv_type)) { gu_info ("due to NETWORK"); } else { gu_warn ("unknown REG_MEMB message"); } ret = spread_comp_deliver (spread, buf, len, msg_type); } else if (Is_caused_leave_mess (serv_type)) { gu_info ("received SELF LEAVE message"); // *msg_type = GCS_MSG_COMPONENT; // memset (buf, 0, len); // trivial component spread->comp_msg = gcs_comp_msg_leave (); ret = spread_comp_deliver (spread, buf, len, msg_type); } else { gu_warn ("received unknown MEMBERSHIP message"); continue; // must do something ??? } } else if (Is_reject_mess (serv_type)) { gu_info ("received REJECTED message form %s", spread->sender); continue; } else /* Unknown message type */ { gu_warn ("received message of unknown type"); continue; } /* If we reached this point we have successfully received a message */ break; } /* message is already in buf and its length in ret */ return ret; } static GCS_BACKEND_NAME_FN(spread_name) { static char str[128]; int maj, min, patch; SP_version (&maj, &min, &patch); snprintf (str, 128, "Spread %d.%d.%d", maj, min, patch); return str; } /* Spread packet structure seem to be: * 42 bytes - Ethernet + IP + UDP header, 32 bytes Spread packet header + * 80 byte Spread message header present only in the first packet */ static GCS_BACKEND_MSG_SIZE_FN(spread_msg_size) { long ps = pkt_size; long frames = 0; const long eth_frame_size = 1514; const long spread_header_size = 154; // total headers in Spread packet const long spread_max_pkt_size = 31794; // 21 Ethernet frames if (pkt_size <= spread_header_size) { ps = spread_header_size + 1; gu_warn ("Requested packet size %d is too small, " "minimum possible is %d", pkt_size, ps); return pkt_size - ps; } if (pkt_size > spread_max_pkt_size) { ps = spread_max_pkt_size; gu_warn ("Requested packet size %d is too big, " "using maximum possible: %d", pkt_size, ps); } frames = ps / eth_frame_size; frames += ((frames * eth_frame_size) < ps); // one incomplete frame return (ps - frames * (42 + 32) - 80); } static GCS_BACKEND_OPEN_FN(spread_open) { long err = 0; spread_t* spread = backend->conn; if (!spread) return -EBADFD; if (!channel) { gu_error ("No channel supplied."); return -EINVAL; } spread->channel = strdup (channel); if (!spread->channel) return -ENOMEM; err = SP_join (spread->mbox, spread->channel); if (err) { switch (err) /* translate error codes */ { case ILLEGAL_GROUP: err = -EADDRNOTAVAIL; break; case ILLEGAL_SESSION: err = -EADDRNOTAVAIL; break; case CONNECTION_CLOSED: err = -ENETRESET; break; default: err = -ENOTCONN; break; } gu_error ("%s", strerror (-err)); return err; } gu_info ("Joined channel: %s", spread->channel); return err; } #if defined(__linux__) extern char *program_invocation_short_name; #endif GCS_BACKEND_CREATE_FN(gcs_spread_create) { long err = 0; long n = 0; spread_t* spread = NULL; backend->conn = NULL; if (!socket) { gu_error ("No socket supplied."); err = -EINVAL; goto out0; } if ((err = spread_create (&spread, socket))) goto out0; do { /* Try to generate unique name */ if (spread_priv_name (spread->priv_name, #if defined(__sun__) getexecname (), #elif defined(__APPLE__) || defined(__FreeBSD__) getprogname (), #elif defined(__linux__) program_invocation_short_name, #else "unknown", #endif n++)) { /* Failed to generate a name in the form * program_name_number. Let spread do it for us */ gu_free (spread->priv_name); spread->priv_name = NULL; } err = SP_connect (spread->socket, spread->priv_name, 0, 1, &spread->mbox, spread->priv_group); } while (REJECT_NOT_UNIQUE == err); if (err < 0) { gu_debug ("Spread connect error"); switch (err) /* translate error codes */ { case ILLEGAL_SPREAD: err = -ESOCKTNOSUPPORT; break; case COULD_NOT_CONNECT: err = -ENETUNREACH; break; case CONNECTION_CLOSED: err = -ENETRESET; break; case REJECT_ILLEGAL_NAME: err = -EADDRNOTAVAIL; gu_error ("Spread returned REJECT_ILLEGAL_NAME"); break; case REJECT_NO_NAME: err = -EDESTADDRREQ; gu_error ("Spread returned REJECT_NO_NAME." "Spread protocol error"); break; case REJECT_VERSION: default: gu_error ("Generic Spread error code: %d", err); err = -EPROTONOSUPPORT; break; } goto out1; } else { assert (err == ACCEPT_SESSION); err = 0; } gu_debug ("Connected to Spread: priv_name = %s, priv_group = %s", spread->priv_name, spread->priv_group); backend->conn = spread; backend->open = spread_open; backend->close = spread_close; backend->send = spread_send; backend->recv = spread_recv; backend->name = spread_name; backend->msg_size = spread_msg_size; backend->destroy = spread_destroy; return err; out1: spread_destroy (backend); out0: gu_error ("Creating Spread backend failed: %s (%d)", strerror (-err), err); return err; } percona-xtradb-cluster-galera/gcs/src/gcs_spread.h0000644000000000000000000000047412247075736022505 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_spread.h 327 2008-05-14 09:12:16Z alex $ */ /* * Definition of Spread GC backend */ #ifndef _gcs_spread_h_ #define _gcs_spread_h_ #include "gcs_backend.h" extern GCS_BACKEND_CREATE_FN (gcs_spread_create); #endif /* _gcs_spread_h_ */ percona-xtradb-cluster-galera/gcs/src/gcs_state_msg.c0000644000000000000000000006205312247075736023211 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_state_msg.c 2832 2012-06-25 20:06:26Z alex $ */ /* * Interface to state messages - implementation * */ #include #include #define GCS_STATE_MSG_VER 2 #define GCS_STATE_MSG_ACCESS #include "gcs_state_msg.h" #include "gcs_node.h" gcs_state_msg_t* gcs_state_msg_create (const gu_uuid_t* state_uuid, const gu_uuid_t* group_uuid, const gu_uuid_t* prim_uuid, gcs_seqno_t prim_seqno, gcs_seqno_t received, long prim_joined, gcs_node_state_t prim_state, gcs_node_state_t current_state, const char* name, const char* inc_addr, int gcs_proto_ver, int repl_proto_ver, int appl_proto_ver, uint8_t flags) { #define CHECK_PROTO_RANGE(LEVEL) \ if (LEVEL < (int)0 || LEVEL > (int)UINT8_MAX) { \ gu_error ("#LEVEL value %d is out of range [0, %d]", LEVEL,UINT8_MAX); \ return NULL; \ } CHECK_PROTO_RANGE(gcs_proto_ver); CHECK_PROTO_RANGE(repl_proto_ver); CHECK_PROTO_RANGE(appl_proto_ver); size_t name_len = strlen(name) + 1; size_t addr_len = strlen(inc_addr) + 1; gcs_state_msg_t* ret = gu_calloc (1, sizeof (gcs_state_msg_t) + name_len + addr_len); if (ret) { ret->state_uuid = *state_uuid; ret->group_uuid = *group_uuid; ret->prim_uuid = *prim_uuid; ret->prim_joined = prim_joined; ret->prim_seqno = prim_seqno; ret->received = received; ret->prim_state = prim_state; ret->current_state = current_state; ret->version = GCS_STATE_MSG_VER; ret->gcs_proto_ver = gcs_proto_ver; ret->repl_proto_ver= repl_proto_ver; ret->appl_proto_ver= appl_proto_ver; ret->name = (void*)(ret + 1); ret->inc_addr = ret->name + name_len; ret->flags = flags; // tmp is a workaround for some combination of GCC flags which don't // allow passing ret->name and ret->inc_addr directly even with casting // char* tmp = (char*)ret->name; strcpy ((char*)ret->name, name); // tmp = (char*)ret->inc_addr; strcpy ((char*)ret->inc_addr, inc_addr); } return ret; } void gcs_state_msg_destroy (gcs_state_msg_t* state) { gu_free (state); } /* Returns length needed to serialize gcs_state_msg_t for sending */ size_t gcs_state_msg_len (gcs_state_msg_t* state) { return ( sizeof (int8_t) + // version (reserved) sizeof (int8_t) + // flags sizeof (int8_t) + // gcs_proto_ver sizeof (int8_t) + // repl_proto_ver sizeof (int8_t) + // prim_state sizeof (int8_t) + // curr_state sizeof (int16_t) + // prim_joined sizeof (gu_uuid_t) + // state_uuid sizeof (gu_uuid_t) + // group_uuid sizeof (gu_uuid_t) + // conf_uuid sizeof (int64_t) + // received sizeof (int64_t) + // prim_seqno strlen (state->name) + 1 + strlen (state->inc_addr) + 1 + sizeof (uint8_t) // appl_proto_ver (in preparation for V1) ); } #define STATE_MSG_FIELDS_V0(_const,buf) \ _const int8_t* version = (buf); \ _const int8_t* flags = version + 1; \ _const int8_t* gcs_proto_ver = flags + 1; \ _const int8_t* repl_proto_ver = gcs_proto_ver + 1; \ _const int8_t* prim_state = repl_proto_ver + 1; \ _const int8_t* curr_state = prim_state + 1; \ _const int16_t* prim_joined = (int16_t*)(curr_state + 1); \ _const gu_uuid_t* state_uuid = (gu_uuid_t*)(prim_joined + 1); \ _const gu_uuid_t* group_uuid = state_uuid + 1; \ _const gu_uuid_t* prim_uuid = group_uuid + 1; \ _const int64_t* received = (int64_t*)(prim_uuid + 1); \ _const int64_t* prim_seqno = received + 1; \ _const char* name = (char*)(prim_seqno + 1); /* Serialize gcs_state_msg_t into buf */ ssize_t gcs_state_msg_write (void* buf, const gcs_state_msg_t* state) { STATE_MSG_FIELDS_V0(,buf); char* inc_addr = name + strlen (state->name) + 1; uint8_t* appl_proto_ver = (void*)(inc_addr + strlen(state->inc_addr) + 1); *version = GCS_STATE_MSG_VER; *flags = state->flags; *gcs_proto_ver = state->gcs_proto_ver; *repl_proto_ver = state->repl_proto_ver; *prim_state = state->prim_state; *curr_state = state->current_state; *prim_joined = htog16(((int16_t)state->prim_joined)); *state_uuid = state->state_uuid; *group_uuid = state->group_uuid; *prim_uuid = state->prim_uuid; *received = htog64(state->received); *prim_seqno = htog64(state->prim_seqno); strcpy (name, state->name); strcpy (inc_addr, state->inc_addr); *appl_proto_ver = state->appl_proto_ver; // in preparation for V1 return (appl_proto_ver + 1 - (uint8_t*)buf); } /* De-serialize gcs_state_msg_t from buf */ gcs_state_msg_t* gcs_state_msg_read (const void* buf, size_t buf_len) { /* beginning of the message is always version 0 */ STATE_MSG_FIELDS_V0(const,buf); const char* inc_addr = name + strlen (name) + 1; int appl_proto_ver = 0; if (*version >= 1) { appl_proto_ver = *(uint8_t*)(inc_addr + strlen(inc_addr) + 1); } gcs_state_msg_t* ret = gcs_state_msg_create ( state_uuid, group_uuid, prim_uuid, gtoh64(*prim_seqno), gtoh64(*received), gtoh16(*prim_joined), *prim_state, *curr_state, name, inc_addr, *gcs_proto_ver, *repl_proto_ver, appl_proto_ver, *flags ); if (ret) ret->version = *version; // dirty hack return ret; } /* Print state message contents to buffer */ int gcs_state_msg_snprintf (char* str, size_t size, const gcs_state_msg_t* state) { str[size - 1] = '\0'; // preventive termination return snprintf (str, size - 1, "\n\tVersion : %d" "\n\tFlags : %u" "\n\tProtocols : %d / %d / %d" "\n\tState : %s" "\n\tPrim state : %s" "\n\tPrim UUID : "GU_UUID_FORMAT "\n\tPrim seqno : %lld" "\n\tLast seqno : %lld" "\n\tPrim JOINED : %ld" "\n\tState UUID : "GU_UUID_FORMAT "\n\tGroup UUID : "GU_UUID_FORMAT "\n\tName : '%s'" "\n\tIncoming addr: '%s'\n", state->version, state->flags, state->gcs_proto_ver, state->repl_proto_ver, state->appl_proto_ver, gcs_node_state_to_str(state->current_state), gcs_node_state_to_str(state->prim_state), GU_UUID_ARGS(&state->prim_uuid), (long long)state->prim_seqno, (long long)state->received, state->prim_joined, GU_UUID_ARGS(&state->state_uuid), GU_UUID_ARGS(&state->group_uuid), state->name, state->inc_addr ); } /* Get state uuid */ const gu_uuid_t* gcs_state_msg_uuid (const gcs_state_msg_t* state) { return &state->state_uuid; } /* Get group uuid */ const gu_uuid_t* gcs_state_msg_group_uuid (const gcs_state_msg_t* state) { return &state->group_uuid; } /* Get action seqno */ gcs_seqno_t gcs_state_msg_received (const gcs_state_msg_t* state) { return state->received; } /* Get current node state */ gcs_node_state_t gcs_state_msg_current_state (const gcs_state_msg_t* state) { return state->current_state; } /* Get node state */ gcs_node_state_t gcs_state_msg_prim_state (const gcs_state_msg_t* state) { return state->prim_state; } /* Get node name */ const char* gcs_state_msg_name (const gcs_state_msg_t* state) { return state->name; } /* Get node incoming address */ const char* gcs_state_msg_inc_addr (const gcs_state_msg_t* state) { return state->inc_addr; } /* Get supported protocols */ void gcs_state_msg_get_proto_ver (const gcs_state_msg_t* state, int* gcs_proto_ver, int* repl_proto_ver, int* appl_proto_ver) { *gcs_proto_ver = state->gcs_proto_ver; *repl_proto_ver = state->repl_proto_ver; *appl_proto_ver = state->appl_proto_ver; } /* Get state message flags */ uint8_t gcs_state_msg_flags (const gcs_state_msg_t* state) { return state->flags; } /* Returns the node which is most representative of a group */ static const gcs_state_msg_t* state_nodes_compare (const gcs_state_msg_t* left, const gcs_state_msg_t* right) { assert (0 == gu_uuid_compare(&left->group_uuid, &right->group_uuid)); /* Allow GCS_SEQNO_ILL seqnos if bootstrapping from non-prim */ assert ((gcs_state_msg_flags(left) & GCS_STATE_FBOOTSTRAP) || left->prim_seqno != GCS_SEQNO_ILL); assert ((gcs_state_msg_flags(right) & GCS_STATE_FBOOTSTRAP) || right->prim_seqno != GCS_SEQNO_ILL); if (left->received < right->received) { assert (left->prim_seqno <= right->prim_seqno); return right; } else if (left->received > right->received) { assert (left->prim_seqno >= right->prim_seqno); return left; } else { // act_id's are equal, choose the one with higher prim_seqno. if (left->prim_seqno < right->prim_seqno) { return right; } else { return left; } } } /* Helper - just prints out all significant (JOINED) nodes */ static void state_report_uuids (char* buf, size_t buf_len, const gcs_state_msg_t* states[], long states_num, gcs_node_state_t min_state) { long j; for (j = 0; j < states_num; j++) { if (states[j]->current_state >= min_state) { int written = gcs_state_msg_snprintf (buf, buf_len, states[j]); buf += written; buf_len -= written; } } } #define GCS_STATE_MAX_LEN 722 #define GCS_STATE_BAD_REP ((void*)-1) /*! checks for inherited primary configuration, returns representative * @retval (void*)-1 in case of fatal error */ static const gcs_state_msg_t* state_quorum_inherit (const gcs_state_msg_t* states[], long states_num, gcs_state_quorum_t* quorum) { /* They all must have the same group_uuid or otherwise quorum is impossible. * Of those we need to find at least one that has complete state - * status >= GCS_STATE_JOINED. If we find none - configuration is * non-primary. * Of those with the status >= GCS_STATE_JOINED we choose the most * representative: with the highest act_seqno and prim_seqno. */ long i, j; const gcs_state_msg_t* rep = NULL; // find at least one JOINED/DONOR (donor was once joined) for (i = 0; i < states_num; i++) { if (gcs_node_is_joined(states[i]->current_state)) { rep = states[i]; break; } } if (!rep) { size_t buf_len = states_num * GCS_STATE_MAX_LEN; char* buf = gu_malloc (buf_len); if (buf) { state_report_uuids (buf, buf_len, states, states_num, GCS_NODE_STATE_NON_PRIM); #ifdef GCS_CORE_TESTING gu_warn ("Quorum: No node with complete state:\n%s", buf); #else /* Print buf into stderr in order to message truncation * of application logger. */ gu_warn ("Quorum: No node with complete state:\n"); fprintf(stderr, "%s\n", buf); #endif /* GCS_CORE_TESTING */ gu_free (buf); } return NULL; } // Check that all JOINED/DONOR have the same group UUID // and find most updated for (j = i + 1; j < states_num; j++) { if (gcs_node_is_joined(states[j]->current_state)) { if (gu_uuid_compare (&rep->group_uuid, &states[j]->group_uuid)) { // for now just freak out and print all conflicting nodes size_t buf_len = states_num * GCS_STATE_MAX_LEN; char* buf = gu_malloc (buf_len); if (buf) { state_report_uuids (buf, buf_len, states, states_num, GCS_NODE_STATE_DONOR); gu_fatal("Quorum impossible: conflicting group UUIDs:\n%s"); gu_free (buf); } else { gu_fatal("Quorum impossible: conflicting group UUIDs"); } return GCS_STATE_BAD_REP; } rep = state_nodes_compare (rep, states[j]); } } quorum->act_id = rep->received; quorum->conf_id = rep->prim_seqno; quorum->group_uuid = rep->group_uuid; quorum->primary = true; return rep; } struct candidate /* remerge candidate */ { gu_uuid_t prim_uuid; // V0 compatibility (0.8.1) gu_uuid_t state_uuid; gcs_seqno_t state_seqno; const gcs_state_msg_t* rep; int prim_joined; int found; }; static bool state_match_candidate (const gcs_state_msg_t* const s, struct candidate* const c, int const state_exchange_version) { switch (state_exchange_version) { case 0: // V0 compatibility (0.8.1) return (0 == gu_uuid_compare(&s->prim_uuid, &c->prim_uuid)); default: return ((0 == gu_uuid_compare(&s->group_uuid, &c->state_uuid)) && (s->received == c->state_seqno)); } } /* try to find representative remerge candidate */ static const struct candidate* state_rep_candidate (const struct candidate const c[], int const c_num) { assert (c_num > 0); const struct candidate* rep = &c[0]; gu_uuid_t const state_uuid = rep->state_uuid; gcs_seqno_t state_seqno = rep->state_seqno; int i; for (i = 1; i < c_num; i++) { if (!gu_uuid_compare(&c[i].state_uuid, &GU_UUID_NIL)) { /* Ignore nodes with undefined state uuid, they have been * added to group before remerge and have clean state. */ continue; } else if (gu_uuid_compare(&state_uuid, &GU_UUID_NIL) && gu_uuid_compare(&state_uuid, &c[i].state_uuid)) { /* There are candidates from different groups */ return NULL; } assert (state_seqno != c[i].state_seqno); if (state_seqno < c[i].state_seqno) { rep = &c[i]; state_seqno = rep->state_seqno; } } return rep; } /*! checks for full prim remerge after non-prim */ static const gcs_state_msg_t* state_quorum_remerge (const gcs_state_msg_t* const states[], long const states_num, bool const bootstrap, gcs_state_quorum_t* const quorum) { struct candidate* candidates = GU_CALLOC(states_num, struct candidate); if (!candidates) { gu_error ("Quorum: could not allocate %zd bytes for re-merge check.", states_num * sizeof(struct candidate)); return NULL; } int i, j; int candidates_found = 0; /* 1. Sort and count all nodes who have ever been JOINED by primary * component UUID */ for (i = 0; i < states_num; i++) { bool cond; if (bootstrap) { cond = gcs_state_msg_flags(states[i]) & GCS_STATE_FBOOTSTRAP; if (cond) gu_debug("found node %s with bootstrap flag", gcs_state_msg_name(states[i])); } else { cond = gcs_node_is_joined(states[i]->prim_state); } if (cond) { if (!bootstrap && GCS_NODE_STATE_JOINER == states[i]->current_state) { /* Joiner always has an undefined state * (and it should be its prim_state!) */ gu_warn ("Inconsistent state message from %d (%s): current " "state is %s, but the primary state was %s.", i, states[i]->name, gcs_node_state_to_str(states[i]->current_state), gcs_node_state_to_str(states[i]->prim_state)); continue; } assert(bootstrap || gu_uuid_compare(&states[i]->prim_uuid, &GU_UUID_NIL)); for (j = 0; j < candidates_found; j++) { if (state_match_candidate (states[i], &candidates[j], quorum->version)) { assert(states[i]->prim_joined == candidates[j].prim_joined); assert(candidates[j].found < candidates[j].prim_joined); assert(candidates[j].found > 0); candidates[j].found++; candidates[j].rep = state_nodes_compare (candidates[j].rep, states[i]); break; } } if (j == candidates_found) { // we don't have this candidate in the list yet candidates[j].prim_uuid = states[i]->prim_uuid; candidates[j].state_uuid = states[i]->group_uuid; candidates[j].state_seqno = states[i]->received; candidates[j].prim_joined = states[i]->prim_joined; candidates[j].rep = states[i]; candidates[j].found = 1; candidates_found++; assert(candidates_found <= states_num); } } } const gcs_state_msg_t* rep = NULL; if (candidates_found) { assert (candidates_found > 0); const struct candidate* const rc = state_rep_candidate (candidates, candidates_found); if (!rc) { gu_error ("Found more than one %s primary component candidate.", bootstrap ? "bootstrap" : "re-merged"); rep = NULL; } else { if (bootstrap) { gu_info ("Bootstrapped primary "GU_UUID_FORMAT" found: %d.", GU_UUID_ARGS(&rc->prim_uuid), rc->found); } else { gu_info ("%s re-merge of primary "GU_UUID_FORMAT" found: " "%d of %d.", rc->found == rc->prim_joined ? "Full" : "Partial", GU_UUID_ARGS(&rc->prim_uuid), rc->found, rc->prim_joined); } rep = rc->rep; assert (NULL != rep); assert (bootstrap || gcs_node_is_joined(rep->prim_state)); quorum->act_id = rep->received; quorum->conf_id = rep->prim_seqno; quorum->group_uuid = rep->group_uuid; quorum->primary = true; } } else { assert (0 == candidates_found); gu_warn ("No %s primary component found.", bootstrap ? "bootstrapped" : "re-merged"); } gu_free (candidates); return rep; } #if 0 // REMOVE WHEN NO LONGER NEEDED FOR REFERENCE /*! Checks for prim comp bootstrap */ static const gcs_state_msg_t* state_quorum_bootstrap (const gcs_state_msg_t* const states[], long const states_num, gcs_state_quorum_t* const quorum) { struct candidate* candidates = GU_CALLOC(states_num, struct candidate); if (!candidates) { gu_error ("Quorum: could not allocate %zd bytes for re-merge check.", states_num * sizeof(struct candidate)); return NULL; } int i, j; int candidates_found = 0; /* 1. Sort and count all nodes which have bootstrap flag set */ for (i = 0; i < states_num; i++) { if (gcs_state_msg_flags(states[i]) & GCS_STATE_FBOOTSTRAP) { gu_debug("found node %s with bootstrap flag", gcs_state_msg_name(states[i])); for (j = 0; j < candidates_found; j++) { if (state_match_candidate (states[i], &candidates[j], quorum->version)) { assert(states[i]->prim_joined == candidates[j].prim_joined); assert(candidates[j].found > 0); candidates[j].found++; candidates[j].rep = state_nodes_compare (candidates[j].rep, states[i]); break; } } if (j == candidates_found) { // we don't have this candidate in the list yet candidates[j].prim_uuid = states[i]->prim_uuid; candidates[j].state_uuid = states[i]->group_uuid; candidates[j].state_seqno = states[i]->received; candidates[j].prim_joined = states[i]->prim_joined; candidates[j].rep = states[i]; candidates[j].found = 1; candidates_found++; assert(candidates_found <= states_num); } } } const gcs_state_msg_t* rep = NULL; if (candidates_found) { assert (candidates_found > 0); const struct candidate* const rc = state_rep_candidate (candidates, candidates_found); if (!rc) { gu_error ("Found more than one bootstrap primary component " "candidate."); rep = NULL; } else { gu_info ("Bootstrapped primary "GU_UUID_FORMAT" found: %d.", GU_UUID_ARGS(&rc->prim_uuid), rc->found); rep = rc->rep; assert (NULL != rep); quorum->act_id = rep->received; quorum->conf_id = rep->prim_seqno; quorum->group_uuid = rep->group_uuid; quorum->primary = true; } } else { assert (0 == candidates_found); gu_warn ("No bootstrapped primary component found."); } gu_free (candidates); return rep; } #endif // 0 /* Get quorum decision from state messages */ long gcs_state_msg_get_quorum (const gcs_state_msg_t* states[], long states_num, gcs_state_quorum_t* quorum) { assert (states_num > 0); assert (NULL != states); long i; const gcs_state_msg_t* rep = NULL; *quorum = GCS_QUORUM_NON_PRIMARY; // pessimistic assumption /* find lowest commonly supported state exchange version */ quorum->version = states[0]->version; for (i = 1; i < states_num; i++) { if (quorum->version > states[i]->version) { quorum->version = states[i]->version; } } rep = state_quorum_inherit (states, states_num, quorum); if (!quorum->primary && rep != GCS_STATE_BAD_REP) { rep = state_quorum_remerge (states, states_num, false, quorum); } if (!quorum->primary && rep != GCS_STATE_BAD_REP) { rep = state_quorum_remerge (states, states_num, true, quorum); } if (!quorum->primary) { gu_error ("Failed to establish quorum."); return 0; } assert (rep != NULL); // select the highest commonly supported protocol: min(proto_max) #define INIT_PROTO_VER(LEVEL) quorum->LEVEL = rep->LEVEL INIT_PROTO_VER(gcs_proto_ver); INIT_PROTO_VER(repl_proto_ver); INIT_PROTO_VER(appl_proto_ver); for (i = 0; i < states_num; i++) { #define CHECK_MIN_PROTO_VER(LEVEL) \ if (states[i]->LEVEL < quorum->LEVEL) { \ quorum->LEVEL = states[i]->LEVEL; \ } // if (!gu_uuid_compare(&states[i]->group_uuid, &quorum->group_uuid)) { CHECK_MIN_PROTO_VER(gcs_proto_ver); CHECK_MIN_PROTO_VER(repl_proto_ver); CHECK_MIN_PROTO_VER(appl_proto_ver); // } } if (quorum->version < 2) {;} // for future generations if (quorum->version < 1) { // appl_proto_ver is not supported by all members assert (quorum->repl_proto_ver <= 1); if (1 == quorum->repl_proto_ver) quorum->appl_proto_ver = 1; else quorum->appl_proto_ver = 0; } return 0; } percona-xtradb-cluster-galera/gcs/src/gcs_state_msg.h0000644000000000000000000001246112247075736023214 0ustar rootroot00000000000000/* * Copyright (C) 2008-2011 Codership Oy * * $Id: gcs_state_msg.h 2738 2012-03-09 10:17:53Z alex $ */ /* * Interface to state messages * */ #ifndef _gcs_state_msg_h_ #define _gcs_state_msg_h_ #include "gcs.h" #include "gcs_seqno.h" #include "gcs_act_proto.h" #include #include /* State flags */ #define GCS_STATE_FREP 0x01 // group representative #define GCS_STATE_FCLA 0x02 // count last applied (for JOINED node) #define GCS_STATE_FBOOTSTRAP 0x04 // part of prim bootstrap process #ifdef GCS_STATE_MSG_ACCESS typedef struct gcs_state_msg { gu_uuid_t state_uuid; // UUID of the current state exchange gu_uuid_t group_uuid; // UUID of the group gu_uuid_t prim_uuid; // last PC state UUID gcs_seqno_t prim_seqno; // last PC state seqno gcs_seqno_t received; // last action seqno (received up to) long prim_joined; // number of joined nodes in its last PC gcs_node_state_t prim_state; // state of the node in its last PC gcs_node_state_t current_state; // current state of the node const char* name; // human assigned node name const char* inc_addr; // incoming address string int version; // version of state message int gcs_proto_ver; int repl_proto_ver; int appl_proto_ver; uint8_t flags; } gcs_state_msg_t; #else typedef struct gcs_state_msg gcs_state_msg_t; #endif /*! Quorum decisions */ typedef struct gcs_state_quorum { gu_uuid_t group_uuid; //! group UUID gcs_seqno_t act_id; //! next global seqno gcs_seqno_t conf_id; //! configuration id bool primary; //! primary configuration or not int version; //! state excahnge version (max understood by all) int gcs_proto_ver; int repl_proto_ver; int appl_proto_ver; } gcs_state_quorum_t; #define GCS_QUORUM_NON_PRIMARY (gcs_state_quorum_t){ \ GU_UUID_NIL, \ GCS_SEQNO_ILL, \ GCS_SEQNO_ILL, \ false, \ -1, -1, -1, -1 \ } extern gcs_state_msg_t* gcs_state_msg_create (const gu_uuid_t* state_uuid, const gu_uuid_t* group_uuid, const gu_uuid_t* prim_uuid, gcs_seqno_t prim_seqno, gcs_seqno_t received, long prim_joined, gcs_node_state_t prim_state, gcs_node_state_t current_state, const char* name, const char* inc_addr, int gcs_proto_ver, int repl_proto_ver, int appl_proto_ver, uint8_t flags); extern void gcs_state_msg_destroy (gcs_state_msg_t* state); /* Returns length needed to serialize gcs_state_msg_t for sending */ extern size_t gcs_state_msg_len (gcs_state_msg_t* state); /* Serialize gcs_state_msg_t into message */ extern ssize_t gcs_state_msg_write (void* msg, const gcs_state_msg_t* state); /* De-serialize gcs_state_msg_t from message */ extern gcs_state_msg_t* gcs_state_msg_read (const void* msg, size_t msg_len); /* Get state uuid */ extern const gu_uuid_t* gcs_state_msg_uuid (const gcs_state_msg_t* state); /* Get group uuid */ extern const gu_uuid_t* gcs_state_msg_group_uuid (const gcs_state_msg_t* state); /* Get last PC uuid */ //extern const gu_uuid_t* //gcs_state_prim_uuid (const gcs_state_msg_t* state); /* Get last received action seqno */ extern gcs_seqno_t gcs_state_msg_received (const gcs_state_msg_t* state); /* Get current node state */ extern gcs_node_state_t gcs_state_msg_current_state (const gcs_state_msg_t* state); /* Get last prim node state */ extern gcs_node_state_t gcs_state_msg_prim_state (const gcs_state_msg_t* state); /* Get node name */ extern const char* gcs_state_msg_name (const gcs_state_msg_t* state); /* Get node incoming address */ extern const char* gcs_state_msg_inc_addr (const gcs_state_msg_t* state); /* Get supported protocols */ extern void gcs_state_msg_get_proto_ver (const gcs_state_msg_t* state, int* gcs_proto_ver, int* repl_proto_ver, int* appl_proto_ver); /* Get state message flags */ extern uint8_t gcs_state_msg_flags (const gcs_state_msg_t* state); /*! Get quorum decision from state messages * * @param[in] states array of state message pointers * @param[in] states_num length of array * @param[out] quorum quorum calculations result * @retval 0 if there were no errors during processing. Quorum results are in * quorum parameter */ extern long gcs_state_msg_get_quorum (const gcs_state_msg_t* states[], long states_num, gcs_state_quorum_t* quorum); /* Print state message contents to buffer */ extern int gcs_state_msg_snprintf (char* str, size_t size, const gcs_state_msg_t* msg); #endif /* _gcs_state_msg_h_ */ percona-xtradb-cluster-galera/gcs/src/gcs_test.c0000644000000000000000000005527412247075736022211 0ustar rootroot00000000000000/* * Copyright (C) 2008-2012 Codership Oy * * $Id: gcs_test.c 3348 2013-11-02 10:56:57Z alex $ */ /***********************************************************/ /* This program imitates 3rd party application and */ /* tests GCS library in a dummy standalone configuration */ /***********************************************************/ #include #include #include #include #include #include #include #include #include #include #include "gcs.h" #include "gcs_test.h" #define USE_WAIT #define gcs_malloc(a) ((a*) malloc (sizeof (a))) static pthread_mutex_t gcs_test_lock = PTHREAD_MUTEX_INITIALIZER; static gcache_t* gcache = NULL; typedef struct gcs_test_log { FILE *file; pthread_mutex_t lock; } gcs_test_log_t; #define SEND_LOG "/dev/shm/gcs_test_send.log" #define RECV_LOG "/dev/shm/gcs_test_recv.log" static gcs_test_log_t *send_log, *recv_log; static bool throughput = true; // bench for throughput static bool total = true; // also enable TO locking typedef enum { GCS_TEST_SEND, GCS_TEST_RECV, GCS_TEST_REPL } gcs_test_repl_t; typedef struct gcs_test_thread { pthread_t thread; long id; struct gcs_action act; long n_tries; void* msg; char* log_msg; } gcs_test_thread_t; #define MAX_MSG_LEN (1 << 16) static long gcs_test_thread_create (gcs_test_thread_t *t, long id, long n_tries) { t->id = id; t->msg = calloc (MAX_MSG_LEN, sizeof(char)); t->act.buf = t->msg; t->act.size = MAX_MSG_LEN; t->act.seqno_g = GCS_SEQNO_ILL; t->act.seqno_l = GCS_SEQNO_ILL; t->act.type = GCS_ACT_TORDERED; t->n_tries = n_tries; if (t->msg) { t->log_msg = calloc (MAX_MSG_LEN, sizeof(char)); if (t->log_msg) return 0; } return -ENOMEM; } static long gcs_test_thread_destroy (gcs_test_thread_t *t) { if (t->msg) free (t->msg); if (t->log_msg) free (t->log_msg); return 0; } typedef struct gcs_test_thread_pool { long n_threads; long n_tries; long n_started; gcs_test_repl_t type; gcs_test_thread_t *threads; } gcs_test_thread_pool_t; static long gcs_test_thread_pool_create (gcs_test_thread_pool_t *pool, const gcs_test_repl_t type, const long n_threads, const long n_tries) { long err = 0; long i; // pool = gcs_malloc (gcs_test_thread_pool_t); // if (!pool) { err = errno; goto out; } pool->n_threads = n_threads; pool->type = type; pool->n_tries = n_tries; pool->n_started = 0; pool->threads = (gcs_test_thread_t *) calloc (pool->n_threads, sizeof (gcs_test_thread_t)); if (!pool->threads) { err = errno; fprintf (stderr, "Failed to allocate %ld thread objects: %ld (%s)\n", n_threads, err, strerror(err)); goto out1; } for (i = 0; i < pool->n_threads; i++) { if ((err = gcs_test_thread_create (pool->threads + i, i, n_tries))) { err = errno; fprintf (stderr, "Failed to create thread object %ld: %ld (%s)\n", i, err, strerror(err)); goto out2; } } // printf ("Created %ld thread objects\n", i); return 0; out2: while (i) { i--; gcs_test_thread_destroy (pool->threads + i); } free (pool->threads); out1: free (pool); //out: return err; } static void gcs_test_thread_pool_destroy (gcs_test_thread_pool_t* pool) { long i; if (pool->threads) { for (i = 0; i < pool->n_threads; i++) { gcs_test_thread_destroy (pool->threads + i); } free (pool->threads); } } static pthread_mutex_t make_msg_lock = PTHREAD_MUTEX_INITIALIZER; //static long total_tries; static inline long test_make_msg (char* msg, const long mlen) { static gcs_seqno_t count = 1; long len = 0; if (!throughput) { pthread_mutex_lock (&make_msg_lock); count++; pthread_mutex_unlock (&make_msg_lock); len = snprintf (msg, mlen, "%10d %9llu %s", rand(), (unsigned long long)count++, gcs_test_data); } else { len = rand() % mlen + 1; // just random length, we don't care about // contents } if (len >= mlen) return mlen; else return len; } static long test_log_open (gcs_test_log_t **log, const char *name) { char real_name[1024]; gcs_test_log_t *l = gcs_malloc (gcs_test_log_t); if (!l) return errno; snprintf (real_name, 1024, "%s.%lld", name, (long long)getpid()); // cppcheck-suppress memleak if (!(l->file = fopen (real_name, "w"))) return errno; pthread_mutex_init (&l->lock, NULL); *log = l; return 0; } static long test_log_close (gcs_test_log_t **log) { long err = 0; gcs_test_log_t *l = *log; if (l) { pthread_mutex_lock (&l->lock); err = fclose (l->file); pthread_mutex_unlock (&l->lock); pthread_mutex_destroy (&l->lock); } return err; } static inline long gcs_test_log_msg (gcs_test_log_t *log, const char *msg) { long err = 0; err = fprintf (log->file, "%s\n", msg); return err; } gcs_conn_t *gcs = NULL; gu_to_t *to = NULL; long msg_sent = 0; long msg_recvd = 0; long msg_repld = 0; long msg_len = 0; size_t size_sent = 0; size_t size_repld = 0; size_t size_recvd = 0; static inline long test_recv_log_create(gcs_test_thread_t* thread) { return snprintf (thread->log_msg, MAX_MSG_LEN - 1, "Thread %3ld(REPL): act_id = %lld, local_act_id = %lld, " "len = %lld: %s", thread->id, (long long)thread->act.seqno_g, (long long)thread->act.seqno_l, (long long)thread->act.size, (const char*)thread->act.buf); } static inline long test_send_log_create(gcs_test_thread_t* thread) { return snprintf (thread->log_msg, MAX_MSG_LEN - 1, "Thread %3ld (REPL): len = %lld, %s", thread->id, (long long) thread->act.size, (const char*)thread->act.buf); } static inline long test_log_msg (gcs_test_log_t* log, const char* msg) { long ret; pthread_mutex_lock (&log->lock); ret = fprintf (recv_log->file, "%s\n", msg); pthread_mutex_lock (&log->lock); return ret; } static inline long test_log_in_to (gu_to_t* to, gcs_seqno_t seqno, const char* msg) { long ret = 0; while ((ret = gu_to_grab (to, seqno)) == -EAGAIN) usleep(10000); if (!ret) {// success if (msg != NULL) gcs_test_log_msg (recv_log, msg); ret = gu_to_release (to, seqno); } return ret; } static gcs_seqno_t group_seqno = 0; static inline long test_send_last_applied (gcs_conn_t* gcs, gcs_seqno_t my_seqno) { long ret = 0; #define SEND_LAST_MASK ((1 << 14) - 1) // every 16K seqno if (!(my_seqno & SEND_LAST_MASK)) { ret = gcs_set_last_applied (gcs, my_seqno); if (ret) { fprintf (stderr,"gcs_set_last_applied(%lld) returned %ld\n", (long long)my_seqno, ret); } // if (!throughput) { fprintf (stdout, "Last applied: my = %lld, group = %lld\n", (long long)my_seqno, (long long)group_seqno); // } } return ret; } static inline long test_before_send (gcs_test_thread_t* thread) { #ifdef USE_WAIT static const struct timespec wait = { 0, 10000000 }; #endif long ret = 0; /* create a message */ thread->act.size = test_make_msg (thread->msg, msg_len); thread->act.buf = thread->msg; if (thread->act.size <= 0) return -1; if (!throughput) { /* log message before replication */ ret = test_send_log_create (thread); ret = test_log_msg (send_log, thread->log_msg); } #ifdef USE_WAIT while ((ret = gcs_wait(gcs)) && ret > 0) nanosleep (&wait, NULL); #endif return ret; } static inline long test_after_recv (gcs_test_thread_t* thread) { long ret; if (!throughput) { /* log message after replication */ ret = test_recv_log_create (thread); ret = test_log_in_to (to, thread->act.seqno_l, thread->log_msg); } else if (total) { ret = test_log_in_to (to, thread->act.seqno_l, NULL); } else { gu_to_self_cancel (to, thread->act.seqno_l); } ret = test_send_last_applied (gcs, thread->act.seqno_g); // fprintf (stdout, "SEQNO applied %lld", thread->local_act_id); if (thread->act.type == GCS_ACT_TORDERED) gcache_free (gcache, thread->act.buf); return ret; } void *gcs_test_repl (void *arg) { gcs_test_thread_t *thread = arg; // long i = thread->n_tries; long ret = 0; pthread_mutex_lock (&gcs_test_lock); pthread_mutex_unlock (&gcs_test_lock); while (thread->n_tries) { ret = test_before_send (thread); if (ret < 0) break; /* replicate message */ ret = gcs_repl (gcs, &thread->act, false); if (ret < 0) { assert (thread->act.seqno_g == GCS_SEQNO_ILL); assert (thread->act.seqno_l == GCS_SEQNO_ILL); break; } msg_repld++; size_repld += thread->act.size; // usleep ((rand() & 1) << 1); test_after_recv (thread); // puts (thread->log_msg); fflush (stdout); } // fprintf (stderr, "REPL thread %ld exiting: %s\n", // thread->id, strerror(-ret)); return NULL; } void *gcs_test_send (void *arg) { long ret = 0; gcs_test_thread_t *thread = arg; // long i = thread->n_tries; pthread_mutex_lock (&gcs_test_lock); pthread_mutex_unlock (&gcs_test_lock); while (thread->n_tries) { ret = test_before_send (thread); if (ret < 0) break; /* send message to group */ ret = gcs_send (gcs, thread->act.buf, thread->act.size, GCS_ACT_TORDERED, false); if (ret < 0) break; //sleep (1); msg_sent++; size_sent += thread->act.size; } // fprintf (stderr, "SEND thread %ld exiting: %s\n", // thread->id, strerror(-ret)); return NULL; } static void gcs_test_handle_configuration (gcs_conn_t* gcs, gcs_test_thread_t* thread) { long ret; static gcs_seqno_t conf_id = 0; gcs_act_conf_t* conf = (void*)thread->msg; fprintf (stdout, "Got GCS_ACT_CONF: Conf: %lld, " "seqno: %lld, members: %ld, my idx: %ld, local seqno: %lld\n", (long long)conf->conf_id, (long long)conf->seqno, conf->memb_num, conf->my_idx, (long long)thread->act.seqno_l); fflush (stdout); // NOTE: what really needs to be checked is seqno and group_uuid, but here // we don't keep track of them (and don't do real transfers), // so for simplicity, just check conf_id. while (-EAGAIN == (ret = gu_to_grab (to, thread->act.seqno_l))); if (0 == ret) { if (conf->my_state == GCS_NODE_STATE_PRIM) { gcs_seqno_t seqno, s; fprintf (stdout,"Gap in configurations: ours: %lld, group: %lld.\n", (long long)conf_id, (long long)conf->conf_id); fflush (stdout); fprintf (stdout, "Requesting state transfer up to %lld: %s\n", (long long)conf->seqno, // this is global seqno strerror (-gcs_request_state_transfer (gcs, &conf->seqno, sizeof(conf->seqno), "", &seqno))); // pretend that state transfer is complete, cancel every action up // to seqno for (s = thread->act.seqno_l + 1; s <= seqno; s++) { gu_to_self_cancel (to, s); // this is local seqno } fprintf (stdout, "Sending JOIN: %s\n", strerror(-gcs_join(gcs, 0))); fflush (stdout); } gcs_resume_recv (gcs); gu_to_release (to, thread->act.seqno_l); } else { fprintf (stderr, "Failed to grab TO: %ld (%s)", ret, strerror(ret)); } conf_id = conf->conf_id; } void *gcs_test_recv (void *arg) { long ret = 0; gcs_test_thread_t *thread = arg; while (thread->n_tries) { /* receive message from group */ while ((ret = gcs_recv (gcs, &thread->act)) == -ECANCELED) { usleep (10000); } if (ret <= 0) { fprintf (stderr, "gcs_recv() %s: %ld (%s). Thread exits.\n", ret < 0 ? "failed" : "connection closed", ret, strerror(-ret)); assert (thread->act.buf == NULL); assert (thread->act.size == 0); assert (thread->act.seqno_g == GCS_SEQNO_ILL); assert (thread->act.seqno_l == GCS_SEQNO_ILL); assert (thread->act.type == GCS_ACT_ERROR); break; } assert (thread->act.type < GCS_ACT_ERROR); msg_recvd++; size_recvd += thread->act.size; switch (thread->act.type) { case GCS_ACT_TORDERED: test_after_recv (thread); //puts (thread->log_msg); fflush (stdout); break; case GCS_ACT_COMMIT_CUT: group_seqno = *(gcs_seqno_t*)thread->act.buf; gu_to_self_cancel (to, thread->act.seqno_l); break; case GCS_ACT_CONF: gcs_test_handle_configuration (gcs, thread); break; case GCS_ACT_STATE_REQ: fprintf (stdout, "Got STATE_REQ\n"); gu_to_grab (to, thread->act.seqno_l); fprintf (stdout, "Sending JOIN: %s\n", strerror(-gcs_join(gcs, 0))); fflush (stdout); gu_to_release (to, thread->act.seqno_l); break; case GCS_ACT_JOIN: fprintf (stdout, "Joined\n"); gu_to_self_cancel (to, thread->act.seqno_l); break; case GCS_ACT_SYNC: fprintf (stdout, "Synced\n"); gu_to_self_cancel (to, thread->act.seqno_l); break; default: fprintf (stderr, "Unexpected action type: %d\n", thread->act.type); } } // fprintf (stderr, "RECV thread %ld exiting: %s\n", // thread->id, strerror(-ret)); return NULL; } static long gcs_test_thread_pool_start (gcs_test_thread_pool_t *pool) { long i; long err = 0; void * (* thread_routine) (void *); switch (pool->type) { case GCS_TEST_REPL: thread_routine = gcs_test_repl; break; case GCS_TEST_SEND: thread_routine = gcs_test_send; break; case GCS_TEST_RECV: thread_routine = gcs_test_recv; break; default: fprintf (stderr, "Bad repl type %u\n", pool->type); return -1; } for (i = 0; i < pool->n_threads; i++) { if ((err = pthread_create (&pool->threads[i].thread, NULL, thread_routine, &pool->threads[i]))) break; } pool->n_started = i; printf ("Started %ld threads of %s type (pool: %p)\n", pool->n_started, GCS_TEST_REPL == pool->type ? "REPL" : (GCS_TEST_SEND == pool->type ? "SEND" :"RECV"), (void*)pool); return 0; } static long gcs_test_thread_pool_join (const gcs_test_thread_pool_t *pool) { long i; for (i = 0; i < pool->n_started; i++) { pthread_join (pool->threads[i].thread, NULL); } return 0; } static long gcs_test_thread_pool_stop (const gcs_test_thread_pool_t *pool) { long i; for (i = 0; i < pool->n_started; i++) { pool->threads[i].n_tries = 0; } return 0; } long gcs_test_thread_pool_cancel (const gcs_test_thread_pool_t *pool) { long i; printf ("Canceling pool: %p\n", (void*)pool); fflush(stdout); printf ("pool type: %u, pool threads: %ld\n", pool->type, pool->n_started); fflush(stdout); for (i = 0; i < pool->n_started; i++) { printf ("Cancelling %ld\n", i); fflush(stdout); pthread_cancel (pool->threads[i].thread); pool->threads[i].n_tries = 0; } return 0; } typedef struct gcs_test_conf { long n_tries; long n_repl; long n_send; long n_recv; const char* backend; } gcs_test_conf_t; static const char* DEFAULT_BACKEND = "dummy://"; static long gcs_test_conf (gcs_test_conf_t *conf, long argc, char *argv[]) { char *endptr; /* defaults */ conf->n_tries = 10; conf->n_repl = 10; conf->n_send = 0; conf->n_recv = 1; conf->backend = DEFAULT_BACKEND; switch (argc) { case 6: conf->n_recv = strtol (argv[5], &endptr, 10); if ('\0' != *endptr) goto error; case 5: conf->n_send = strtol (argv[4], &endptr, 10); if ('\0' != *endptr) goto error; case 4: conf->n_repl = strtol (argv[3], &endptr, 10); if ('\0' != *endptr) goto error; case 3: conf->n_tries = strtol (argv[2], &endptr, 10); if ('\0' != *endptr) goto error; case 2: conf->backend = argv[1]; break; default: break; } printf ("Config: n_tries = %ld, n_repl = %ld, n_send = %ld, n_recv = %ld, " "backend = %s\n", conf->n_tries, conf->n_repl, conf->n_send, conf->n_recv, conf->backend); return 0; error: printf ("Usage: %s [backend] [tries:%ld] [repl threads:%ld] " "[send threads: %ld] [recv threads: %ld]\n", argv[0], conf->n_tries, conf->n_repl, conf->n_send, conf->n_recv); exit (EXIT_SUCCESS); } static inline void test_print_stat (long msgs, size_t size, double interval) { printf ("%7ld (%7.1f per sec.) / %7zuKb (%7.1f Kb/s)\n", msgs, (double)msgs/interval, size >> 10, (double)(size >> 10)/interval); } int main (int argc, char *argv[]) { long err = 0; gcs_test_conf_t conf; gcs_test_thread_pool_t repl_pool, send_pool, recv_pool; char *channel = "my_channel"; struct timeval t_begin, t_end; gcs_conf_debug_on(); // turn on debug messages if ((err = gcs_test_conf (&conf, argc, argv))) goto out; if (!throughput) { if ((err = test_log_open (&send_log, SEND_LOG))) goto out; if ((err = test_log_open (&recv_log, RECV_LOG))) goto out; } to = gu_to_create ((conf.n_repl + conf.n_recv + 1)*2, GCS_SEQNO_FIRST); if (!to) goto out; // total_tries = conf.n_tries * (conf.n_repl + conf.n_send); printf ("Opening connection: channel = %s, backend = %s\n", channel, conf.backend); gu_config_t* gconf = gu_config_create ("gcache.size=0; " "gcache.page_size=1M"); if (!gconf) goto out; if (!(gcache = gcache_create (gconf, ""))) goto out; if (!(gcs = gcs_create (gconf, gcache, NULL, NULL, 0, 0))) goto out; puts ("debug"); fflush(stdout); /* the following hack won't work if there is 0.0.0.0 in URL options */ bool bstrap = (NULL != strstr(conf.backend, "0.0.0.0")); if ((err = gcs_open (gcs, channel, conf.backend, bstrap))) goto out; printf ("Connected\n"); msg_len = 1300; if (msg_len > MAX_MSG_LEN) msg_len = MAX_MSG_LEN; gcs_conf_set_pkt_size (gcs, 7570); // to test fragmentation if ((err = gcs_test_thread_pool_create (&repl_pool, GCS_TEST_REPL, conf.n_repl, conf.n_tries))) goto out; if ((err = gcs_test_thread_pool_create (&send_pool, GCS_TEST_SEND, conf.n_send, conf.n_tries))) goto out; if ((err = gcs_test_thread_pool_create (&recv_pool, GCS_TEST_RECV, conf.n_recv, conf.n_tries))) goto out; pthread_mutex_lock (&gcs_test_lock); gcs_test_thread_pool_start (&recv_pool); gcs_test_thread_pool_start (&repl_pool); gcs_test_thread_pool_start (&send_pool); printf ("Press any key to start the load:"); fgetc (stdin); puts ("Started load."); gettimeofday (&t_begin, NULL); printf ("Waiting for %ld seconds\n", conf.n_tries); fflush (stdout); pthread_mutex_unlock (&gcs_test_lock); usleep (conf.n_tries*1000000); puts ("Stopping SEND and REPL threads..."); fflush(stdout); fflush(stderr); gcs_test_thread_pool_stop (&send_pool); gcs_test_thread_pool_stop (&repl_pool); puts ("Threads stopped."); gcs_test_thread_pool_join (&send_pool); gcs_test_thread_pool_join (&repl_pool); puts ("SEND and REPL threads joined."); printf ("Closing GCS connection... "); if ((err = gcs_close (gcs))) goto out; puts ("done."); gcs_test_thread_pool_join (&recv_pool); puts ("RECV threads joined."); gettimeofday (&t_end, NULL); { double interval = (t_end.tv_sec - t_begin.tv_sec) + 0.000001*t_end.tv_usec - 0.000001*t_begin.tv_usec; printf ("Actions sent: "); test_print_stat (msg_sent, size_sent, interval); printf ("Actions received: "); test_print_stat (msg_recvd, size_recvd, interval); printf ("Actions replicated: "); test_print_stat (msg_repld, size_repld, interval); puts("---------------------------------------------------------------"); printf ("Total throughput: "); test_print_stat (msg_repld + msg_recvd, size_repld + size_recvd, interval); printf ("Overhead at 10000 actions/sec: %5.2f%%\n", 1000000.0 * interval / (msg_repld + msg_recvd)); puts(""); } printf ("Press any key to exit the program:\n"); fgetc (stdin); printf ("Freeing GCS connection handle..."); if ((err = gcs_destroy (gcs))) goto out; gcs = NULL; printf ("done\n"); fflush (stdout); printf ("Destroying GCache object:\n"); gcache_destroy (gcache); gcs_test_thread_pool_destroy (&repl_pool); gcs_test_thread_pool_destroy (&send_pool); gcs_test_thread_pool_destroy (&recv_pool); gu_to_destroy(&to); if (!throughput) { printf ("Closing send log\n"); test_log_close (&send_log); printf ("Closing recv log\n"); test_log_close (&recv_log); } { ssize_t total; ssize_t allocs; ssize_t reallocs; ssize_t deallocs; void gu_mem_stats (ssize_t*, ssize_t*, ssize_t*, ssize_t*); gu_mem_stats (&total, &allocs, &reallocs, &deallocs); printf ("Memory statistics:\n" "Memory still allocated: %10lld\n" "Times allocated: %10lld\n" "Times reallocated: %10lld\n" "Times freed: %10lld\n", (long long)total, (long long)allocs, (long long)reallocs, (long long)deallocs); } return 0; out: printf ("Error: %ld (%s)\n", err, strerror (-err)); return err; } percona-xtradb-cluster-galera/gcs/src/gcs_test.h0000644000000000000000000001055012247075736022202 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_test.h 2745 2012-03-17 00:00:23Z alex $ */ #ifndef _gcs_test_h_ #define _gcs_test_h_ // some data to test bugger packets static char gcs_test_data[] = "001 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "002 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "003 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "004 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "005 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "006 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "007 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "008 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "009 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "010 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "011 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "012 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "013 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "014 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "015 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "016 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "017 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "018 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "019 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "020 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "021 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "022 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "023 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "024 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "025 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "026 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "027 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "028 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "029 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "030 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "031 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "032 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "033 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "034 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "035 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "036 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "037 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "038 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "039 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "040 456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "041 4567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234" ; #endif percona-xtradb-cluster-galera/gcs/src/gcs_test.sh0000755000000000000000000000113212247075736022364 0ustar rootroot00000000000000#!/bin/sh # # This script checks the output of the gcs_test program # to verify that all actions that were sent were received # intact # # $Id: gcs_test.sh 261 2008-04-03 11:08:37Z alex $ SEND_LOG="gcs_test_send.log" RECV_LOG="gcs_test_recv.log" echo "Sent action count: $(wc -l $SEND_LOG)" echo "Received action count: $(wc -l $RECV_LOG)" SEND_MD5=$(cat "$SEND_LOG" | awk '{ print $4 " " $5 }'| sort -n -k 2 | tee sort_send | md5sum) echo "send_log md5: $SEND_MD5" RECV_MD5=$(cat "$RECV_LOG" | awk '{ print $4 " " $5 }'| sort -n -k 2 | tee sort_recv | md5sum) echo "recv_log md5: $RECV_MD5" # percona-xtradb-cluster-galera/gcs/src/unit_tests/0000755000000000000000000000000012247075736022416 5ustar rootroot00000000000000percona-xtradb-cluster-galera/gcs/src/unit_tests/SConscript0000644000000000000000000000366412247075736024441 0ustar rootroot00000000000000 Import('check_env') env = check_env.Clone() gcs_tests_sources = Split(''' gcs_tests.c gcs_fifo_test.c ../gcs_fifo_lite.c gcs_sm_test.c ../gcs_sm.c gcs_comp_test.c ../gcs_comp_msg.c gcs_state_msg_test.c ../gcs_state_msg.c gcs_backend_test.c ../gcs_backend.c gcs_proto_test.c ../gcs_act_proto.c gcs_defrag_test.c ../gcs_defrag.c gcs_node_test.c ../gcs_node.c gcs_group_test.c gcs_memb_test.c ../gcs_group.c gcs_core_test.c ../gcs_core.c ../gcs_dummy.c ../gcs_msg_type.c ../gcs.c ../gcs_params.c gcs_fc_test.c ../gcs_fc.c ''') env.Append(CPPFLAGS = ' -DGCS_USE_GCOMM -DGCS_CORE_TESTING -DGCS_DUMMY_TESTING') env.Append(LIBS = File('#/gcache/src/libgcache.a')) env.Append(LIBS = File('#/galerautils/src/libgalerautils++.a')) env.Append(LIBS = File('#/galerautils/src/libgalerautils.a')) env.Append(LIBS = ['m']) gcs_tests = env.Program(target = 'gcs_tests', source = gcs_tests_sources, OBJPREFIX = 'gcs-tests-', LINK = env['CXX']) env.Test("gcs_tests.passed", gcs_tests) env.Alias("test", "gcs_tests.passed") Clean(gcs_tests, '#/gcs_tests.log') percona-xtradb-cluster-galera/gcs/src/unit_tests/gcs_backend_test.c0000644000000000000000000000461212247075736026047 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_backend_test.c 1865 2010-08-16 21:09:39Z alex $ */ #include #include #include #include #include #include #include "../gcs_backend.h" #include "gcs_backend_test.h" // Fake backend definitons. Must be global for gcs_backend.c to see GCS_BACKEND_NAME_FN(gcs_test_name) { return "DUMMIEEEE!"; } GCS_BACKEND_CREATE_FN(gcs_test_create) { backend->name = gcs_test_name; return 0; } GCS_BACKEND_NAME_FN(gcs_spread_name) { return "SPREAT"; } GCS_BACKEND_CREATE_FN(gcs_spread_create) { backend->name = gcs_spread_name; return 0; } GCS_BACKEND_NAME_FN(gcs_vs_name) { return "vsssssssss"; } GCS_BACKEND_CREATE_FN(gcs_vs_create) { backend->name = gcs_vs_name; return 0; } GCS_BACKEND_NAME_FN(gcs_gcomm_name) { return "gCOMMMMM!!!"; } GCS_BACKEND_CREATE_FN(gcs_gcomm_create) { backend->name = gcs_gcomm_name; return 0; } START_TEST (gcs_backend_test) { gcs_backend_t backend; long ret; gu_config_t* config = gu_config_create (""); fail_if (config == NULL); ret = gcs_backend_init (&backend, "wrong://kkk", config); fail_if (ret != -ESOCKTNOSUPPORT); ret = gcs_backend_init (&backend, "spread:", config); fail_if (ret != -EINVAL); ret = gcs_backend_init (&backend, "dummy://", config); fail_if (ret != 0, "ret = %d (%s)", ret, strerror(-ret)); // fail_if (backend.name != gcs_test_name); this test is broken since we can // no longer use global gcs_dummy_create() symbol because linking with real // gcs_dummy.o ret = gcs_backend_init (&backend, "gcomm://0.0.0.0:4567", config); fail_if (ret != 0, "ret = %d (%s)", ret, strerror(-ret)); fail_if (backend.name != gcs_gcomm_name); // ret = gcs_backend_init (&backend, "vsbes://kkk"); // fail_if (ret != 0, "ret = %d (%s)", ret, strerror(-ret)); // fail_if (backend.name != gcs_vs_name); // ret = gcs_backend_init (&backend, "spread://"); // fail_if (ret != 0, "ret = %d (%s)", ret, strerror(-ret)); // fail_if (backend.name != gcs_spread_name); } END_TEST Suite *gcs_backend_suite(void) { Suite *suite = suite_create("GCS backend interface"); TCase *tcase = tcase_create("gcs_backend"); suite_add_tcase (suite, tcase); tcase_add_test (tcase, gcs_backend_test); return suite; } percona-xtradb-cluster-galera/gcs/src/unit_tests/gcs_backend_test.h0000644000000000000000000000040212247075736026045 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_backend_test.h 261 2008-04-03 11:08:37Z alex $ */ #ifndef __gcs_backend_test__ #define __gcs_backend_test__ extern Suite *gcs_backend_suite(void); #endif /* __gu_backend_test__ */ percona-xtradb-cluster-galera/gcs/src/unit_tests/gcs_comp_test.c0000644000000000000000000001042712247075736025417 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_comp_test.c 2734 2012-03-08 10:47:53Z teemu $ */ #include #include #include #include #include #include #define GCS_COMP_MSG_ACCESS #include "../gcs_comp_msg.h" #include "gcs_comp_test.h" static const char* members[] = { "0", "88888888", "1", "7777777", "22", "666666", "333", "55555", "4444" }; static char long_id[] = "just make it longer when the test starts to fail because of increased limit"; static void check_msg_identity (const gcs_comp_msg_t* m, const gcs_comp_msg_t* n) { long i; fail_if (n->primary != m->primary); fail_if (n->my_idx != m->my_idx); fail_if (n->memb_num != m->memb_num); for (i = 0; i < m->memb_num; i++) { fail_if (strlen(n->memb[i].id) != strlen(m->memb[i].id), "member %d id len does not match: %d vs %d", i, strlen(n->memb[i].id), strlen(m->memb[i].id)); fail_if (strncmp (n->memb[i].id, m->memb[i].id, GCS_COMP_MEMB_ID_MAX_LEN), "member %d IDs don't not match: got '%s', should be '%s'", i, members[i], m->memb[i].id); } } START_TEST (gcs_comp_test) { long memb_num = sizeof(members)/sizeof(char*); long my_idx = getpid() % memb_num; long prim = my_idx % 2; gcs_comp_msg_t* m = gcs_comp_msg_new (prim, false, my_idx, memb_num); gcs_comp_msg_t* n = NULL; size_t buf_len = gcs_comp_msg_size (m); char buf[buf_len]; long i, j; long ret; fail_if (NULL == m); fail_if (memb_num != gcs_comp_msg_num (m)); fail_if (my_idx != gcs_comp_msg_self (m)); // add members except for the last for (i = 0; i < memb_num - 1; i++) { ret = gcs_comp_msg_add (m, members[i]); fail_if (ret != i, "gcs_comp_msg_add() returned %d, expected %d", ret, i); } // try to add a id that was added already if (my_idx < i) { j = my_idx; } else { j = i - 1; } ret = gcs_comp_msg_add (m, members[j]); fail_if (ret != -ENOTUNIQ, "gcs_comp_msg_add() returned %d, expected " "-ENOTUNIQ (%d)", ret, -ENOTUNIQ); // try to add empty id ret = gcs_comp_msg_add (m, ""); fail_if (ret != -EINVAL, "gcs_comp_msg_add() returned %d, expected " "-EINVAL (%d)", ret, -EINVAL); // try to add id that is too long ret = gcs_comp_msg_add (m, long_id); fail_if (ret != -ENAMETOOLONG, "gcs_comp_msg_add() returned %d, expected " "-ENAMETOOLONG (%d)", ret, -ENAMETOOLONG); // add final id ret = gcs_comp_msg_add (m, members[i]); fail_if (ret != i, "gcs_comp_msg_add() returned %d, expected %d", ret, i); // check that all added correctly for (i = 0; i < memb_num; i++) { const char* const id = gcs_comp_msg_id (m, i); fail_if (strcmp (members[i], id), "Memeber %ld (%s) recorded as %s", i, members[i], id); } // check that memcpy preserves the message // (it can be treated just as a byte array) memcpy (buf, m, buf_len); n = (gcs_comp_msg_t*) buf; check_msg_identity (m, n); gcs_comp_msg_delete (m); mark_point(); // check that gcs_comp_msg_copy() works m = gcs_comp_msg_copy (n); fail_if (NULL == m); check_msg_identity (m, n); gcs_comp_msg_delete (m); // test gcs_comp_msg_id() fail_unless (NULL == gcs_comp_msg_id (n, -1)); for (i = 0; i < memb_num; i++) { const char* id = gcs_comp_msg_id (n, i); fail_if (NULL == id); fail_if (strcmp(members[i], id)); } fail_unless (NULL == gcs_comp_msg_id (n, i)); // test gcs_comp_msg_idx() fail_if (-1 != gcs_comp_msg_idx (n, "")); fail_if (-1 != gcs_comp_msg_idx (n, long_id)); for (i = 0; i < memb_num; i++) fail_if (i != gcs_comp_msg_idx (n, members[i])); // test gcs_comp_msg_primary() fail_if (n->primary != gcs_comp_msg_primary(n)); } END_TEST Suite *gcs_comp_suite(void) { Suite *suite = suite_create("GCS component message"); TCase *tcase = tcase_create("gcs_comp"); suite_add_tcase (suite, tcase); tcase_add_test (tcase, gcs_comp_test); return suite; } percona-xtradb-cluster-galera/gcs/src/unit_tests/gcs_comp_test.h0000644000000000000000000000036312247075736025422 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_comp_test.h 261 2008-04-03 11:08:37Z alex $ */ #ifndef __gcs_comp_test__ #define __gcs_comp_test__ extern Suite *gcs_comp_suite(void); #endif /* __gu_comp_test__ */ percona-xtradb-cluster-galera/gcs/src/unit_tests/gcs_core_test.c0000644000000000000000000005467212247075736025423 0ustar rootroot00000000000000/* * Copyright (C) 2008-2012 Codership Oy * * $Id: gcs_core_test.c 3336 2013-10-28 07:41:56Z teemu $ */ /* * @file * * Defines unit tests for gcs_core (and as a result tests gcs_group and * a dummy backend which gcs_core depends on) * * Most of the checks require independent sending and receiving threads. * Approach 1 is to start separate threads for both sending and receiving * and use the current thread of execution to sychronize between them: * * CORE_RECV_START(act_r) * CORE_SEND_START(act_s) * while (gcs_core_send_step(Core)) { // step through action fragments * (do something) * }; * CORE_SEND_END(act_s, ret) // check return code * CORE_RECV_END(act_r, size, type) // makes checks against size and type * * A simplified approach 2 is: * * CORE_SEND_START(act_s) * while (gcs_core_send_step(Core)) { // step through action fragments * (do something) * }; * CORE_SEND_END(act_s, ret) // check return code * CORE_RECV_ACT(act_r, size, type) // makes checks agains size and type * * In the first approach group messages will be received concurrently. * In the second apporach messages will wait in queue and be fetched afterwards * */ #include #include #include #include #include "../gcs_core.h" #include "../gcs_dummy.h" #include "../gcs_seqno.h" #include "gcs_core_test.h" extern ssize_t gcs_tests_get_allocated(); static const long UNKNOWN_SIZE = 1234567890; // some unrealistic number static gcs_core_t* Core = NULL; static gcs_backend_t* Backend = NULL; static gcs_seqno_t Seqno = 0; typedef struct action { const void* data; const void* repl_buf; ssize_t size; gcs_act_type_t type; gcs_seqno_t seqno; gu_thread_t thread; } action_t; //static struct action_t RecvAct; static const ssize_t FRAG_SIZE = 4; // desirable action fragment size static const char act1[] = "101"; // 1-fragment action static const char act2[] = "202122"; // 2-fragment action static const char act3[] = "3031323334"; // 3-fragment action /* * Huge macros which follow below cannot be functions for the purpose * of correct line reporting. */ // action receive thread, returns after first action received, stores action // in the passed action_t object, uses global Core to receive static void* core_recv_thread (void* arg) { action_t* act = arg; // @todo: refactor according to new gcs_act types struct gcs_act_rcvd recv_act; act->size = gcs_core_recv (Core, &recv_act, GU_TIME_ETERNITY); act->data = recv_act.act.buf; act->repl_buf = recv_act.repl_buf; act->type = recv_act.act.type; act->seqno = recv_act.id; return (NULL); } // this macro logs errors from within a function #define FAIL_IF(expr, format, ...) \ if (expr) { \ gu_fatal ("FAIL: "format, ## __VA_ARGS__, NULL); \ fail_if (true, format, ## __VA_ARGS__, NULL); \ return true; \ } // Start a thread to receive an action // args: action_t object static inline bool CORE_RECV_START(action_t* act) { return (0 != gu_thread_create (&act->thread, NULL, core_recv_thread, act)); } static bool COMMON_RECV_CHECKS(action_t* act, const void* buf, ssize_t size, gcs_act_type_t type, gcs_seqno_t* seqno) { FAIL_IF (size != UNKNOWN_SIZE && size != act->size, "gcs_core_recv(): expected %lld, returned %zd (%s)", (long long) size, act->size, strerror (-act->size)); FAIL_IF (act->type != type, "type does not match: expected %d, got %d", type, act->type); FAIL_IF (act->size > 0 && act->data == NULL, "null buffer with positive size: %zu", act->size); // action is ordered only if it is of type GCS_ACT_TORDERED and not an error if (act->seqno >= GCS_SEQNO_NIL) { FAIL_IF (GCS_ACT_TORDERED != act->type, "GCS_ACT_TORDERED != act->type (%d), while act->seqno: %lld", act->type, (long long)act->seqno); FAIL_IF ((*seqno + 1) != act->seqno, "expected seqno %lld, got %lld", (long long)(*seqno + 1), (long long)act->seqno); *seqno = *seqno + 1; } if (NULL != buf) { if (GCS_ACT_TORDERED == act->type) { // local action buffer should not be copied FAIL_IF (act->repl_buf != buf, "Received buffer ptr is not the same as sent", NULL); } else { FAIL_IF (act->repl_buf == buf, "Received the same buffer ptr as sent", NULL); FAIL_IF (memcmp (buf, act->data, act->size), "Received buffer contents is not the same as sent", NULL); } } return false; } // Wait for recv thread to complete, perform required checks // args: action_t, expected size, expected type static bool CORE_RECV_END(action_t* act, const void* buf, ssize_t size, gcs_act_type_t type) { { int ret = gu_thread_join (act->thread, NULL); act->thread = (pthread_t)-1; FAIL_IF(0 != ret, "Failed to join recv thread: %ld (%s)", ret, strerror (ret)); } return COMMON_RECV_CHECKS (act, buf, size, type, &Seqno); } // Receive action in one call, perform required checks // args: pointer to action_t, expected size, expected type static bool CORE_RECV_ACT (action_t* act, const void* buf, ssize_t size, gcs_act_type_t type) { struct gcs_act_rcvd recv_act; act->size = gcs_core_recv (Core, &recv_act, GU_TIME_ETERNITY); act->data = recv_act.act.buf; act->repl_buf = recv_act.repl_buf; act->type = recv_act.act.type; act->seqno = recv_act.id; return COMMON_RECV_CHECKS (act, buf, size, type, &Seqno); } // Sending always needs to be done via separate thread (uses lock-stepping) void* core_send_thread (void* arg) { action_t* act = arg; // use seqno field to pass the return code, it is signed 8-byte integer act->seqno = gcs_core_send (Core, act->data, act->size, act->type); return (NULL); } // Start a thread to send an action // args: action_t object static bool CORE_SEND_START(action_t* act) { return (0 != gu_thread_create (&act->thread, NULL, core_send_thread, act)); } // Wait for send thread to complete, perform required checks // args: action_t, expected return code static bool CORE_SEND_END(action_t* act, long ret) { { long _ret = gu_thread_join (act->thread, NULL); act->thread = (pthread_t)-1; FAIL_IF (0 != _ret, "Failed to join recv thread: %ld (%s)", _ret, strerror (_ret)); } FAIL_IF (ret != act->seqno, "gcs_core_send(): expected %lld, returned %lld (%s)", (long long) ret, (long long) act->seqno, strerror (-act->seqno)); return false; } // check if configuration is the one that we expected static long core_test_check_conf (const gcs_act_conf_t* conf, bool prim, long my_idx, long memb_num) { long ret = 0; if ((conf->conf_id >= 0) != prim) { gu_error ("Expected %s conf, received %s", prim ? "PRIMARY" : "NON-PRIMARY", (conf->conf_id >= 0) ? "PRIMARY" : "NON-PRIMARY"); ret = -1; } if (conf->my_idx != my_idx) { gu_error ("Expected my_idx = %ld, got %ld", my_idx, conf->my_idx); ret = -1; } if (conf->my_idx != my_idx) { gu_error ("Expected my_idx = %ld, got %ld", my_idx, conf->my_idx); ret = -1; } return ret; } static long core_test_set_payload_size (ssize_t s) { long ret; const ssize_t arbitrary_pkt_size = s + 64; // big enough for payload to fit ret = gcs_core_set_pkt_size (Core, arbitrary_pkt_size); if (ret <= 0) { gu_error("set_pkt_size(%zd) returned: %ld (%s)", arbitrary_pkt_size, ret, strerror (-ret)); return ret; } ret = gcs_core_set_pkt_size (Core, arbitrary_pkt_size - ret + s); if (ret != s) { gu_error("set_pkt_size() returned: %ld instead of %zd", ret, s); return ret; } return 0; } // Initialises core and backend objects + some common tests static inline void core_test_init () { long ret; action_t act; mark_point(); gu_config_t* config = gu_config_create (""); fail_if (config == NULL); Core = gcs_core_create (config, NULL, "core_test", "aaa.bbb.ccc.ddd:xxxx", 0, 0); fail_if (NULL == Core); Backend = gcs_core_get_backend (Core); fail_if (NULL == Backend); Seqno = 0; // reset seqno ret = core_test_set_payload_size (FRAG_SIZE); fail_if (-EBADFD != ret, "Expected -EBADFD, got: %ld (%s)", ret, strerror(-ret)); ret = gcs_core_open (Core, "yadda-yadda", "owkmevc", 1); fail_if (-EINVAL != ret, "Expected -EINVAL, got %ld (%s)", ret, strerror(-ret)); ret = gcs_core_open (Core, "yadda-yadda", "dummy://", 1); fail_if (0 != ret, "Failed to open core connection: %ld (%s)", ret, strerror(-ret)); // receive first configuration message fail_if (CORE_RECV_ACT (&act, NULL, UNKNOWN_SIZE, GCS_ACT_CONF)); fail_if (core_test_check_conf(act.data, true, 0, 1)); free ((void*)act.data); // this will configure backend to have desired fragment size ret = core_test_set_payload_size (FRAG_SIZE); fail_if (0 != ret, "Failed to set up the message payload size: %ld (%s)", ret, strerror(-ret)); // try to send an action to check that everything's alright ret = gcs_core_send (Core, act1, sizeof(act1), GCS_ACT_TORDERED); fail_if (ret != sizeof(act1), "Expected %d, got %d (%s)", sizeof(act1), ret, strerror (-ret)); gu_warn ("Next CORE_RECV_ACT fails under valgrind"); fail_if (CORE_RECV_ACT (&act, act1, sizeof(act1), GCS_ACT_TORDERED)); ret = gcs_core_send_join (Core, Seqno); fail_if (ret != 0, "gcs_core_send_join(): %ld (%s)", ret, strerror(-ret)); // no action to be received (we're joined already) ret = gcs_core_send_sync (Core, Seqno); fail_if (ret != 0, "gcs_core_send_sync(): %ld (%s)", ret, strerror(-ret)); fail_if (CORE_RECV_ACT(&act,NULL,sizeof(gcs_seqno_t),GCS_ACT_SYNC)); fail_if (Seqno != gcs_seqno_gtoh(*(gcs_seqno_t*)act.data)); gcs_core_send_lock_step (Core, true); mark_point(); } // cleans up core and backend objects static inline void core_test_cleanup () { long ret; char tmp[1]; action_t act; fail_if (NULL == Core); fail_if (NULL == Backend); // to fetch self-leave message fail_if (CORE_RECV_START (&act)); ret = gcs_core_close (Core); fail_if (0 != ret, "Failed to close core: %ld (%s)", ret, strerror (-ret)); ret = CORE_RECV_END (&act, NULL, UNKNOWN_SIZE, GCS_ACT_CONF); fail_if (ret, "ret: %ld (%s)", ret, strerror(-ret)); free ((void*)act.data); // check that backend is closed too ret = Backend->send (Backend, tmp, sizeof(tmp), GCS_MSG_ACTION); fail_if (ret != -EBADFD); ret = gcs_core_destroy (Core); fail_if (0 != ret, "Failed to destroy core: %ld (%s)", ret, strerror (-ret)); { ssize_t allocated; allocated = gcs_tests_get_allocated(); fail_if (0 != allocated, "Expected 0 allocated bytes, found %zd", allocated); } } // just a smoke test for core API START_TEST (gcs_core_test_api) { #define ACT act3 long ret; long tout = 100; // 100 ms timeout size_t act_size = sizeof(ACT); action_t act_s = { ACT, NULL, act_size, GCS_ACT_TORDERED, -1, (pthread_t)-1 }; action_t act_r; long i = 5; core_test_init (); fail_if (NULL == Core); fail_if (NULL == Backend); // test basic fragmentaiton while (i--) { long frags = (act_size - 1)/FRAG_SIZE + 1; gu_info ("Iteration %ld: act: %s, size: %zu, frags: %ld", i, ACT, act_size, frags); fail_if (CORE_SEND_START (&act_s)); while ((ret = gcs_core_send_step (Core, 3*tout)) > 0) { frags--; gu_info ("frags: %ld", frags); // usleep (1000); } fail_if (ret != 0, "gcs_core_send_step() returned: %ld (%s)", ret, strerror(-ret)); fail_if (frags != 0, "frags = %ld, instead of 0", frags); fail_if (CORE_SEND_END (&act_s, act_size)); fail_if (CORE_RECV_ACT (&act_r, ACT, act_size, GCS_ACT_TORDERED)); ret = gcs_core_set_last_applied (Core, Seqno); fail_if (ret != 0, "gcs_core_set_last_applied(): %ld (%s)", ret, strerror(-ret)); fail_if (CORE_RECV_ACT (&act_r, NULL, sizeof(gcs_seqno_t), GCS_ACT_COMMIT_CUT)); fail_if (Seqno != gcs_seqno_gtoh(*(gcs_seqno_t*)act_r.data)); free ((void*)act_r.data); } // send fake flow control action, its contents is not important gcs_core_send_fc (Core, ACT, act_size); fail_if (ret != 0, "gcs_core_send_fc(): %ld (%s)", ret, strerror(-ret)); fail_if (CORE_RECV_ACT(&act_r, ACT, act_size, GCS_ACT_FLOW)); core_test_cleanup (); } END_TEST // do a single send step, compare with the expected result static inline bool CORE_SEND_STEP (gcs_core_t* core, long timeout, long ret) { long err = gcs_core_send_step (core, timeout); FAIL_IF (err < 0, "gcs_core_send_step(): %ld (%s)", err, strerror (-err)); if (ret >= 0) { FAIL_IF (err != ret, "gcs_core_send_step(): expected %ld, got %ld", ret, err); } return false; } static bool DUMMY_INJECT_COMPONENT (gcs_backend_t* backend, const gcs_comp_msg_t* comp) { long ret = gcs_dummy_inject_msg (Backend, comp, gcs_comp_msg_size(comp), GCS_MSG_COMPONENT, GCS_SENDER_NONE); FAIL_IF (ret <= 0, "gcs_dummy_inject_msg(): %ld (%s)", ret, strerror(ret)); return false; } static bool DUMMY_INSTALL_COMPONENT (gcs_backend_t* backend, const gcs_comp_msg_t* comp) { bool primary = gcs_comp_msg_primary (comp); long my_idx = gcs_comp_msg_self (comp); long members = gcs_comp_msg_num (comp); action_t act; FAIL_IF (gcs_dummy_set_component(Backend, comp), "", NULL); FAIL_IF (DUMMY_INJECT_COMPONENT (Backend, comp), "", NULL); FAIL_IF (CORE_RECV_ACT (&act, NULL, UNKNOWN_SIZE, GCS_ACT_CONF), "", NULL); FAIL_IF (core_test_check_conf(act.data, primary, my_idx, members),"",NULL); free ((void*)act.data); return false; } START_TEST (gcs_core_test_own) { #undef ACT #define ACT act2 long tout = 1000; // 100 ms timeout size_t act_size = sizeof(ACT); action_t act_s = { ACT, NULL, act_size, GCS_ACT_TORDERED, -1, (pthread_t)-1 }; action_t act_r = { NULL, NULL, -1, -1, -1, (pthread_t)-1 }; // Create primary and non-primary component messages gcs_comp_msg_t* prim = gcs_comp_msg_new (true, false, 0, 1); gcs_comp_msg_t* non_prim = gcs_comp_msg_new (false, false, 0, 1); fail_if (NULL == prim); fail_if (NULL == non_prim); gcs_comp_msg_add (prim, "node1"); gcs_comp_msg_add (non_prim, "node1"); core_test_init (); ///////////////////////////////////////////// /// check behaviour in transitional state /// ///////////////////////////////////////////// fail_if (CORE_RECV_START (&act_r)); fail_if (CORE_SEND_START (&act_s)); fail_if (CORE_SEND_STEP (Core, tout, 1)); // 1st frag usleep (10000); // resolve race between sending and setting transitional gcs_dummy_set_transitional (Backend); fail_if (CORE_SEND_STEP (Core, tout, 1)); // 2nd frag fail_if (CORE_SEND_STEP (Core, tout, 0)); // no frags left fail_if (NULL != act_r.data); // should not have received anything fail_if (gcs_dummy_set_component (Backend, prim)); // return to PRIM state fail_if (CORE_SEND_END (&act_s, act_size)); fail_if (CORE_RECV_END (&act_r, ACT, act_size, GCS_ACT_TORDERED)); /* * TEST CASE 1: Action was sent successfully, but NON_PRIM component * happened before any fragment could be delivered. * EXPECTED OUTCOME: action is received with -ENOTCONN instead of global * seqno */ fail_if (DUMMY_INJECT_COMPONENT (Backend, non_prim)); fail_if (CORE_SEND_START (&act_s)); fail_if (CORE_SEND_STEP (Core, tout, 1)); // 1st frag fail_if (CORE_SEND_STEP (Core, tout, 1)); // 2nd frag fail_if (CORE_SEND_END (&act_s, act_size)); fail_if (gcs_dummy_set_component(Backend, non_prim)); fail_if (CORE_RECV_ACT (&act_r, NULL, UNKNOWN_SIZE, GCS_ACT_CONF)); fail_if (core_test_check_conf(act_r.data, false, 0, 1)); free ((void*)act_r.data); fail_if (CORE_RECV_ACT (&act_r, ACT, act_size, GCS_ACT_TORDERED)); fail_if (-ENOTCONN != act_r.seqno, "Expected -ENOTCONN, received %ld (%s)", act_r.seqno, strerror (-act_r.seqno)); /* * TEST CASE 2: core in NON_PRIM state. There is attempt to send an * action. * EXPECTED OUTCOME: CORE_SEND_END should return -ENOTCONN after 1st * fragment send fails. */ fail_if (CORE_SEND_START (&act_s)); fail_if (CORE_SEND_STEP (Core, tout, 1)); // 1st frag fail_if (CORE_SEND_STEP (Core, tout, 0)); // bail out after 1st frag fail_if (CORE_SEND_END (&act_s, -ENOTCONN)); /* * TEST CASE 3: Backend in NON_PRIM state. There is attempt to send an * action. * EXPECTED OUTCOME: CORE_SEND_END should return -ENOTCONN after 1st * fragment send fails. */ fail_if (DUMMY_INSTALL_COMPONENT (Backend, prim)); fail_if (gcs_dummy_set_component(Backend, non_prim)); fail_if (DUMMY_INJECT_COMPONENT (Backend, non_prim)); fail_if (CORE_SEND_START (&act_s)); fail_if (CORE_SEND_STEP (Core, tout, 1)); // 1st frag fail_if (CORE_SEND_END (&act_s, -ENOTCONN)); fail_if (CORE_RECV_ACT (&act_r, NULL, UNKNOWN_SIZE, GCS_ACT_CONF)); fail_if (core_test_check_conf(act_r.data, false, 0, 1)); free ((void*)act_r.data); /* * TEST CASE 4: Action was sent successfully, but NON_PRIM component * happened in between delivered fragments. * EXPECTED OUTCOME: action is received with -ENOTCONN instead of global * seqno. */ fail_if (DUMMY_INSTALL_COMPONENT (Backend, prim)); fail_if (CORE_SEND_START (&act_s)); fail_if (CORE_SEND_STEP (Core, tout, 1)); // 1st frag fail_if (DUMMY_INJECT_COMPONENT (Backend, non_prim)); fail_if (CORE_SEND_STEP (Core, tout, 1)); // 2nd frag fail_if (CORE_SEND_END (&act_s, act_size)); fail_if (CORE_RECV_ACT (&act_r, NULL, UNKNOWN_SIZE, GCS_ACT_CONF)); fail_if (core_test_check_conf(act_r.data, false, 0, 1)); free ((void*)act_r.data); fail_if (CORE_RECV_ACT (&act_r, ACT, act_size, GCS_ACT_TORDERED)); fail_if (-ENOTCONN != act_r.seqno, "Expected -ENOTCONN, received %ld (%s)", act_r.seqno, strerror (-act_r.seqno)); /* * TEST CASE 5: Action is being sent and received concurrently. In between * two fragments recv thread receives NON_PRIM and then PRIM components. * EXPECTED OUTCOME: CORE_RECV_ACT should receive the action with -ERESTART * instead of seqno. */ fail_if (DUMMY_INSTALL_COMPONENT (Backend, prim)); fail_if (CORE_SEND_START (&act_s)); fail_if (CORE_SEND_STEP (Core, tout, 1)); // 1st frag usleep (100000); // make sure 1st fragment gets in before new component fail_if (DUMMY_INSTALL_COMPONENT (Backend, non_prim)); fail_if (DUMMY_INSTALL_COMPONENT (Backend, prim)); fail_if (CORE_SEND_STEP (Core, tout, 1)); // 2nd frag fail_if (CORE_SEND_END (&act_s, act_size)); fail_if (CORE_RECV_ACT (&act_r, ACT, act_size, GCS_ACT_TORDERED)); fail_if (-ERESTART != act_r.seqno, "Expected -ERESTART, received %ld (%s)", act_r.seqno, strerror (-act_r.seqno)); /* * TEST CASE 6: Action has 3 fragments, 2 were sent successfully but the * 3rd failed because backend is in NON_PRIM. In addition NON_PRIM component * happened in between delivered fragments. * subcase 1: new component received first * subcase 2: 3rd fragment is sent first * EXPECTED OUTCOME: CORE_SEND_END should return -ENOTCONN after 3rd * fragment send fails. */ #undef ACT #define ACT act3 act_size = sizeof(ACT); act_s.data = ACT; act_s.size = act_size; // subcase 1 fail_if (DUMMY_INSTALL_COMPONENT (Backend, prim)); fail_if (CORE_SEND_START (&act_s)); fail_if (CORE_SEND_STEP (Core, tout, 1)); // 1st frag fail_if (DUMMY_INJECT_COMPONENT (Backend, non_prim)); fail_if (CORE_SEND_STEP (Core, tout, 1)); // 2nd frag usleep (500000); // fail_if_seq fail_if (gcs_dummy_set_component(Backend, non_prim)); fail_if (CORE_RECV_ACT (&act_r, NULL, UNKNOWN_SIZE, GCS_ACT_CONF)); fail_if (core_test_check_conf(act_r.data, false, 0, 1)); free ((void*)act_r.data); fail_if (CORE_SEND_STEP (Core, tout, 1)); // 3rd frag fail_if (CORE_SEND_END (&act_s, -ENOTCONN)); // subcase 2 fail_if (DUMMY_INSTALL_COMPONENT (Backend, prim)); fail_if (CORE_SEND_START (&act_s)); fail_if (CORE_SEND_STEP (Core, tout, 1)); // 1st frag fail_if (DUMMY_INJECT_COMPONENT (Backend, non_prim)); fail_if (CORE_SEND_STEP (Core, tout, 1)); // 2nd frag usleep (1000000); fail_if (gcs_dummy_set_component(Backend, non_prim)); fail_if (CORE_SEND_STEP (Core, 4*tout, 1)); // 3rd frag fail_if (CORE_RECV_ACT (&act_r, NULL, UNKNOWN_SIZE, GCS_ACT_CONF)); fail_if (core_test_check_conf(act_r.data, false, 0, 1)); free ((void*)act_r.data); fail_if (CORE_SEND_END (&act_s, -ENOTCONN)); gu_free (prim); gu_free (non_prim); core_test_cleanup (); } END_TEST #if 0 // requires multinode support from gcs_dummy START_TEST (gcs_core_test_foreign) { core_test_init (); core_test_cleanup (); } END_TEST #endif // 0 Suite *gcs_core_suite(void) { Suite *suite = suite_create("GCS core context"); TCase *tcase = tcase_create("gcs_core"); suite_add_tcase (suite, tcase); tcase_add_test (tcase, gcs_core_test_api); tcase_add_test (tcase, gcs_core_test_own); // tcase_add_test (tcase, gcs_core_test_foreign); tcase_set_timeout(tcase, 60); return suite; } percona-xtradb-cluster-galera/gcs/src/unit_tests/gcs_core_test.h0000644000000000000000000000036512247075736025416 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_core_test.h 3336 2013-10-28 07:41:56Z teemu $ */ #ifndef __gcs_core_test__ #define __gcs_core_test__ extern Suite *gcs_core_suite(void); #endif /* __gu_core_test__ */ percona-xtradb-cluster-galera/gcs/src/unit_tests/gcs_defrag_test.c0000644000000000000000000001153412247075736025711 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_defrag_test.c 2279 2011-08-06 14:09:43Z alex $ */ #include #include #include #include #include "gcs_defrag_test.h" #include "../gcs_defrag.h" #define TRUE (0 == 0) #define FALSE (!TRUE) static void defrag_check_init (gcs_defrag_t* defrag) { fail_if (defrag->sent_id != GCS_SEQNO_ILL); fail_if (defrag->head != NULL); fail_if (defrag->tail != NULL); fail_if (defrag->size != 0); fail_if (defrag->received != 0); fail_if (defrag->frag_no != 0); } START_TEST (gcs_defrag_test) { ssize_t ret; // The Action const char act_buf[] = "Test action smuction"; size_t act_len = sizeof (act_buf); // lengths of three fragments of the action size_t frag1_len = act_len / 3; size_t frag2_len = frag1_len; size_t frag3_len = act_len - frag1_len - frag2_len; // pointer to the three fragments of the action const char* frag1 = act_buf; const char* frag2 = frag1 + frag1_len; const char* frag3 = frag2 + frag2_len; // recv fragments gcs_act_frag_t frg1, frg2, frg3, frg4; gcs_defrag_t defrag; struct gcs_act recv_act; void* tail; mark_point(); #ifndef NDEBUG // debug build breaks this test due to asserts return; #endif // Initialize message parameters frg1.act_id = getpid(); frg1.act_size = act_len; frg1.frag = frag1; frg1.frag_len = frag1_len; frg1.frag_no = 0; frg1.act_type = GCS_ACT_TORDERED; frg1.proto_ver = 0; // normal fragments frg2 = frg3 = frg1; frg2.frag = frag2; frg2.frag_len = frag2_len; frg2.frag_no = frg1.frag_no + 1; frg3.frag = frag3; frg3.frag_len = frag3_len; frg3.frag_no = frg2.frag_no + 1; // bad fragmets to be tried instead of frg2 frg4 = frg2; frg4.frag = "junk"; frg4.frag_len = strlen("junk"); frg4.act_id = frg2.act_id + 1; // wrong action id mark_point(); // ready for the first fragment gcs_defrag_init (&defrag, NULL); defrag_check_init (&defrag); mark_point(); // 1. Try fragment that is not the first ret = gcs_defrag_handle_frag (&defrag, &frg3, &recv_act, FALSE); fail_if (ret != -EPROTO); mark_point(); defrag_check_init (&defrag); // should be no changes // 2. Try first fragment ret = gcs_defrag_handle_frag (&defrag, &frg1, &recv_act, FALSE); fail_if (ret != 0); fail_if (defrag.head == NULL); fail_if (defrag.received != frag1_len); fail_if (defrag.tail != defrag.head + defrag.received); tail = defrag.tail; #define TRY_WRONG_2ND_FRAGMENT(frag) \ ret = gcs_defrag_handle_frag (&defrag, frag, &recv_act, FALSE); \ fail_if (ret != -EPROTO); \ fail_if (defrag.received != frag1_len); \ fail_if (defrag.tail != tail); // 3. Try first fragment again TRY_WRONG_2ND_FRAGMENT(&frg1); // 4. Try third fragment TRY_WRONG_2ND_FRAGMENT(&frg3); // 5. Try fouth fragment TRY_WRONG_2ND_FRAGMENT(&frg4); // 6. Try second fragment ret = gcs_defrag_handle_frag (&defrag, &frg2, &recv_act, FALSE); fail_if (ret != 0); fail_if (defrag.received != frag1_len + frag2_len); fail_if (defrag.tail != defrag.head + defrag.received); // 7. Try third fragment, last one ret = gcs_defrag_handle_frag (&defrag, &frg3, &recv_act, FALSE); fail_if (ret != (long)act_len); // 8. Check the action fail_if (recv_act.buf_len != (long)act_len); fail_if (strncmp(recv_act.buf, act_buf, act_len), "Action received: '%s', expected '%s'",recv_act.buf,act_buf); defrag_check_init (&defrag); // should be empty // memleak in recv_act.buf ! // 9. Try the same with local action ret = gcs_defrag_handle_frag (&defrag, &frg1, &recv_act, TRUE); fail_if (ret != 0); // fail_if (defrag.head != NULL); (and now we may allocate it for cache) ret = gcs_defrag_handle_frag (&defrag, &frg2, &recv_act, TRUE); fail_if (ret != 0); // fail_if (defrag.head != NULL); (and now we may allocate it for cache) ret = gcs_defrag_handle_frag (&defrag, &frg3, &recv_act, TRUE); fail_if (ret != (long)act_len); // fail_if (defrag.head != NULL); (and now we may allocate it for cache) // 10. Check the action fail_if (recv_act.buf_len != (long)act_len); // fail_if (recv_act.buf != NULL); (and now we may allocate it for cache) defrag_check_init (&defrag); // should be empty // memleack in recv_act.buf ! } END_TEST Suite *gcs_defrag_suite(void) { Suite *suite = suite_create("GCS defragmenter"); TCase *tcase = tcase_create("gcs_defrag"); suite_add_tcase (suite, tcase); tcase_add_test (tcase, gcs_defrag_test); return suite; } percona-xtradb-cluster-galera/gcs/src/unit_tests/gcs_defrag_test.h0000644000000000000000000000037512247075736025717 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_defrag_test.h 274 2008-04-04 18:54:47Z alex $ */ #ifndef __gcs_defrag_test__ #define __gcs_defrag_test__ extern Suite *gcs_defrag_suite(void); #endif /* __gu_defrag_test__ */ percona-xtradb-cluster-galera/gcs/src/unit_tests/gcs_fc_test.c0000644000000000000000000001007712247075736025052 0ustar rootroot00000000000000// Copyright (C) 2010 Codership Oy // $Id$ #include "gcs_fc_test.h" #include "../gcs_fc.h" #include #include START_TEST(gcs_fc_test_limits) { gcs_fc_t fc; int ret; ret = gcs_fc_init (&fc, 16, 0.5, 0.1); fail_if (ret != 0); ret = gcs_fc_init (&fc, -1, 0.5, 0.1); fail_if (ret != -EINVAL); ret = gcs_fc_init (&fc, 16, 1.0, 0.1); fail_if (ret != -EINVAL); ret = gcs_fc_init (&fc, 16, 0.5, 1.0); fail_if (ret != -EINVAL); } END_TEST /* This is a macro to preserve line numbers in fail_if() output */ #define SKIP_N_ACTIONS(fc_,n_) \ { \ int i; \ for (i = 0; i < n_; ++i) \ { \ long long ret = gcs_fc_process (fc_, 0); \ fail_if (ret != 0, "0-sized action #%d returned %d (%s)", \ i, ret, strerror(-ret)); \ } \ } START_TEST(gcs_fc_test_basic) { gcs_fc_t fc; int ret; long long pause; ret = gcs_fc_init (&fc, 16, 0.5, 0.1); fail_if (ret != 0); gcs_fc_reset (&fc, 8); usleep (1000); SKIP_N_ACTIONS(&fc, 7); /* Here we exceed soft limit almost instantly, which should give a very high * data rate and as a result a need to sleep */ pause = gcs_fc_process (&fc, 7); fail_if(pause <= 0, "Soft limit trip returned %lld (%s)", pause, strerror(-pause)); gcs_fc_reset (&fc, 7); usleep (1000); SKIP_N_ACTIONS(&fc, 7); /* Here we reach soft limit almost instantly, which should give a very high * data rate, but soft limit is not exceeded, so no sleep yet. */ pause = gcs_fc_process (&fc, 1); fail_if(pause != 0, "Soft limit touch returned %lld (%s)", pause, strerror(-pause)); SKIP_N_ACTIONS(&fc, 7); usleep (1000); pause = gcs_fc_process (&fc, 7); fail_if(pause <= 0, "Soft limit trip returned %lld (%s)", pause, strerror(-pause)); /* hard limit excess should be detected instantly */ pause = gcs_fc_process (&fc, 1); fail_if(pause != -ENOMEM, "Hard limit trip returned %lld (%s)", pause, strerror(-pause)); } END_TEST static inline bool double_equals (double a, double b) { static double const eps = 0.001; double diff = (a - b) / (a + b); // roughly relative difference return !(diff > eps || diff < -eps); } START_TEST(gcs_fc_test_precise) { gcs_fc_t fc; long long ret; struct timespec p10ms = { .tv_sec = 0, .tv_nsec = 10000000 }; // 10 ms ret = gcs_fc_init (&fc, 2000, 0.5, 0.5); fail_if (ret != 0); gcs_fc_reset (&fc, 500); SKIP_N_ACTIONS(&fc, 7); nanosleep (&p10ms, NULL); ret = gcs_fc_process (&fc, 1000); fail_if(ret <= 0, "Soft limit trip returned %d (%s)", ret, strerror(-ret)); // measured data rate should be ~100000 b/s // slave queue length should be half-way between soft limit and hard limit // desired rate should be half between 1.0 and 0.5 of full rate -> 75000 b/s // excess over soft limit is 500 and corresponding interval: 5ms // (500/5ms == 100000 b/s) // additional sleep must be 1.6667 ms (500/(5 + 1.6667) ~ 75000 b/s) double const correction = 100000.0/fc.max_rate; // due to imprecise sleep double const expected_sleep = 0.001666667*correction; double sleep = ((double)ret)*1.0e-9; fail_if(!double_equals(sleep, expected_sleep), "Sleep: %f, expected %f", sleep, expected_sleep); } END_TEST Suite *gcs_fc_suite(void) { Suite *s = suite_create("GCS state transfer FC"); TCase *tc = tcase_create("gcs_fc"); suite_add_tcase (s, tc); tcase_add_test (tc, gcs_fc_test_limits); tcase_add_test (tc, gcs_fc_test_basic); tcase_add_test (tc, gcs_fc_test_precise); return s; } percona-xtradb-cluster-galera/gcs/src/unit_tests/gcs_fc_test.h0000644000000000000000000000030012247075736025043 0ustar rootroot00000000000000// Copyright (C) 2010 Codership Oy // $Id$ #ifndef __gcs_fc_test__ #define __gcs_fc_test__ #include Suite *gcs_fc_suite(void); #endif /* __gcs_fc_test__ */ percona-xtradb-cluster-galera/gcs/src/unit_tests/gcs_fifo_test.c0000644000000000000000000000574512247075736025413 0ustar rootroot00000000000000// Copyright (C) 2007 Codership Oy // $Id: gcs_fifo_test.c 2272 2011-07-28 23:24:41Z alex $ #include #include "gcs_fifo_test.h" #include "../gcs_fifo_lite.h" #define FIFO_LENGTH 10 START_TEST (gcs_fifo_lite_test) { gcs_fifo_lite_t* fifo; long ret; long i; long* item; fifo = gcs_fifo_lite_create (0, 1); fail_if (fifo != NULL); fifo = gcs_fifo_lite_create (1, 0); fail_if (fifo != NULL); fifo = gcs_fifo_lite_create (1, 1); fail_if (fifo == NULL); ret = gcs_fifo_lite_destroy (fifo); fail_if (ret != 0, "gcs_fifo_lite_destroy() returned %d", ret); fifo = gcs_fifo_lite_create (FIFO_LENGTH, sizeof(i)); fail_if (fifo == NULL); fail_if (fifo->used != 0, "fifo->used is %z for an empty FIFO", fifo->used); gcs_fifo_lite_open (fifo); // fill FIFO for (i = 1; i <= FIFO_LENGTH; i++) { item = gcs_fifo_lite_get_tail (fifo); fail_if (NULL == item, "gcs_fifo_lite_get_tail() returned NULL"); *item = i; gcs_fifo_lite_push_tail (fifo); } fail_if (fifo->used != FIFO_LENGTH, "fifo->used is %zu, expected %zu", fifo->used, FIFO_LENGTH); // test remove for (i = 1; i <= FIFO_LENGTH; i++) { ret = gcs_fifo_lite_remove (fifo); fail_if (0 == ret, "gcs_fifo_lite_remove() failed, i = %ld", i); } fail_if (fifo->used != 0, "fifo->used is %zu, expected %zu", fifo->used, 0); // try remove on empty queue ret = gcs_fifo_lite_remove (fifo); fail_if (0 != ret, "gcs_fifo_lite_remove() from empty FIFO returned true"); // it should be possible to fill FIFO again for (i = 1; i <= FIFO_LENGTH; i++) { item = gcs_fifo_lite_get_tail (fifo); fail_if (NULL == item, "gcs_fifo_lite_get_tail() returned NULL"); *item = i; gcs_fifo_lite_push_tail (fifo); } fail_if (fifo->used != FIFO_LENGTH, "fifo->used is %zu, expected %zu", fifo->used, FIFO_LENGTH); // test get for (i = 1; i <= FIFO_LENGTH; i++) { item = gcs_fifo_lite_get_head (fifo); fail_if (NULL == item, "gcs_fifo_lite_get_head() returned NULL"); fail_if (*item != i, "gcs_fifo_lite_get_head() returned %ld, " "expected %ld", *item, i); gcs_fifo_lite_release (fifo); item = gcs_fifo_lite_get_head (fifo); fail_if (NULL == item, "gcs_fifo_lite_get_head() returned NULL"); fail_if (*item != i, "gcs_fifo_lite_get_head() returned %ld, " "expected %ld", *item, i); gcs_fifo_lite_pop_head (fifo); } fail_if (fifo->used != 0, "fifo->used for empty queue is %ld", fifo->used); ret = gcs_fifo_lite_destroy (fifo); fail_if (ret != 0, "gcs_fifo_lite_destroy() failed: %d", ret); } END_TEST Suite *gcs_fifo_suite(void) { Suite *s = suite_create("GCS FIFO functions"); TCase *tc = tcase_create("gcs_fifo"); suite_add_tcase (s, tc); tcase_add_test (tc, gcs_fifo_lite_test); return s; } percona-xtradb-cluster-galera/gcs/src/unit_tests/gcs_fifo_test.h0000644000000000000000000000034412247075736025406 0ustar rootroot00000000000000// Copyright (C) 2007 Codership Oy // $Id: gcs_fifo_test.h 394 2008-08-21 20:06:58Z alex $ #ifndef __gcs_fifo_test__ #define __gcs_fifo_test__ Suite *gcs_fifo_suite(void); #endif /* __gcs_fifo_test__ */ percona-xtradb-cluster-galera/gcs/src/unit_tests/gcs_group_test.c0000644000000000000000000004213212247075736025613 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_group_test.c 2832 2012-06-25 20:06:26Z alex $ */ #include #include #include #include #include #include "gcs_group_test.h" #include "../gcs_group.h" #include "../gcs_act_proto.h" #include "../gcs_comp_msg.h" #define TRUE (0 == 0) #define FALSE (!TRUE) /* * header will be written to buf from frg, act_len of payload will be copied * from act, msg structure will be filled in */ static void msg_write (gcs_recv_msg_t* msg, gcs_act_frag_t* frg, char* buf, size_t buf_len, const char* data, size_t data_len, long sender_idx, gcs_msg_type_t type) { long ret; ret = gcs_act_proto_write (frg, buf, buf_len); fail_if (ret, "error code: %d", ret); fail_if (frg->frag == NULL); fail_if (frg->frag_len < data_len, "Resulting frag_len %lu is less than required act_len %lu\n" "Refactor the test and increase buf_len.", frg->frag_len,data_len); memcpy ((void*)frg->frag, data, data_len); msg->buf = buf; msg->buf_len = buf_len; msg->size = (buf_len - frg->frag_len + data_len); msg->sender_idx = sender_idx; msg->type = type; } static long new_component (gcs_group_t* group, const gcs_comp_msg_t* comp) { long ret = gcs_group_handle_comp_msg (group, comp); // modelling real state exchange is really tedious here, just fake it // group->state = GCS_GROUP_PRIMARY; return ret; } // just pretend we received SYNC message //#define RECEIVE_SYNC() group.new_memb = FALSE; #define RECEIVE_SYNC() #define LOCALHOST "localhost" #define REMOTEHOST "remotehost" #define DISTANTHOST "distanthost" // This tests tests configuration changes START_TEST (gcs_group_configuration) { ssize_t ret; gcs_group_t group; gcs_seqno_t seqno = 1; // The Action const char act_buf[] = "Test action smuction"; ssize_t act_len = sizeof (act_buf); // lengths of three fragments of the action long frag1_len = act_len / 3; long frag2_len = frag1_len; long frag3_len = act_len - frag1_len - frag2_len; // pointer to the three fragments of the action const char* frag1 = act_buf; const char* frag2 = frag1 + frag1_len; const char* frag3 = frag2 + frag2_len; // message buffers const long buf_len = 64; char buf1[buf_len], buf2[buf_len], buf3[buf_len], buf4[buf_len], buf5[buf_len]; // recv message structures gcs_recv_msg_t msg1, msg2, msg3, msg4, msg5; gcs_act_frag_t frg1, frg2, frg3, frg4, frg5, frg; struct gcs_act_rcvd r_act; struct gcs_act* act = &r_act.act; gcs_comp_msg_t* comp; mark_point(); #ifndef NDEBUG // debug build breaks the test due to asserts return; #endif // Initialize message parameters frg1.act_id = getpid(); frg1.act_size = act_len; frg1.frag = NULL; frg1.frag_len = 0; frg1.frag_no = 0; frg1.act_type = GCS_ACT_TORDERED; frg1.proto_ver = 0; // normal fragments frg2 = frg3 = frg1; frg2.frag_no = frg1.frag_no + 1; frg3.frag_no = frg2.frag_no + 1; // bad fragmets to be tried instead of frg2 frg4 = frg5 = frg2; frg4.act_id = frg2.act_id + 1; // wrong action id frg5.act_type = GCS_ACT_SERVICE; // wrong action type mark_point(); msg_write (&msg1, &frg1, buf1, buf_len, frag1, frag1_len, 0,GCS_MSG_ACTION); msg_write (&msg2, &frg2, buf2, buf_len, frag2, frag2_len, 0,GCS_MSG_ACTION); msg_write (&msg3, &frg3, buf3, buf_len, frag3, frag3_len, 0,GCS_MSG_ACTION); msg_write (&msg4, &frg4, buf4, buf_len, "4444", 4, 0, GCS_MSG_ACTION); msg_write (&msg5, &frg5, buf5, buf_len, "55555", 5, 0, GCS_MSG_ACTION); mark_point(); // ready gcs_group_init (&group, NULL, "my node", "my addr", 0, 0, 0); fail_if (gcs_group_is_primary(&group)); fail_if (group.num != 1); // Prepare first primary component message containing only one node comp = gcs_comp_msg_new (TRUE, false, 0, 1); fail_if (comp == NULL); fail_if (gcs_comp_msg_add (comp, LOCALHOST)); ret = new_component (&group, comp); fail_if (ret < 0); // fail_if (!gcs_group_is_primary(&group)); // fail_if (!gcs_group_new_members(&group)); RECEIVE_SYNC(); #define TRY_MESSAGE(msg) \ ret = gcs_act_proto_read (&frg, (msg).buf, (msg).size); \ ret = gcs_group_handle_act_msg (&group, &frg, &(msg), &r_act); // 1. Try fragment that is not the first memset (&r_act, 0, sizeof(r_act)); // ret = gcs_group_handle_act_msg (&group, &frg, &msg3, &r_act); TRY_MESSAGE(msg3); fail_if (ret != -EPROTO); fail_if (act->buf != NULL); fail_if (act->buf_len != 0); mark_point(); // 2. Try first fragment // ret = gcs_group_handle_act_msg (&group, &msg1, &r_act); TRY_MESSAGE(msg1); fail_if (ret != 0); fail_if (act->buf != NULL); fail_if (act->buf_len != 0); #define TRY_WRONG_2ND_FRAGMENT(frag) \ /*ret = gcs_group_handle_act_msg (&group, &frag, &r_act);*/ \ TRY_MESSAGE(frag); \ fail_if (ret != -EPROTO); \ fail_if (act->buf_len != 0); // 3. Try first fragment again gu_debug (""); TRY_WRONG_2ND_FRAGMENT(msg1); gu_debug (""); // 4. Try third fragment TRY_WRONG_2ND_FRAGMENT(msg3); // 5. Try fouth fragment TRY_WRONG_2ND_FRAGMENT(msg4); // 6. Try fifth fragment TRY_WRONG_2ND_FRAGMENT(msg5); // 7. Try correct second fragment // ret = gcs_group_handle_act_msg (&group, &msg2, &r_act); TRY_MESSAGE(msg2); fail_if (ret != 0); fail_if (act->buf != NULL); act->buf = (void*)0x12354; // shall be NULLed fail_if (act->buf_len != 0); // 8. Try third fragment, last one // ret = gcs_group_handle_act_msg (&group, &msg3, &r_act); TRY_MESSAGE(msg3); fail_if (ret != act_len); fail_if (r_act.sender_idx != 0); fail_if (act->buf != NULL); // local action, must be fetched from local fifo fail_if (act->buf_len != act_len); fail_if (r_act.id != seqno, "Expected seqno %llu, found %llu", seqno, r_act.id); seqno++; // cleanup memset (&r_act, 0, sizeof(r_act)); // 10. New component message gcs_comp_msg_delete (comp); comp = gcs_comp_msg_new (TRUE, false, 1, 2); fail_if (comp == NULL); fail_if (gcs_comp_msg_add (comp, REMOTEHOST) < 0); fail_if (gcs_comp_msg_add (comp, LOCALHOST) < 0); ret = new_component (&group, comp); fail_if (ret < 0); // fail_if (!gcs_group_is_primary(&group)); // fail_if (!gcs_group_new_members(&group)); RECEIVE_SYNC(); // 11. Try the same with foreign action (now my index is 1, sender is 0) // ret = gcs_group_handle_act_msg (&group, &msg1, &r_act); TRY_MESSAGE(msg1); fail_if (ret != 0); fail_if (act->buf_len != 0); fail_if (act->buf != NULL); // ret = gcs_group_handle_act_msg (&group, &msg2, &r_act); TRY_MESSAGE(msg2); fail_if (ret != 0); fail_if (act->buf_len != 0); fail_if (act->buf != NULL); // ret = gcs_group_handle_act_msg (&group, &msg3, &r_act); TRY_MESSAGE(msg3); fail_if (ret != act_len, "Expected ret = %zd, got %zd", act_len, ret); fail_if (act->buf_len != act_len); fail_if (act->buf == NULL); fail_if (strncmp(act->buf, act_buf, act_len), "Action received: '%s', expected '%s'", act_buf); fail_if (r_act.sender_idx != 0); fail_if (act->type != GCS_ACT_TORDERED); fail_if (r_act.id != seqno, "Expected seqno %llu, found %llu", seqno, r_act.id); seqno++; // cleanup free ((void*)act->buf); memset (&r_act, 0, sizeof(r_act)); // 12. Try foreign action with a new node joined in the middle. gcs_comp_msg_delete (comp); comp = gcs_comp_msg_new (TRUE, false, 1, 3); fail_if (comp == NULL); fail_if (gcs_comp_msg_add (comp, REMOTEHOST) < 0); fail_if (gcs_comp_msg_add (comp, LOCALHOST) < 0); fail_if (gcs_comp_msg_add (comp, DISTANTHOST) < 0); // ret = gcs_group_handle_act_msg (&group, &msg1, &r_act); TRY_MESSAGE(msg1); fail_if (ret != 0); fail_if (act->buf_len != 0); fail_if (act->buf != NULL); ret = new_component (&group, comp); fail_if (ret < 0); // fail_if (!gcs_group_is_primary(&group)); // fail_if (!gcs_group_new_members(&group)); RECEIVE_SYNC(); // now I must be able to resend the action from scratch // ret = gcs_group_handle_act_msg (&group, &msg1, &r_act); TRY_MESSAGE(msg1); fail_if (ret != 0); fail_if (act->buf_len != 0); fail_if (act->buf != NULL); // ret = gcs_group_handle_act_msg (&group, &msg2, &r_act); TRY_MESSAGE(msg2); fail_if (ret != 0); fail_if (act->buf_len != 0); fail_if (act->buf != NULL); // ret = gcs_group_handle_act_msg (&group, &msg3, &r_act); TRY_MESSAGE(msg3); fail_if (ret != act_len); fail_if (act->buf_len != act_len); fail_if (act->buf == NULL); fail_if (strncmp(act->buf, act_buf, act_len), "Action received: '%s', expected '%s'", act_buf); fail_if (r_act.sender_idx != 0); fail_if (act->type != GCS_ACT_TORDERED); fail_if (r_act.id != seqno, "Expected seqno %llu, found %llu", seqno, r_act.id); seqno++; // cleanup free ((void*)act->buf); memset (&r_act, 0, sizeof(r_act)); // 13. Try to send an action with one node disappearing in the middle // and order of nodes changed // 13.1 Each node sends a message // ret = gcs_group_handle_act_msg (&group, &msg1, &r_act); TRY_MESSAGE(msg1); fail_if (ret != 0); fail_if (act->buf_len != 0); fail_if (act->buf != NULL); msg_write (&msg1, &frg1, buf1, buf_len, frag1, frag1_len, 1,GCS_MSG_ACTION); // ret = gcs_group_handle_act_msg (&group, &msg1, &r_act); TRY_MESSAGE(msg1); fail_if (ret != 0); fail_if (act->buf_len != 0); fail_if (act->buf != NULL); msg_write (&msg1, &frg1, buf1, buf_len, frag1, frag1_len, 2,GCS_MSG_ACTION); // ret = gcs_group_handle_act_msg (&group, &msg1, &r_act); TRY_MESSAGE(msg1); fail_if (ret != 0); fail_if (act->buf_len != 0); fail_if (act->buf != NULL); // 13.2 configuration changes, one node disappears // (REMOTEHOST, LOCALHOST, DISTANTHOST) -> (LOCALHOST, REMOTEHOST) gcs_comp_msg_delete (comp); comp = gcs_comp_msg_new (TRUE, false, 0, 2); fail_if (comp == NULL); fail_if (gcs_comp_msg_add (comp, LOCALHOST) < 0); fail_if (gcs_comp_msg_add (comp, REMOTEHOST) < 0); ret = new_component (&group, comp); fail_if (ret < 0); // fail_if (!gcs_group_is_primary(&group)); // fail_if (gcs_group_new_members(&group), "Nodes: %d: node0 - '%s', " // "node1 - '%s'", group.num, // group.nodes[0].id, group.nodes[1].id); RECEIVE_SYNC(); gcs_comp_msg_delete (comp); return; // 13.3 now I just continue sending messages // ret = gcs_group_handle_act_msg (&group, &msg2, &r_act); // local TRY_MESSAGE(msg2); fail_if (ret != 0, "%d (%s)", ret, strerror(-ret)); fail_if (act->buf_len != 0); fail_if (act->buf != NULL); msg_write (&msg2, &frg2, buf2, buf_len, frag2, frag2_len, 1,GCS_MSG_ACTION); // ret = gcs_group_handle_act_msg (&group, &msg2, &r_act); // foreign TRY_MESSAGE(msg2); fail_if (ret != 0); fail_if (act->buf_len != 0); fail_if (act->buf != NULL); act->buf = (void*)0x11111; // shall be NULLed below when local act is recvd // ret = gcs_group_handle_act_msg (&group, &msg3, &r_act); // local TRY_MESSAGE(msg3); fail_if (ret != act_len); fail_if (act->buf_len != act_len); fail_if (act->buf != NULL); fail_if (r_act.sender_idx != 0); fail_if (act->type != GCS_ACT_TORDERED); fail_if (r_act.id != seqno, "Expected seqno %llu, found %llu", seqno, r_act.id); seqno++; msg_write (&msg3, &frg3, buf3, buf_len, frag3, frag3_len, 1,GCS_MSG_ACTION); // ret = gcs_group_handle_act_msg (&group, &msg3, &r_act); // foreign TRY_MESSAGE(msg3); fail_if (ret != act_len); fail_if (act->buf_len != act_len); fail_if (act->buf == NULL); fail_if (strncmp(act->buf, act_buf, act_len), "Action received: '%s', expected '%s'", act_buf); fail_if (r_act.sender_idx != 1); fail_if (act->type != GCS_ACT_TORDERED); fail_if (r_act.id != seqno, "Expected seqno %llu, found %llu", seqno, r_act.id); seqno++; // cleanup free ((void*)act->buf); memset (&r_act, 0, sizeof(r_act)); // Leave group comp = gcs_comp_msg_new (FALSE, false, -1, 0); fail_if (comp == NULL); ret = new_component (&group, comp); fail_if (ret < 0); // fail_if (gcs_group_is_primary(&group)); // comment until implemented: fail_if (!gcs_group_new_members(&group)); RECEIVE_SYNC(); } END_TEST static inline void group_set_last_msg (gcs_recv_msg_t* msg, gcs_seqno_t seqno) { *(gcs_seqno_t*)(msg->buf) = gcs_seqno_htog (seqno); } static inline gcs_seqno_t group_get_last_msg (gcs_recv_msg_t* msg) { return gcs_seqno_gtoh(*(gcs_seqno_t*)(msg->buf)); } // This tests last applied functionality START_TEST(gcs_group_last_applied) { long ret; gcs_group_t group; gcs_comp_msg_t* comp; gcs_recv_msg_t msg0, msg1, msg2, msg3; uint8_t buf0[sizeof(gcs_seqno_t)]; uint8_t buf1[sizeof(gcs_seqno_t)]; uint8_t buf2[sizeof(gcs_seqno_t)]; uint8_t buf3[sizeof(gcs_seqno_t)]; // set up message structures msg0.type = GCS_MSG_LAST; msg0.buf_len = sizeof(gcs_seqno_t); msg0.size = sizeof(gcs_seqno_t); msg1 = msg2 = msg3 = msg0; msg0.buf = buf0; msg1.buf = buf1; msg2.buf = buf2; msg3.buf = buf3; msg0.sender_idx = 0; msg1.sender_idx = 1; msg2.sender_idx = 2; msg3.sender_idx = 3; // Create 4-node component comp = gcs_comp_msg_new (TRUE, false, 0, 4); fail_if (comp == NULL); fail_if (gcs_comp_msg_add (comp, LOCALHOST) < 0); fail_if (gcs_comp_msg_add (comp, REMOTEHOST) < 0); fail_if (gcs_comp_msg_add (comp, DISTANTHOST"1") < 0); fail_if (gcs_comp_msg_add (comp, DISTANTHOST"2") < 0); fail_if (gcs_comp_msg_add (comp, DISTANTHOST"2") >= 0); gcs_group_init(&group, NULL, "", "", 0, 0, 1); mark_point(); ret = new_component (&group, comp); fail_if (ret < 0); // fail_if (!gcs_group_is_primary(&group)); // fail_if (!gcs_group_new_members(&group)); RECEIVE_SYNC(); // 0, 0, 0, 0 fail_if (group.last_applied != 0); group_set_last_msg (&msg0, 1); fail_if (1 != group_get_last_msg(&msg0)); gcs_group_handle_last_msg (&group, &msg0); // 1, 0, 0, 0 fail_if (group.last_applied != 0); // smallest is still 0 group_set_last_msg (&msg1, 2); gcs_group_handle_last_msg (&group, &msg1); // 1, 2, 0, 0 fail_if (group.last_applied != 0); // smallest is still 0 group_set_last_msg (&msg2, 3); gcs_group_handle_last_msg (&group, &msg2); // 1, 2, 3, 0 fail_if (group.last_applied != 0); // smallest is still 0 group_set_last_msg (&msg3, 4); gcs_group_handle_last_msg (&group, &msg3); // 1, 2, 3, 4 fail_if (group.last_applied != 1); // now must be 1 group_set_last_msg (&msg1, 6); gcs_group_handle_last_msg (&group, &msg1); // 1, 6, 3, 4 fail_if (group.last_applied != 1); // now must still be 1 group_set_last_msg (&msg0, 7); gcs_group_handle_last_msg (&group, &msg0); // 7, 6, 3, 4 fail_if (group.last_applied != 3); // now must be 3 group_set_last_msg (&msg3, 8); gcs_group_handle_last_msg (&group, &msg3); // 7, 6, 3, 8 fail_if (group.last_applied != 3); // must still be 3 // remove the lagging node gcs_comp_msg_delete(comp); comp = gcs_comp_msg_new (TRUE, false, 0, 3); fail_if (comp == NULL); fail_if (gcs_comp_msg_add (comp, LOCALHOST) < 0); fail_if (gcs_comp_msg_add (comp, REMOTEHOST) < 0); fail_if (gcs_comp_msg_add (comp, DISTANTHOST"2") < 0); ret = new_component (&group, comp); fail_if (ret < 0); // fail_if (!gcs_group_is_primary(&group)); // fail_if (gcs_group_new_members(&group)); RECEIVE_SYNC(); // 7, 6, 8 fail_if (group.last_applied != 6, "Expected %u, got %llu\nGroup: %d: %s, %s, %s", 6, group.last_applied, group.num, group.nodes[0].id, group.nodes[1].id,group.nodes[2].id); // add new node gcs_comp_msg_delete(comp); comp = gcs_comp_msg_new (TRUE, false, 0, 4); fail_if (comp == NULL); fail_if (gcs_comp_msg_add (comp, LOCALHOST) < 0); fail_if (gcs_comp_msg_add (comp, REMOTEHOST) < 0); fail_if (gcs_comp_msg_add (comp, DISTANTHOST"2") < 0); fail_if (gcs_comp_msg_add (comp, DISTANTHOST"1") < 0); ret = new_component (&group, comp); fail_if (ret < 0); // fail_if (!gcs_group_is_primary(&group)); // fail_if (!gcs_group_new_members(&group)); // 7, 6, 8, 0 fail_if (group.last_applied != 0); } END_TEST Suite *gcs_group_suite(void) { Suite *suite = suite_create("GCS group context"); TCase *tcase = tcase_create("gcs_group"); suite_add_tcase (suite, tcase); tcase_add_test (tcase, gcs_group_configuration); tcase_add_test (tcase, gcs_group_last_applied); return suite; } percona-xtradb-cluster-galera/gcs/src/unit_tests/gcs_group_test.h0000644000000000000000000000037012247075736025616 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_group_test.h 301 2008-04-14 15:04:14Z alex $ */ #ifndef __gcs_group_test__ #define __gcs_group_test__ extern Suite *gcs_group_suite(void); #endif /* __gu_group_test__ */ percona-xtradb-cluster-galera/gcs/src/unit_tests/gcs_memb_test.c0000644000000000000000000003573212247075736025407 0ustar rootroot00000000000000/* * Copyright (C) 2011 Codership Oy * * $Id$ */ #include #include "gcs_memb_test.h" #include "../gcs_group.h" #include "../gcs_comp_msg.h" #include #include struct node { gcs_group_t group; const char id[GCS_COMP_MEMB_ID_MAX_LEN + 1]; /// ID assigned by the backend }; #define MAX_NODES 10 struct group { struct node* nodes[MAX_NODES]; int nodes_num; }; /* delivers new component message to all memebers */ static long deliver_component_msg (struct group* group, bool prim) { int i; for (i = 0; i < group->nodes_num; i++) { gcs_comp_msg_t* msg = gcs_comp_msg_new (prim, false, i, group->nodes_num); if (msg) { int j; for (j = 0; j < group->nodes_num; j++) { const struct node* const node = group->nodes[j]; long ret = gcs_comp_msg_add (msg, node->id); fail_if (j != ret, "Failed to add %d member: %ld (%s)", j, ret, strerror(-ret)); } /* check component message */ fail_if (i != gcs_comp_msg_self(msg)); fail_if (group->nodes_num != gcs_comp_msg_num(msg)); for (j = 0; j < group->nodes_num; j++) { const char* const src_id = group->nodes[j]->id; const char* const dst_id = gcs_comp_msg_id (msg, j); fail_if (strcmp(src_id, dst_id), "%d node id %s, recorded in comp msg as %s", j, src_id, dst_id); } gcs_group_state_t ret = gcs_group_handle_comp_msg (&(group->nodes[i]->group), msg); fail_if (ret != GCS_GROUP_WAIT_STATE_UUID); gcs_comp_msg_delete (msg); /* check that uuids are properly recorded in internal structures */ for (j = 0; j < group->nodes_num; j++) { const char* src_id = group->nodes[j]->id; const char* dst_id = group->nodes[i]->group.nodes[j].id; fail_if (strcmp(src_id, dst_id), "%d node id %s, recorded at node %d as %s", j, src_id, i, dst_id); } } else { return -ENOMEM; } } return 0; } #if 0 static long group_send_msg (struct group* group, gcs_group_t* node, const void* msg, ssize_t msg_len) { return 0; } #endif static long perform_state_exchange (struct group* group) { /* first deliver state uuid message */ gu_uuid_t state_uuid; gu_uuid_generate (&state_uuid, NULL, 0); gcs_recv_msg_t uuid_msg = { .buf = &state_uuid, .buf_len = sizeof (state_uuid), .size = sizeof (state_uuid), .sender_idx = 0, .type = GCS_MSG_STATE_UUID }; gcs_group_state_t state; int i; for (i = 0; i < group->nodes_num; i++) { state = gcs_group_handle_uuid_msg (&(group->nodes[i]->group),&uuid_msg); fail_if (state != GCS_GROUP_WAIT_STATE_MSG, "Wrong group state after STATE_UUID message. " "Expected: %d, got: %d", GCS_GROUP_WAIT_STATE_MSG, state); } /* complete state message exchange */ for (i = 0; i < group->nodes_num; i++) { /* create state message from node i */ gcs_state_msg_t* state = gcs_group_get_state (&(group->nodes[i]->group)); fail_if (NULL == state); ssize_t state_len = gcs_state_msg_len (state); uint8_t state_buf[state_len]; gcs_state_msg_write (state_buf, state); gcs_recv_msg_t state_msg = { .buf = state_buf, .buf_len = sizeof (state_buf), .size = sizeof (state_buf), .sender_idx = i, .type = GCS_MSG_STATE_MSG }; /* deliver to each of the nodes */ int j; for (j = 0; j < group->nodes_num; j++) { gcs_group_state_t ret = gcs_group_handle_state_msg (&(group->nodes[j]->group), &state_msg); if (group->nodes_num - 1 == i) { // a message from the last node fail_if (ret != GCS_GROUP_PRIMARY, "Handling state msg failed: sender %d, receiver %d", i, j); } else { fail_if (ret != GCS_GROUP_WAIT_STATE_MSG, "Handling state msg failed: sender %d, receiver %d", i, j); } } gcs_state_msg_destroy (state); } return 0; } static long group_add_node (struct group* group, struct node* node, bool new_id) { if (new_id) { gu_uuid_t node_uuid; gu_uuid_generate (&node_uuid, NULL, 0); gu_uuid_print (&node_uuid, (char*)node->id, sizeof (node->id)); gu_debug ("Node %d (%p) UUID: %s", group->nodes_num, node, node->id); } group->nodes[group->nodes_num] = node; group->nodes_num++; /* check that all node ids are different */ int i; for (i = 0; i < group->nodes_num; i++) { int j; for (j = i+1; j < group->nodes_num; j++) { fail_if (!strcmp (group->nodes[i]->id, group->nodes[j]->id), "%d (%p) and %d (%p) have the same id: %s/%s", i, group->nodes[i], j,group->nodes[j], group->nodes[i]->id, group->nodes[j]->id); } } /* deliver new component message to all nodes */ long ret = deliver_component_msg (group, true); fail_if (ret != 0, "Component message delivery failed: %d (%s)", ret, strerror(-ret)); /* deliver state exchange uuid */ ret = perform_state_exchange (group); fail_if (ret != 0, "State exchange failed: %d (%s)", ret, strerror(-ret)); return 0; } /* NOTE: this function uses simplified and determinitstic algorithm where * dropped node is always replaced by the last one in group. * For our purposes (reproduction of #465) it fits perfectly. */ static struct node* group_drop_node (struct group* group, int idx) { struct node* dropped = group->nodes[idx]; group->nodes[idx] = group->nodes[group->nodes_num - 1]; group->nodes[group->nodes_num - 1] = NULL; group->nodes_num--; if (group->nodes_num > 0) { deliver_component_msg (group, true); perform_state_exchange (group); } return dropped; } static gcs_node_state_t get_node_state (struct node* node) { return node->group.nodes[node->group.my_idx].status; } /* for delivery of GCS_MSG_SYNC or GCS_MSG_JOIN msg*/ static long deliver_join_sync_msg (struct group* const group, int const src, gcs_msg_type_t type) { gcs_seqno_t seqno = group->nodes[src]->group.act_id; gcs_recv_msg_t msg = { .buf = &seqno, .buf_len = sizeof (seqno), .size = sizeof (seqno), .sender_idx = src, .type = type }; long ret = -1; int i; for (i = 0; i < group->nodes_num; i++) { gcs_group_t* const gr = &group->nodes[i]->group; switch (type) { case GCS_MSG_JOIN: ret = gcs_group_handle_join_msg(gr, &msg); if (i == src) { fail_if (ret != 1, "%d failed to handle own JOIN message: %d (%s)", i, ret, strerror (-ret)); } else { fail_if (ret != 0, "%d failed to handle other JOIN message: %d (%s)", i, ret, strerror (-ret)); } break; case GCS_MSG_SYNC: ret = gcs_group_handle_sync_msg(gr, &msg); if (i == src) { fail_if (ret != 1 && gr->nodes[src].status == GCS_NODE_STATE_JOINED, "%d failed to handle own SYNC message: %d (%s)", i, ret, strerror (-ret)); } else { fail_if (ret != 0, "%d failed to handle other SYNC message: %d (%s)", i, ret, strerror (-ret)); } break; default: fail ("wrong message type: %d", type); } } return ret; } static bool verify_node_state_across_group (struct group* group, int const idx, gcs_node_state_t const check) { bool ret = false; int i; for (i = 0; i < group->nodes_num; i++) { gcs_node_state_t state = group->nodes[i]->group.nodes[idx].status; if (check != state) { gu_error("At node %d node's %d status is not %d, but %d", i, idx, check, state); ret = true; } } return ret; } /* start SST on behald of node idx (joiner) */ static long group_sst_start (struct group* group, int const src_idx, const char* donor) { ssize_t const req_len = strlen (donor) + 2; // leave one byte as sst request payload int donor_idx = -1; int i; for (i = 0; i < group->nodes_num; i++) { // sst request is expected to be dynamically allocated char* req_buf = malloc (req_len); fail_if (NULL == req_buf); sprintf (req_buf, "%s", donor); struct gcs_act_rcvd req = { .act = {.buf = req_buf, .buf_len = req_len, .type = GCS_ACT_STATE_REQ }, .sender_idx = src_idx, .id = GCS_SEQNO_ILL }; long ret; ret = gcs_group_handle_state_request (&group->nodes[i]->group, &req); if (ret < 0) { // don't fail here, we may want to test negatives gu_error (ret < 0, "Handling state request to '%s' failed: %d (%s)", donor, ret, strerror (-ret)); return ret; } if (i == src_idx) { fail_if (ret != req_len); free (req_buf); // passed to joiner } else { if (ret > 0) { if (donor_idx < 0) { fail_if (req.id != i); donor_idx = i; free (req_buf); // passed to donor } else { fail ("More than one donor selected: %d, first donor: %d", i, donor_idx); } } } } fail_if (donor_idx < 0, "Failed to select donor"); for (i = 0; i < group->nodes_num; i++) { gcs_node_state_t state; gcs_group_t* gr = &group->nodes[i]->group; state = gr->nodes[donor_idx].status; fail_if (state != GCS_NODE_STATE_DONOR, "%d is not donor at %d", donor_idx, i); state = gr->nodes[src_idx].status; fail_if (state != GCS_NODE_STATE_JOINER, "%d is not joiner at %d", src_idx, i); /* check that donor and joiner point at each other */ fail_if (memcmp (gr->nodes[donor_idx].joiner, gr->nodes[src_idx].id, GCS_COMP_MEMB_ID_MAX_LEN+1), "Donor points at wrong joiner: expected %s, got %s", gr->nodes[src_idx].id, gr->nodes[donor_idx].joiner); fail_if (memcmp (gr->nodes[src_idx].donor, gr->nodes[donor_idx].id, GCS_COMP_MEMB_ID_MAX_LEN+1), "Joiner points at wrong donor: expected %s, got %s", gr->nodes[donor_idx].id, gr->nodes[src_idx].donor); } return 0; } /* Thes test was specifically created to reproduce #465 */ START_TEST(gcs_memb_test_465) { struct group group; group.nodes_num = 0; struct node nodes[MAX_NODES]; int i; ssize_t ret = 0; // initialize individual node structures for (i = 0; i < MAX_NODES; i++) { int const str_len = 32; char name_str[str_len]; char addr_str[str_len]; sprintf(name_str, "node%d", i); sprintf(addr_str, "addr%d", i); gcs_group_init (&nodes[i].group, NULL, name_str, addr_str, 0, 0, 0); } gcs_node_state_t node_state; // bootstrap the cluster group_add_node (&group, &nodes[0], true); fail_if (nodes[0].group.state != GCS_GROUP_PRIMARY); node_state = get_node_state (&nodes[0]); fail_if (node_state != GCS_NODE_STATE_JOINED); deliver_join_sync_msg (&group, 0, GCS_MSG_SYNC); node_state = get_node_state (&nodes[0]); fail_if (node_state != GCS_NODE_STATE_SYNCED); group_add_node (&group, &nodes[1], true); fail_if (nodes[1].group.state != GCS_GROUP_PRIMARY); node_state = get_node_state (&nodes[1]); fail_if (node_state != GCS_NODE_STATE_PRIM); // need sst group_add_node (&group, &nodes[2], true); fail_if (nodes[2].group.state != GCS_GROUP_PRIMARY); node_state = get_node_state (&nodes[2]); fail_if (node_state != GCS_NODE_STATE_PRIM); // need sst fail_if (verify_node_state_across_group (&group, 0, GCS_NODE_STATE_SYNCED)); group_sst_start (&group, 2, nodes[0].group.nodes[0].name); deliver_join_sync_msg (&group, 0, GCS_MSG_JOIN); // end of donor SST deliver_join_sync_msg (&group, 0, GCS_MSG_SYNC); // donor synced deliver_join_sync_msg (&group, 2, GCS_MSG_SYNC); // joiner can't sync fail_if (verify_node_state_across_group (&group, 2, GCS_NODE_STATE_JOINER)); deliver_join_sync_msg (&group, 2, GCS_MSG_JOIN); // end of joiner SST deliver_join_sync_msg (&group, 2, GCS_MSG_SYNC); // joiner synced fail_if (verify_node_state_across_group (&group, 0, GCS_NODE_STATE_SYNCED)); fail_if (verify_node_state_across_group (&group, 1, GCS_NODE_STATE_PRIM)); fail_if (verify_node_state_across_group (&group, 2, GCS_NODE_STATE_SYNCED)); group_sst_start (&group, 1, nodes[0].group.nodes[0].name); deliver_join_sync_msg (&group, 0, GCS_MSG_JOIN); // end of donor SST deliver_join_sync_msg (&group, 1, GCS_MSG_JOIN); // end of joiner SST struct node* dropped = group_drop_node (&group, 1); fail_if (NULL == dropped); /* After that, according to #465, node 1 shifted from SYNCED to PRIMARY */ fail_if (verify_node_state_across_group (&group, 1, GCS_NODE_STATE_SYNCED)); struct gcs_act act; int proto_ver = -1; ret = gcs_group_act_conf (&group.nodes[1]->group, &act, &proto_ver); fail_if (ret <= 0, "gcs_group_act_cnf() retruned %zd (%s)", ret, strerror (-ret)); fail_if (ret != act.buf_len); fail_if (proto_ver != 0 /* current version */, "proto_ver = %d", proto_ver); const gcs_act_conf_t* conf = act.buf; fail_if (NULL == conf); fail_if (conf->my_idx != 1); /* according to #465 this was GCS_NODE_STATE_PRIM */ fail_if (conf->my_state != GCS_NODE_STATE_SYNCED); deliver_join_sync_msg (&group, 0, GCS_MSG_SYNC); // donor synced fail_if (verify_node_state_across_group (&group, 0, GCS_NODE_STATE_SYNCED)); } END_TEST Suite *gcs_memb_suite(void) { Suite *suite = suite_create("GCS membership changes"); TCase *tcase = tcase_create("gcs_memb"); suite_add_tcase (suite, tcase); tcase_add_test (tcase, gcs_memb_test_465); return suite; } percona-xtradb-cluster-galera/gcs/src/unit_tests/gcs_memb_test.h0000644000000000000000000000030412247075736025377 0ustar rootroot00000000000000/* * Copyright (C) 2011 Codership Oy * * $Id$ */ #ifndef __gcs_memb_test__ #define __gcs_memb_test__ extern Suite *gcs_memb_suite(void); #endif /* __gu_group_test__ */ percona-xtradb-cluster-galera/gcs/src/unit_tests/gcs_node_test.c0000644000000000000000000000326512247075736025410 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_node_test.c 2272 2011-07-28 23:24:41Z alex $ */ #include #include #include #include #include "gcs_node_test.h" #include "../gcs_node.h" #define NODE_ID "owpiefd[woie" #define NODE_NAME "strange name" #define NODE_ADDR "0.0.0.0:0" START_TEST (gcs_node_test) { /* this is a small unit test as node unit does almost nothing */ gcs_node_t node1, node2; static const gcs_seqno_t seqno = 333; gcs_node_init (&node1, NULL, NODE_ID, NODE_NAME, NODE_ADDR, 0, 0, 0); gcs_node_init (&node2, NULL, "baka", NULL, NULL, 0, 0, 0); fail_if (strcmp(node1.id, NODE_ID), "Expected node id '%s', found '%s'", NODE_ID, node1.id); fail_if (strcmp(node1.name, NODE_NAME), "Expected node name '%s', " "found '%s'", NODE_NAME, node1.name); fail_if (strcmp(node1.inc_addr, NODE_ADDR), "Expected node id '%s', " "found '%s'", NODE_ADDR, node1.inc_addr); fail_if (gcs_node_get_last_applied(&node1)); gcs_node_set_last_applied (&node1, seqno); mark_point(); gcs_node_move (&node2, &node1); fail_if (seqno != gcs_node_get_last_applied (&node2), "move didn't preserve last_applied"); fail_if (strcmp(node2.id, NODE_ID), "Expected node id '%s', found '%s'", NODE_ID, node2.id); gcs_node_reset (&node1); mark_point(); gcs_node_free (&node2); } END_TEST Suite *gcs_node_suite(void) { Suite *suite = suite_create("GCS node context"); TCase *tcase = tcase_create("gcs_node"); suite_add_tcase (suite, tcase); tcase_add_test (tcase, gcs_node_test); return suite; } percona-xtradb-cluster-galera/gcs/src/unit_tests/gcs_node_test.h0000644000000000000000000000036312247075736025411 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_node_test.h 272 2008-04-04 15:58:28Z alex $ */ #ifndef __gcs_node_test__ #define __gcs_node_test__ extern Suite *gcs_node_suite(void); #endif /* __gu_node_test__ */ percona-xtradb-cluster-galera/gcs/src/unit_tests/gcs_proto_test.c0000644000000000000000000000744512247075736025632 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_proto_test.c 2025 2011-01-28 00:48:01Z alex $ */ #include #include #include #include "../gcs_act_proto.h" #include "gcs_proto_test.h" static long frgcmp (gcs_act_frag_t* f1, gcs_act_frag_t* f2) { if ( (f1->act_id == f2->act_id) && (f1->act_size == f2->act_size) && (f1->act_type == f2->act_type) && (f1->frag_len == f2->frag_len) && // expect to point (f1->frag == f2->frag) // at the same buffer here ) return 0; else return -1; } START_TEST (gcs_proto_test) { const char act_send[] = "Test action smuction"; const char* act_send_ptr = act_send; char act_recv[] = "owoeijrvfokpvfcsdnfvkmk;l"; char* act_recv_ptr = act_recv; const size_t buf_len = 32; char buf[buf_len]; gcs_act_frag_t frg_send, frg_recv; long ret; frg_send.act_id = getpid(); frg_send.act_size = strlen (act_send); frg_send.frag = NULL; frg_send.frag_len = 0; frg_send.frag_no = 0; frg_send.act_type = 0; frg_send.proto_ver = 0; // set up action header ret = gcs_act_proto_write (&frg_send, buf, buf_len); fail_if (ret, "error code: %d", ret); fail_if (frg_send.frag == NULL); fail_if (frg_send.frag_len == 0); fail_if (strlen(act_send) < frg_send.frag_len, "Expected fragmentation, but action seems to fit in buffer" " - increase send action length"); // write action to the buffer, it should not fit strncpy ((char*)frg_send.frag, act_send_ptr, frg_send.frag_len); act_send_ptr += frg_send.frag_len; // message was sent and received, now parse the header ret = gcs_act_proto_read (&frg_recv, buf, buf_len); fail_if (ret, "error code: %d", ret); fail_if (frg_recv.frag == NULL); fail_if (frg_recv.frag_len == 0); fail_if (frgcmp (&frg_send, &frg_recv), "Sent and recvd headers are not identical"); fail_if (frg_send.frag_no != frg_recv.frag_no, "Fragment numbers are not identical: %d %d", frg_send.frag_no, frg_recv.frag_no); // read the fragment into receiving action buffer // FIXME: this works by sheer luck - only because strncpy() pads // the remaining buffer space with 0 strncpy (act_recv_ptr, frg_recv.frag, frg_recv.frag_len); act_recv_ptr += frg_recv.frag_len; // send the second fragment. Increment the fragment counter gcs_act_proto_inc (buf); // should be 1 now // write action to the buffer, it should fit now strncpy ((char*)frg_send.frag, act_send_ptr, frg_send.frag_len); // act_send_ptr += frg_send.frag_len; // message was sent and received, now parse the header ret = gcs_act_proto_read (&frg_recv, buf, buf_len); fail_if (ret, "error code: %d", ret); fail_if (frgcmp (&frg_send, &frg_recv), "Sent and recvd headers are not identical"); fail_if (frg_send.frag_no + 1 != frg_recv.frag_no, "Fragment numbers are not sequential: %d %d", frg_send.frag_no, frg_recv.frag_no); // read the fragment into receiving action buffer // FIXME: this works by sheer luck - only because strncpy() pads // the remaining buffer space with 0 strncpy (act_recv_ptr, frg_recv.frag, frg_recv.frag_len); fail_if (strlen(act_recv_ptr) >= frg_send.frag_len, "Fragment does not seem to fit in buffer: '%s'(%d)", strlen(act_recv_ptr), act_recv_ptr); // check that actions are identical fail_if (strcmp(act_send, act_recv), "Actions don't match: '%s' -- '%s'", act_send, act_recv); } END_TEST Suite *gcs_proto_suite(void) { Suite *suite = suite_create("GCS core protocol"); TCase *tcase = tcase_create("gcs_proto"); suite_add_tcase (suite, tcase); tcase_add_test (tcase, gcs_proto_test); return suite; } percona-xtradb-cluster-galera/gcs/src/unit_tests/gcs_proto_test.h0000644000000000000000000000037012247075736025625 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_proto_test.h 261 2008-04-03 11:08:37Z alex $ */ #ifndef __gcs_proto_test__ #define __gcs_proto_test__ extern Suite *gcs_proto_suite(void); #endif /* __gu_proto_test__ */ percona-xtradb-cluster-galera/gcs/src/unit_tests/gcs_sm_test.c0000644000000000000000000003502112247075736025075 0ustar rootroot00000000000000// Copyright (C) 2010 Codership Oy // $Id: gcs_sm_test.c 1886 2010-09-03 11:48:46Z alex $ #include #include #include "gcs_sm_test.h" #include "../gcs_sm.h" #define TEST_USLEEP 10000 /* we can't use pthread functions for waiting for certain conditions */ #define WAIT_FOR(cond) \ { int count = 1000; while (--count && !(cond)) { usleep (1000); }} START_TEST (gcs_sm_test_basic) { int ret; gcs_sm_t* sm = gcs_sm_create(2, 1); fail_if(!sm); gu_cond_t cond; gu_cond_init (&cond, NULL); int i; for (i = 1; i < 5; i++) { ret = gcs_sm_enter(sm, &cond, false); fail_if(ret, "gcs_sm_enter() failed: %d (%s)", ret, strerror(-ret)); fail_if(sm->users != 1, "users = %ld, expected 1", sm->users); fail_if(sm->entered != 1, "entered = %d, expected 1", sm->entered); gcs_sm_leave(sm); fail_if(sm->entered != 0, "entered = %d, expected %d", sm->entered, 0); } ret = gcs_sm_close(sm); fail_if(ret); gcs_sm_destroy(sm); gu_cond_destroy(&cond); } END_TEST volatile long simple_ret; static void* simple_thread(void* arg) { gcs_sm_t* sm = (gcs_sm_t*) arg; gu_cond_t cond; gu_cond_init (&cond, NULL); if (0 == (simple_ret = gcs_sm_enter (sm, &cond, false))) { usleep(1000); gcs_sm_leave (sm); } gu_cond_destroy (&cond); return NULL; } START_TEST (gcs_sm_test_simple) { int ret; gcs_sm_t* sm = gcs_sm_create(4, 1); fail_if(!sm); gu_cond_t cond; gu_cond_init (&cond, NULL); ret = gcs_sm_enter(sm, &cond, false); fail_if(ret, "gcs_sm_enter() failed: %d (%s)", ret, strerror(-ret)); fail_if(sm->users != 1, "users = %ld, expected 1", sm->users); fail_if(sm->entered != true, "entered = %d, expected %d", sm->users, true); gu_thread_t t1, t2, t3, t4; gu_thread_create (&t1, NULL, simple_thread, sm); gu_thread_create (&t2, NULL, simple_thread, sm); gu_thread_create (&t3, NULL, simple_thread, sm); WAIT_FOR ((long)sm->wait_q_len == sm->users); fail_if((long)sm->wait_q_len != sm->users, "wait_q_len = %lu, users = %ld", sm->wait_q_len, sm->users); gu_thread_create (&t4, NULL, simple_thread, sm); mark_point(); gu_thread_join (t4, NULL); // there's no space in the queue fail_if (simple_ret != -EAGAIN); fail_if (0 != sm->wait_q_tail, "wait_q_tail = %lu, expected 0", sm->wait_q_tail); fail_if (1 != sm->wait_q_head, "wait_q_head = %lu, expected 1", sm->wait_q_head); fail_if (4 != sm->users, "users = %lu, expected 4", sm->users); gu_info ("Calling gcs_sm_leave()"); gcs_sm_leave(sm); fail_unless(4 > sm->users, "users = %lu, expected 4", sm->users); gu_info ("Calling gcs_sm_close()"); ret = gcs_sm_close(sm); fail_if(ret); gu_thread_join(t1, NULL); gu_thread_join(t2, NULL); gu_thread_join(t3, NULL); gcs_sm_destroy(sm); gu_cond_destroy(&cond); } END_TEST static volatile int order = 0; // global variable to trac the order of events static void* closing_thread (void* data) { gcs_sm_t* sm = (gcs_sm_t*)data; fail_if(order != 0, "order is %d, expected 0", order); order = 1; int ret = gcs_sm_close(sm); fail_if(ret); fail_if(order != 2, "order is %d, expected 2", order); gcs_sm_destroy(sm); return NULL; } START_TEST (gcs_sm_test_close) { order = 0; gcs_sm_t* sm = gcs_sm_create(2, 1); fail_if(!sm); gu_cond_t cond; gu_cond_init (&cond, NULL); int ret = gcs_sm_enter(sm, &cond, false); fail_if(ret, "gcs_sm_enter() failed: %d (%s)", ret, strerror(-ret)); fail_if(sm->users != 1, "users = %ld, expected 1", sm->users); fail_if(order != 0); fail_if(1 != sm->wait_q_head, "wait_q_head = %lu, expected 1", sm->wait_q_head); fail_if(1 != sm->wait_q_tail, "wait_q_tail = %lu, expected 1", sm->wait_q_tail); gu_thread_t thr; gu_thread_create (&thr, NULL, closing_thread, sm); WAIT_FOR(1 == order); fail_if(order != 1, "order is %d, expected 1", order); usleep(TEST_USLEEP); // make sure closing_thread() blocks in gcs_sm_close() fail_if(sm->users != 2, "users = %ld, expected 2", sm->users); gu_info ("Started close thread, users = %ld", sm->users); fail_if(1 != sm->wait_q_head, "wait_q_head = %lu, expected 1", sm->wait_q_head); fail_if(0 != sm->wait_q_tail, "wait_q_tail = %lu, expected 0", sm->wait_q_tail); fail_if(1 != sm->entered); order = 2; gcs_sm_leave(sm); mark_point(); gu_thread_join(thr, NULL); gu_cond_destroy(&cond); } END_TEST static volatile int pause_order = 0; static void* pausing_thread (void* data) { gu_info ("pausing_thread start, pause_order = %d", pause_order); gcs_sm_t* sm = (gcs_sm_t*)data; gu_cond_t cond; gu_cond_init (&cond, NULL); gcs_sm_schedule (sm); gu_info ("pausing_thread scheduled, pause_order = %d", pause_order); fail_if (pause_order != 0, "pause_order = %d, expected 0"); pause_order = 1; gcs_sm_enter (sm, &cond, true); gu_info ("pausing_thread entered, pause_order = %d", pause_order); fail_if (pause_order != 2, "pause_order = %d, expected 2"); pause_order = 3; usleep(TEST_USLEEP); gcs_sm_leave (sm); mark_point(); gu_cond_destroy(&cond); gu_info ("pausing_thread exit, pause_order = %d", pause_order); return NULL; } START_TEST (gcs_sm_test_pause) { long q_len; double q_len_avg; double paused_for; gcs_sm_t* sm = gcs_sm_create(4, 1); fail_if(!sm); fail_if(1 != sm->wait_q_head, "wait_q_head = %lu, expected 1", sm->wait_q_head); gu_cond_t cond; gu_cond_init (&cond, NULL); gu_thread_t thr; gcs_sm_stats (sm, &q_len, &q_len_avg, &paused_for); fail_if (paused_for != 0.0); fail_if (q_len_avg != 0.0); fail_if (q_len != 0); // Test attempt to enter paused monitor pause_order = 0; gcs_sm_pause (sm); gu_thread_create (&thr, NULL, pausing_thread, sm); WAIT_FOR(1 == pause_order); fail_if (pause_order != 1, "pause_order = %d, expected 1"); usleep(TEST_USLEEP); // make sure pausing_thread blocked in gcs_sm_enter() pause_order = 2; // testing taking stats in the middle of the pause pt. 1 gcs_sm_stats (sm, &q_len, &q_len_avg, &paused_for); fail_if (paused_for <= 0.0); fail_if (q_len_avg != 0.0); gu_info ("Calling gcs_sm_continue()"); gcs_sm_continue (sm); gu_thread_join (thr, NULL); fail_if (pause_order != 3, "pause_order = %d, expected 3"); fail_if(2 != sm->wait_q_head, "wait_q_head = %lu, expected 2", sm->wait_q_head); fail_if(1 != sm->wait_q_tail, "wait_q_tail = %lu, expected 1", sm->wait_q_tail); // testing taking stats in the middle of the pause pt. 2 gcs_sm_stats (sm, &q_len, &q_len_avg, &paused_for); fail_if (paused_for <= 0.0); fail_if (q_len_avg != 0.0); // Testing scheduling capability gcs_sm_schedule (sm); fail_if(2 != sm->wait_q_tail, "wait_q_tail = %lu, expected 2", sm->wait_q_tail); gu_thread_create (&thr, NULL, pausing_thread, sm); usleep (TEST_USLEEP); // no changes in pause_order fail_if (pause_order != 3, "pause_order = %d, expected 3"); pause_order = 0; int ret = gcs_sm_enter(sm, &cond, true); fail_if (ret, "gcs_sm_enter() failed: %d (%s)", ret, strerror(-ret)); // released monitor lock, thr should continue and schedule, // set pause_order to 1 WAIT_FOR(1 == pause_order); fail_if (pause_order != 1, "pause_order = %d, expected 1"); fail_if (sm->users != 2, "users = %ld, expected 2", sm->users); fail_if(2 != sm->wait_q_head, "wait_q_head = %lu, expected 2", sm->wait_q_head); fail_if(3 != sm->wait_q_tail, "wait_q_tail = %lu, expected 3", sm->wait_q_tail); gcs_sm_stats (sm, &q_len, &q_len_avg, &paused_for); fail_if (paused_for != 0.0); fail_if (q_len != sm->users, "found q_len %d, expected = %d", q_len, sm->users); fail_if ((q_len_avg - 0.5) > 0.0000001 || (q_len_avg - 0.5) < -0.0000001); gu_info ("Started pause thread, users = %ld", sm->users); // Now test pausing when monitor is in entered state pause_order = 2; gcs_sm_pause (sm); usleep (TEST_USLEEP); gcs_sm_continue (sm); // nothing should continue, since monitor is entered usleep (TEST_USLEEP); fail_if (pause_order != 2, "pause_order = %d, expected 2"); fail_if (sm->entered != 1, "entered = %ld, expected 1", sm->entered); // Now test pausing when monitor is left gcs_sm_pause (sm); fail_if (sm->users != 2, "users = %ld, expected 2", sm->users); gcs_sm_leave (sm); fail_if (sm->users != 1, "users = %ld, expected 1", sm->users); fail_if (sm->entered != 0, "entered = %ld, expected 1", sm->entered); fail_if(3 != sm->wait_q_head, "wait_q_head = %lu, expected 3", sm->wait_q_head); fail_if(3 != sm->wait_q_tail, "wait_q_tail = %lu, expected 3", sm->wait_q_tail); usleep (TEST_USLEEP); // nothing should change, since monitor is paused fail_if (pause_order != 2, "pause_order = %d, expected 2"); fail_if (sm->entered != 0, "entered = %ld, expected 0", sm->entered); fail_if (sm->users != 1, "users = %ld, expected 1", sm->users); gcs_sm_continue (sm); // paused thread should continue WAIT_FOR(3 == pause_order); fail_if (pause_order != 3, "pause_order = %d, expected 3"); gcs_sm_stats (sm, &q_len, &q_len_avg, &paused_for); fail_if (paused_for <= 0.0); fail_if (q_len_avg != 0.0); gcs_sm_enter (sm, &cond, false); // by now paused thread exited monitor fail_if (sm->entered != 1, "entered = %ld, expected 1", sm->entered); fail_if (sm->users != 1, "users = %ld, expected 1", sm->users); fail_if(0 != sm->wait_q_head, "wait_q_head = %lu, expected 0", sm->wait_q_head); fail_if(0 != sm->wait_q_tail, "wait_q_tail = %lu, expected 0", sm->wait_q_tail); gcs_sm_leave (sm); fail_if(1 != sm->wait_q_head, "wait_q_head = %lu, expected 1", sm->wait_q_head); mark_point(); gu_cond_destroy(&cond); gcs_sm_close (sm); mark_point(); gu_thread_join(thr, NULL); gcs_sm_destroy (sm); } END_TEST static volatile long global_handle = 0; static volatile long global_ret = 0; static void* interrupt_thread(void* arg) { gcs_sm_t* sm = (gcs_sm_t*) arg; global_handle = gcs_sm_schedule (sm); if (global_handle >= 0) { pthread_cond_t cond; pthread_cond_init (&cond, NULL); if (0 == (global_ret = gcs_sm_enter (sm, &cond, true))) { gcs_sm_leave (sm); } pthread_cond_destroy (&cond); } return NULL; } #define TEST_CREATE_THREAD(thr, tail, h, u) \ global_handle = -1; \ gu_thread_create (thr, NULL, interrupt_thread, sm); \ WAIT_FOR(global_handle == h); \ fail_if (sm->wait_q_tail != tail, "wait_q_tail = %lu, expected %lu", \ sm->wait_q_tail, tail); \ fail_if (global_handle != h, "global_handle = %ld, expected %ld", \ global_handle, h); \ fail_if (sm->users != u, "users = %ld, expected %ld", sm->users, u); #define TEST_INTERRUPT_THREAD(h, t) \ ret = gcs_sm_interrupt (sm, (h)); \ fail_if (ret != 0); \ gu_thread_join ((t), NULL); \ fail_if (global_ret != -EINTR, "global_ret = %ld, expected %ld (-EINTR)", \ global_ret, -EINTR); START_TEST (gcs_sm_test_interrupt) { gcs_sm_t* sm = gcs_sm_create(4, 1); fail_if(!sm); gu_cond_t cond; gu_cond_init (&cond, NULL); gu_thread_t thr1; gu_thread_t thr2; gu_thread_t thr3; long handle = gcs_sm_schedule (sm); fail_if (handle != 0, "handle = %ld, expected 0"); fail_if (sm->wait_q_tail != 1, "wait_q_tail = %lu, expected 1", sm->wait_q_tail); long ret = gcs_sm_enter (sm, &cond, true); fail_if (ret != 0); /* 1. Test interrupting blocked by previous thread */ TEST_CREATE_THREAD(&thr1, 2, 3, 2); TEST_CREATE_THREAD(&thr2, 3, 4, 3); TEST_INTERRUPT_THREAD(3, thr1); gcs_sm_leave (sm); // this should let 2nd enter monitor gu_thread_join (thr2, NULL); fail_if (global_ret != 0, "global_ret = %ld, expected 0", global_ret); fail_if (sm->users != 0, "users = %ld, expected 0", sm->users); ret = gcs_sm_interrupt (sm, 4); // try to interrupt 2nd which has exited fail_if (ret != -ESRCH); /* 2. Test interrupting blocked by pause */ gcs_sm_pause (sm); TEST_CREATE_THREAD(&thr1, 0, 1, 1); TEST_INTERRUPT_THREAD(1, thr1); TEST_CREATE_THREAD(&thr2, 1, 2, 2); /* test queueing after interrupted */ TEST_CREATE_THREAD(&thr3, 2, 3, 3); TEST_INTERRUPT_THREAD(3, thr3); /* test interrupting last waiter */ gcs_sm_continue (sm); gu_thread_join (thr2, NULL); fail_if (global_ret != 0, "global_ret = %ld, expected 0", global_ret); /* 3. Unpausing totally interrupted monitor */ gcs_sm_pause (sm); TEST_CREATE_THREAD(&thr1, 3, 4, 1); TEST_INTERRUPT_THREAD(4, thr1); TEST_CREATE_THREAD(&thr1, 0, 1, 2); TEST_INTERRUPT_THREAD(1, thr1); gcs_sm_continue (sm); /* check that monitor is still functional */ ret = gcs_sm_enter (sm, &cond, false); fail_if (ret != 0); fail_if(1 != sm->wait_q_head, "wait_q_head = %lu, expected 1", sm->wait_q_head); fail_if(1 != sm->wait_q_tail, "wait_q_tail = %lu, expected 1", sm->wait_q_tail); fail_if (sm->users != 1, "users = %ld, expected 1", sm->users); TEST_CREATE_THREAD(&thr1, 2, 3, 2); gu_info ("Calling gcs_sm_leave()"); gcs_sm_leave (sm); pthread_join (thr1, NULL); fail_if (global_ret != 0, "global_ret = %ld, expected 0", global_ret); pthread_cond_destroy (&cond); gcs_sm_close (sm); gcs_sm_destroy (sm); } END_TEST Suite *gcs_send_monitor_suite(void) { Suite *s = suite_create("GCS send monitor"); TCase *tc = tcase_create("gcs_sm"); suite_add_tcase (s, tc); tcase_add_test (tc, gcs_sm_test_basic); tcase_add_test (tc, gcs_sm_test_simple); tcase_add_test (tc, gcs_sm_test_close); tcase_add_test (tc, gcs_sm_test_pause); tcase_add_test (tc, gcs_sm_test_interrupt); return s; } percona-xtradb-cluster-galera/gcs/src/unit_tests/gcs_sm_test.h0000644000000000000000000000034512247075736025103 0ustar rootroot00000000000000// Copyright (C) 2007 Codership Oy // $Id: gcs_sm_test.h 1711 2010-05-30 07:01:15Z alex $ #ifndef __gcs_sm_test__ #define __gcs_sm_test__ Suite *gcs_send_monitor_suite(void); #endif /* __gcs_sm_test__ */ percona-xtradb-cluster-galera/gcs/src/unit_tests/gcs_state_msg_test.c0000644000000000000000000004272012247075736026450 0ustar rootroot00000000000000// Copyright (C) 2007 Codership Oy // $Id: gcs_state_msg_test.c 2553 2011-11-25 14:18:07Z alex $ #include #include #include "gcs_state_msg_test.h" #define GCS_STATE_MSG_ACCESS #include "../gcs_state_msg.h" static int const QUORUM_VERSION = 2; START_TEST (gcs_state_msg_test_basic) { ssize_t send_len, ret; gu_uuid_t state_uuid; gu_uuid_t group_uuid; gu_uuid_t prim_uuid; gcs_state_msg_t* send_state; gcs_state_msg_t* recv_state; gu_uuid_generate (&state_uuid, NULL, 0); gu_uuid_generate (&group_uuid, NULL, 0); gu_uuid_generate (&prim_uuid, NULL, 0); send_state = gcs_state_msg_create (&state_uuid, &group_uuid, &prim_uuid, 457, // prim_seqno 3465, // last received seq. 5, // prim_joined GCS_NODE_STATE_JOINED, // prim_state GCS_NODE_STATE_NON_PRIM, // current_state "My Name", // name "192.168.0.1:2345", // inc_addr 0, // gcs_proto_ver 1, // repl_proto_ver 1, // appl_proto_ver GCS_STATE_FREP // flags ); fail_if (NULL == send_state); send_len = gcs_state_msg_len (send_state); fail_if (send_len < 0, "gcs_state_msg_len() returned %zd (%s)", send_len, strerror (-send_len)); { uint8_t send_buf[send_len]; ret = gcs_state_msg_write (send_buf, send_state); fail_if (ret != send_len, "Return value does not match send_len: " "expected %zd, got %zd", send_len, ret); recv_state = gcs_state_msg_read (send_buf, send_len); fail_if (NULL == recv_state); } fail_if (send_state->flags != recv_state->flags); fail_if (send_state->gcs_proto_ver != recv_state->gcs_proto_ver); fail_if (send_state->repl_proto_ver != recv_state->repl_proto_ver); fail_if (send_state->appl_proto_ver != recv_state->appl_proto_ver); fail_if (recv_state->appl_proto_ver != 1, "appl_proto_ver: %d", recv_state->appl_proto_ver); fail_if (send_state->received != recv_state->received, "Last received seqno: sent %lld, recv %lld", send_state->received, recv_state->received); fail_if (send_state->prim_seqno != recv_state->prim_seqno); fail_if (send_state->current_state != recv_state->current_state); fail_if (send_state->prim_state != recv_state->prim_state); fail_if (send_state->prim_joined != recv_state->prim_joined); fail_if (gu_uuid_compare (&recv_state->state_uuid, &state_uuid)); fail_if (gu_uuid_compare (&recv_state->group_uuid, &group_uuid)); fail_if (gu_uuid_compare (&recv_state->prim_uuid, &prim_uuid)); fail_if (strcmp(send_state->name, recv_state->name)); fail_if (strcmp(send_state->inc_addr, recv_state->inc_addr)); { size_t str_len = 1024; char send_str[str_len]; char recv_str[str_len]; fail_if (gcs_state_msg_snprintf (send_str, str_len, send_state) <= 0); fail_if (gcs_state_msg_snprintf (recv_str, str_len, recv_state) <= 0); // no longer true fail_if (strncmp (send_str, recv_str, str_len)); } gcs_state_msg_destroy (send_state); gcs_state_msg_destroy (recv_state); } END_TEST START_TEST (gcs_state_msg_test_quorum_inherit) { gcs_state_msg_t* st[3] = { NULL, }; gu_uuid_t state_uuid; gu_uuid_t group1_uuid, group2_uuid; gu_uuid_t prim1_uuid, prim2_uuid; gu_uuid_generate (&state_uuid, NULL, 0); gu_uuid_generate (&group1_uuid, NULL, 0); gu_uuid_generate (&group2_uuid, NULL, 0); gu_uuid_generate (&prim1_uuid, NULL, 0); gu_uuid_generate (&prim2_uuid, NULL, 0); gcs_seqno_t prim1_seqno = 123; gcs_seqno_t prim2_seqno = 834; gcs_seqno_t act1_seqno = 345; gcs_seqno_t act2_seqno = 239472508908LL; gcs_state_quorum_t quorum; mark_point(); /* First just nodes from different groups and configurations, none JOINED */ st[0] = gcs_state_msg_create (&state_uuid, &group2_uuid, &prim2_uuid, prim2_seqno - 1, act2_seqno - 1, 5, GCS_NODE_STATE_PRIM, GCS_NODE_STATE_PRIM, "node0", "", 0, 1, 1, 0); fail_if(NULL == st[0]); st[1] = gcs_state_msg_create (&state_uuid, &group1_uuid, &prim1_uuid, prim1_seqno, act1_seqno, 3, GCS_NODE_STATE_PRIM, GCS_NODE_STATE_PRIM, "node1", "", 0, 1, 0, 0); fail_if(NULL == st[1]); st[2] = gcs_state_msg_create (&state_uuid, &group2_uuid, &prim2_uuid, prim2_seqno, act2_seqno, 5, GCS_NODE_STATE_PRIM, GCS_NODE_STATE_PRIM, "node2", "", 0, 1, 1, 1); fail_if(NULL == st[2]); gu_info (" Inherited 1"); int ret = gcs_state_msg_get_quorum ((const gcs_state_msg_t**)st, sizeof(st)/sizeof(gcs_state_msg_t*), &quorum); fail_if (0 != ret); fail_if (QUORUM_VERSION != quorum.version); fail_if (false != quorum.primary); fail_if (0 != gu_uuid_compare(&quorum.group_uuid, &GU_UUID_NIL)); fail_if (GCS_SEQNO_ILL != quorum.act_id); fail_if (GCS_SEQNO_ILL != quorum.conf_id); fail_if (-1 != quorum.gcs_proto_ver); fail_if (-1 != quorum.repl_proto_ver); fail_if (-1 != quorum.appl_proto_ver); /* now make node1 inherit PC */ gcs_state_msg_destroy (st[1]); st[1] = gcs_state_msg_create (&state_uuid, &group1_uuid, &prim1_uuid, prim1_seqno, act1_seqno, 3, GCS_NODE_STATE_JOINED, GCS_NODE_STATE_DONOR, "node1", "", 0, 1, 0, 0); fail_if(NULL == st[1]); gu_info (" Inherited 2"); ret = gcs_state_msg_get_quorum ((const gcs_state_msg_t**)st, sizeof(st)/sizeof(gcs_state_msg_t*), &quorum); fail_if (0 != ret); fail_if (QUORUM_VERSION != quorum.version); fail_if (true != quorum.primary); fail_if (0 != gu_uuid_compare(&quorum.group_uuid, &group1_uuid)); fail_if (act1_seqno != quorum.act_id); fail_if (prim1_seqno != quorum.conf_id); fail_if (0 != quorum.gcs_proto_ver); fail_if (1 != quorum.repl_proto_ver); fail_if (0 != quorum.appl_proto_ver); /* now make node0 inherit PC (should yield conflicting uuids) */ gcs_state_msg_destroy (st[0]); st[0] = gcs_state_msg_create (&state_uuid, &group2_uuid, &prim2_uuid, prim2_seqno - 1, act2_seqno - 1, 5, GCS_NODE_STATE_SYNCED, GCS_NODE_STATE_SYNCED, "node0", "", 0, 1, 1, 0); fail_if(NULL == st[0]); gu_info (" Inherited 3"); ret = gcs_state_msg_get_quorum ((const gcs_state_msg_t**)st, sizeof(st)/sizeof(gcs_state_msg_t*), &quorum); fail_if (0 != ret); fail_if (QUORUM_VERSION != quorum.version); fail_if (false != quorum.primary); fail_if (0 != gu_uuid_compare(&quorum.group_uuid, &GU_UUID_NIL)); fail_if (GCS_SEQNO_ILL != quorum.act_id); fail_if (GCS_SEQNO_ILL != quorum.conf_id); fail_if (-1 != quorum.gcs_proto_ver); fail_if (-1 != quorum.repl_proto_ver); fail_if (-1 != quorum.appl_proto_ver); /* now make node1 non-joined again: group2 should win */ gcs_state_msg_destroy (st[1]); st[1] = gcs_state_msg_create (&state_uuid, &group1_uuid, &prim1_uuid, prim1_seqno, act1_seqno, 3, GCS_NODE_STATE_JOINED, GCS_NODE_STATE_PRIM, "node1", "", 0, 1, 0, 0); fail_if(NULL == st[1]); gu_info (" Inherited 4"); ret = gcs_state_msg_get_quorum ((const gcs_state_msg_t**)st, sizeof(st)/sizeof(gcs_state_msg_t*), &quorum); fail_if (0 != ret); fail_if (QUORUM_VERSION != quorum.version); fail_if (true != quorum.primary); fail_if (0 != gu_uuid_compare(&quorum.group_uuid, &group2_uuid)); fail_if (act2_seqno - 1 != quorum.act_id); fail_if (prim2_seqno - 1 != quorum.conf_id); fail_if (0 != quorum.gcs_proto_ver); fail_if (1 != quorum.repl_proto_ver); fail_if (0 != quorum.appl_proto_ver); /* now make node2 joined: it should become a representative */ gcs_state_msg_destroy (st[2]); st[2] = gcs_state_msg_create (&state_uuid, &group2_uuid, &prim2_uuid, prim2_seqno, act2_seqno, 5, GCS_NODE_STATE_SYNCED, GCS_NODE_STATE_SYNCED, "node2", "", 0, 1, 1, 0); fail_if(NULL == st[2]); gu_info (" Inherited 5"); ret = gcs_state_msg_get_quorum ((const gcs_state_msg_t**)st, sizeof(st)/sizeof(gcs_state_msg_t*), &quorum); fail_if (0 != ret); fail_if (QUORUM_VERSION != quorum.version); fail_if (true != quorum.primary); fail_if (0 != gu_uuid_compare(&quorum.group_uuid, &group2_uuid)); fail_if (act2_seqno != quorum.act_id); fail_if (prim2_seqno != quorum.conf_id); fail_if (0 != quorum.gcs_proto_ver); fail_if (1 != quorum.repl_proto_ver); fail_if (0 != quorum.appl_proto_ver); gcs_state_msg_destroy (st[0]); gcs_state_msg_destroy (st[1]); gcs_state_msg_destroy (st[2]); } END_TEST START_TEST (gcs_state_msg_test_quorum_remerge) { gcs_state_msg_t* st[3] = { NULL, }; gu_uuid_t state_uuid; gu_uuid_t group1_uuid, group2_uuid; gu_uuid_t prim0_uuid, prim1_uuid, prim2_uuid; gu_uuid_generate (&state_uuid, NULL, 0); gu_uuid_generate (&group1_uuid, NULL, 0); gu_uuid_generate (&group2_uuid, NULL, 0); gu_uuid_generate (&prim0_uuid, NULL, 0); gu_uuid_generate (&prim1_uuid, NULL, 0); gu_uuid_generate (&prim2_uuid, NULL, 0); gcs_seqno_t prim1_seqno = 123; gcs_seqno_t prim2_seqno = 834; gcs_seqno_t act1_seqno = 345; gcs_seqno_t act2_seqno = 239472508908LL; gcs_state_quorum_t quorum; mark_point(); /* First just nodes from different groups and configurations, none JOINED */ st[0] = gcs_state_msg_create (&state_uuid, &group2_uuid, &prim0_uuid, prim2_seqno - 1, act2_seqno - 1, 5, GCS_NODE_STATE_JOINER,GCS_NODE_STATE_NON_PRIM, "node0", "", 0, 1, 1, 0); fail_if(NULL == st[0]); st[1] = gcs_state_msg_create (&state_uuid, &group1_uuid, &prim1_uuid, prim1_seqno, act1_seqno, 3, GCS_NODE_STATE_JOINER,GCS_NODE_STATE_NON_PRIM, "node1", "", 0, 1, 0, 0); fail_if(NULL == st[1]); st[2] = gcs_state_msg_create (&state_uuid, &group2_uuid, &prim2_uuid, prim2_seqno, act2_seqno, 5, GCS_NODE_STATE_JOINER,GCS_NODE_STATE_NON_PRIM, "node2", "", 0, 1, 1, 1); fail_if(NULL == st[2]); gu_info (" Remerged 1"); int ret = gcs_state_msg_get_quorum ((const gcs_state_msg_t**)st, sizeof(st)/sizeof(gcs_state_msg_t*), &quorum); fail_if (0 != ret); fail_if (QUORUM_VERSION != quorum.version); fail_if (false != quorum.primary); fail_if (0 != gu_uuid_compare(&quorum.group_uuid, &GU_UUID_NIL)); fail_if (GCS_SEQNO_ILL != quorum.act_id); fail_if (GCS_SEQNO_ILL != quorum.conf_id); fail_if (-1 != quorum.gcs_proto_ver); fail_if (-1 != quorum.repl_proto_ver); fail_if (-1 != quorum.appl_proto_ver); /* Now make node0 to be joined at least once */ gcs_state_msg_destroy (st[0]); st[0] = gcs_state_msg_create (&state_uuid, &group2_uuid, &prim0_uuid, prim2_seqno - 1, act2_seqno - 1, 5, GCS_NODE_STATE_DONOR, GCS_NODE_STATE_NON_PRIM, "node0", "", 0, 1, 1, 0); fail_if(NULL == st[0]); gu_info (" Remerged 2"); ret = gcs_state_msg_get_quorum ((const gcs_state_msg_t**)st, sizeof(st)/sizeof(gcs_state_msg_t*), &quorum); fail_if (0 != ret); fail_if (QUORUM_VERSION != quorum.version); fail_if (true != quorum.primary); fail_if (0 != gu_uuid_compare(&quorum.group_uuid, &group2_uuid)); fail_if (act2_seqno - 1 != quorum.act_id); fail_if (prim2_seqno - 1 != quorum.conf_id); fail_if (0 != quorum.gcs_proto_ver); fail_if (1 != quorum.repl_proto_ver); fail_if (0 != quorum.appl_proto_ver); /* Now make node2 to be joined too */ gcs_state_msg_destroy (st[2]); st[2] = gcs_state_msg_create (&state_uuid, &group2_uuid, &prim2_uuid, prim2_seqno, act2_seqno, 5, GCS_NODE_STATE_JOINED,GCS_NODE_STATE_NON_PRIM, "node2", "", 0, 1, 1, 1); fail_if(NULL == st[2]); gu_info (" Remerged 3"); ret = gcs_state_msg_get_quorum ((const gcs_state_msg_t**)st, sizeof(st)/sizeof(gcs_state_msg_t*), &quorum); fail_if (0 != ret); fail_if (QUORUM_VERSION != quorum.version); fail_if (true != quorum.primary); fail_if (0 != gu_uuid_compare(&quorum.group_uuid, &group2_uuid)); fail_if (act2_seqno != quorum.act_id); fail_if (prim2_seqno != quorum.conf_id); fail_if (0 != quorum.gcs_proto_ver); fail_if (1 != quorum.repl_proto_ver); fail_if (0 != quorum.appl_proto_ver); /* now make node1 joined too: conflict */ gcs_state_msg_destroy (st[1]); st[1] = gcs_state_msg_create (&state_uuid, &group1_uuid, &prim1_uuid, prim1_seqno, act1_seqno, 3, GCS_NODE_STATE_SYNCED,GCS_NODE_STATE_NON_PRIM, "node1", "", 0, 1, 0, 0); fail_if(NULL == st[1]); gu_info (" Remerged 4"); ret = gcs_state_msg_get_quorum ((const gcs_state_msg_t**)st, sizeof(st)/sizeof(gcs_state_msg_t*), &quorum); fail_if (0 != ret); fail_if (QUORUM_VERSION != quorum.version); fail_if (false != quorum.primary); fail_if (0 != gu_uuid_compare(&quorum.group_uuid, &GU_UUID_NIL)); fail_if (GCS_SEQNO_ILL != quorum.act_id); fail_if (GCS_SEQNO_ILL != quorum.conf_id); fail_if (-1 != quorum.gcs_proto_ver); fail_if (-1 != quorum.repl_proto_ver); fail_if (-1 != quorum.appl_proto_ver); /* now make node1 current joiner: should be ignored */ gcs_state_msg_destroy (st[1]); st[1] = gcs_state_msg_create (&state_uuid, &group1_uuid, &prim1_uuid, prim1_seqno, act1_seqno, 3, GCS_NODE_STATE_SYNCED, GCS_NODE_STATE_JOINER, "node1", "", 0, 1, 0, 0); fail_if(NULL == st[1]); gu_info (" Remerged 5"); ret = gcs_state_msg_get_quorum ((const gcs_state_msg_t**)st, sizeof(st)/sizeof(gcs_state_msg_t*), &quorum); fail_if (0 != ret); fail_if (QUORUM_VERSION != quorum.version); fail_if (true != quorum.primary); fail_if (0 != gu_uuid_compare(&quorum.group_uuid, &group2_uuid)); fail_if (act2_seqno != quorum.act_id); fail_if (prim2_seqno != quorum.conf_id); fail_if (0 != quorum.gcs_proto_ver); fail_if (1 != quorum.repl_proto_ver); fail_if (0 != quorum.appl_proto_ver); gcs_state_msg_destroy (st[0]); gcs_state_msg_destroy (st[1]); gcs_state_msg_destroy (st[2]); } END_TEST Suite *gcs_state_msg_suite(void) { Suite *s = suite_create("GCS state message"); TCase *tc_basic = tcase_create("gcs_state_msg_basic"); TCase *tc_inherit = tcase_create("gcs_state_msg_inherit"); TCase *tc_remerge = tcase_create("gcs_state_msg_remerge"); suite_add_tcase (s, tc_basic); tcase_add_test (tc_basic, gcs_state_msg_test_basic); suite_add_tcase (s, tc_inherit); tcase_add_test (tc_inherit, gcs_state_msg_test_quorum_inherit); suite_add_tcase (s, tc_remerge); tcase_add_test (tc_remerge, gcs_state_msg_test_quorum_remerge); return s; } percona-xtradb-cluster-galera/gcs/src/unit_tests/gcs_state_msg_test.h0000644000000000000000000000037612247075736026456 0ustar rootroot00000000000000// Copyright (C) 2007 Codership Oy // $Id: gcs_state_msg_test.h 1556 2010-03-28 12:35:24Z alex $ #ifndef __gcs_state_msg_test__ #define __gcs_state_msg_test__ Suite *gcs_state_msg_suite(void); #endif /* __gcs_state_msg_test__ */ percona-xtradb-cluster-galera/gcs/src/unit_tests/gcs_tests.c0000644000000000000000000000410512247075736024560 0ustar rootroot00000000000000/* * Copyright (C) 2008 Codership Oy * * $Id: gcs_tests.c 2272 2011-07-28 23:24:41Z alex $ */ #include // printf() #include // strcmp() #include // EXIT_SUCCESS | EXIT_FAILURE #include #include #include "gcs_comp_test.h" #include "gcs_sm_test.h" #include "gcs_state_msg_test.h" #include "gcs_fifo_test.h" #include "gcs_proto_test.h" #include "gcs_defrag_test.h" #include "gcs_node_test.h" #include "gcs_memb_test.h" #include "gcs_group_test.h" #include "gcs_backend_test.h" #include "gcs_core_test.h" #include "gcs_fc_test.h" typedef Suite *(*suite_creator_t)(void); static suite_creator_t suites[] = { gcs_comp_suite, gcs_send_monitor_suite, gcs_state_msg_suite, gcs_fifo_suite, gcs_proto_suite, gcs_defrag_suite, gcs_node_suite, gcs_memb_suite, // gcs_group_suite, gcs_backend_suite, gcs_core_suite, gcs_fc_suite, NULL }; int main(int argc, char* argv[]) { int no_fork = ((argc > 1) && !strcmp(argv[1], "nofork")) ? 1 : 0; int i = 0; int failed = 0; FILE* log_file = NULL; log_file = fopen ("gcs_tests.log", "w"); if (!log_file) return EXIT_FAILURE; gu_conf_set_log_file (log_file); gu_conf_debug_on(); gu_conf_self_tstamp_on(); while (suites[i]) { SRunner* sr = srunner_create(suites[i]()); gu_info ("#########################"); gu_info ("Test %d.", i); gu_info ("#########################"); if (no_fork) srunner_set_fork_status(sr, CK_NOFORK); srunner_run_all (sr, CK_NORMAL); failed += srunner_ntests_failed (sr); srunner_free (sr); i++; } fclose (log_file); printf ("Total test failed: %d\n", failed); return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } /* When the suite compiled in debug mode, returns number of allocated bytes */ ssize_t gcs_tests_get_allocated() { ssize_t total; ssize_t allocs; ssize_t reallocs; ssize_t deallocs; void gu_mem_stats (ssize_t*, ssize_t*, ssize_t*, ssize_t*); gu_mem_stats (&total, &allocs, &reallocs, &deallocs); return total; } percona-xtradb-cluster-galera/scripts/build.sh0000755000000000000000000002314212247075736022001 0ustar rootroot00000000000000#!/bin/bash -eu # $Id: build.sh 3336 2013-10-28 07:41:56Z teemu $ get_cores() { case $OS in "Linux") echo "$(grep -c ^processor /proc/cpuinfo)" ;; "SunOS") echo "$(psrinfo | wc -l)" ;; "Darwin" | "FreeBSD") echo "$(sysctl -n hw.ncpu)" ;; *) echo "CPU information not available: unsupported OS: '$OS'" >/dev/stderr echo 1 ;; esac } usage() { cat << EOF Usage: build.sh [OPTIONS] Options: --stage --last-stage -s|--scratch build everything from scratch -c|--configure reconfigure the build system (implies -s) -b|--bootstap rebuild the build system (implies -c) -o|--opt configure build with debug disabled (implies -c) -d|--debug configure build with debug enabled (implies -c) --dl set debug level for Scons build (1, implies -c) -r|--release release number -m32/-m64 build 32/64-bit binaries for x86 -p|--package build RPM and DEB packages at the end. --with-spread configure build with spread backend (implies -c to gcs) --source build source packages --sb skip actual build, use the existing binaries --scons build using Scons build system (yes) --so Sconscript option -j|--jobs how many parallel jobs to use for Scons (1) "\nSet DISABLE_GCOMM/DISABLE_VSBES to 'yes' to disable respective modules" EOF } OS=$(uname) # disable building vsbes by default DISABLE_VSBES=${DISABLE_VSBES:-"yes"} DISABLE_GCOMM=${DISABLE_GCOMM:-"no"} PACKAGE=${PACKAGE:-"no"} SKIP_BUILD=${SKIP_BUILD:-"no"} RELEASE=${RELEASE:-""} SOURCE=${SOURCE:-"no"} DEBUG=${DEBUG:-"no"} DEBUG_LEVEL=${DEBUG_LEVEL:-"1"} SCONS=${SCONS:-"yes"} SCONS_OPTS=${SCONS_OPTS:-""} JOBS=${JOBS:-"$(get_cores)"} SCRATCH=${SCRATCH:-"no"} OPT="yes" NO_STRIP=${NO_STRIP:-"no"} WITH_SPREAD="no" if [ "$OS" == "FreeBSD" ]; then chown=/usr/sbin/chown true=/usr/bin/true epm=/usr/local/bin/epm else chown=/bin/chown true=/bin/true epm=/usr/bin/epm fi EXTRA_SYSROOT=${EXTRA_SYSROOT:-""} if [ "$OS" == "Darwin" ]; then if which -s port && test -x /opt/local/bin/port; then EXTRA_SYSROOT=/opt/local elif which -s brew && test -x /usr/local/bin/brew; then EXTRA_SYSROOT=/usr/local elif which -s fink && test -x /sw/bin/fink; then EXTRA_SYSROOT=/sw fi elif [ "$OS" == "FreeBSD" ]; then EXTRA_SYSROOT=/usr/local fi which dpkg >/dev/null 2>&1 && DEBIAN=${DEBIAN:-1} || DEBIAN=${DEBIAN:-0} if [ "$OS" == "FreeBSD" ]; then CC=${CC:-"gcc48"} CXX=${CXX:-"g++48"} LD_LIBRARY_PATH=${LD_LIBRARY_PATH:-"/usr/local/lib/$(basename $CC)"} else CC=${CC:-"gcc"} CXX=${CXX:-"g++"} fi if ccache -V > /dev/null 2>&1 then echo "$CC" | grep "ccache" > /dev/null || CC="ccache $CC" echo "$CXX" | grep "ccache" > /dev/null || CXX="ccache $CXX" fi export CC CXX LD_LIBRARY_PATH CFLAGS=${CFLAGS:-"-O2"} CXXFLAGS=${CXXFLAGS:-"$CFLAGS"} CPPFLAGS=${CPPFLAGS:-} initial_stage="galerautils" last_stage="galera" gainroot="" TARGET=${TARGET:-""} # default target while test $# -gt 0 do case $1 in --stage) initial_stage=$2 shift ;; --last-stage) last_stage=$2 shift ;; --gainroot) gainroot=$2 shift ;; -b|--bootstrap) BOOTSTRAP="yes" # Bootstrap the build system ;; -c|--configure) CONFIGURE="yes" # Reconfigure the build system ;; -s|--scratch) SCRATCH="yes" # Build from scratch (run make clean) ;; -o|--opt) OPT="yes" # Compile without debug ;; -d|--debug) DEBUG="yes" # Compile with debug NO_STRIP="yes" ;; -r|--release) RELEASE="$2" shift ;; -m32) CFLAGS="$CFLAGS -m32" CXXFLAGS="$CXXFLAGS -m32" SCRATCH="yes" TARGET="i686" ;; -m64) CFLAGS="$CFLAGS -m64" CXXFLAGS="$CXXFLAGS -m64" SCRATCH="yes" TARGET="x86_64" ;; -p|--package) PACKAGE="yes" # build binary packages ;; --with*-spread) WITH_SPREAD="$1" ;; --help) usage exit 0 ;; --source) SOURCE="yes" ;; --sb) SKIP_BUILD="yes" ;; --scons) SCONS="yes" ;; --so) SCONS_OPTS="$SCONS_OPTS $2" shift ;; -j|--jobs) JOBS=$2 shift ;; --dl) DEBUG_LEVEL=$2 shift ;; *) if test ! -z "$1"; then echo "Unrecognized option: $1" fi usage exit 1 ;; esac shift done # check whether sudo accepts -E to preserve environment if [ "$PACKAGE" == "yes" ] then echo "testing sudo" if sudo -E $true >/dev/null 2>&1 then echo "sudo accepts -E" SUDO="sudo -E" else echo "sudo does not accept param -E" if [ $(id -ur) != 0 ] then echo "error, must build as root" exit 1 else echo "I'm root, can continue" SUDO="" fi fi fi if [ "$OPT" == "yes" ]; then CONFIGURE="yes"; conf_flags="--disable-debug --disable-dbug"; fi if [ "$DEBUG" == "yes" ]; then CONFIGURE="yes"; fi if [ -n "$WITH_SPREAD" ]; then CONFIGURE="yes"; fi if [ "$CONFIGURE" == "yes" ] && [ "$SCONS" != "yes" ]; then SCRATCH="yes"; fi # Be quite verbose set -x # Build process base directory build_base=$(cd $(dirname $0)/..; pwd -P) get_arch() { if ! [ -z "$TARGET" ] then if [ "$TARGET" == "i686" ] then echo "i386" else echo "amd64" fi elif [ "$OS" == "Darwin" ]; then if file $build_base/gcs/src/gcs.o | grep "i386" >/dev/null 2>&1 then echo "i386" else echo "amd64" fi else if file $build_base/gcs/src/gcs.o | grep "80386" >/dev/null 2>&1 then echo "i386" else echo "amd64" fi fi } build_packages() { local PKG_DIR=$build_base/scripts/packages pushd $PKG_DIR local ARCH=$(get_arch) local WHOAMI=$(whoami) export BUILD_BASE=$build_base export GALERA_VER=$RELEASE if [ $DEBIAN -eq 0 ] && [ "$ARCH" == "amd64" ] then ARCH="x86_64" export x86_64=$ARCH # for epm fi export $ARCH local STRIP_OPT="" [ "$NO_STRIP" == "yes" ] && STRIP_OPT="-g" $SUDO rm -rf $ARCH set +e if [ $DEBIAN -ne 0 ]; then # build DEB $SUDO /usr/bin/epm -n -m "$ARCH" -a "$ARCH" -f "deb" \ --output-dir $ARCH $STRIP_OPT galera # && \ $SUDO /bin/chown -R $WHOAMI.users $ARCH elif [ "$OS" == "FreeBSD" ]; then if test "$NO_STRIP" != "yes"; then strip $build_base/{garb/garbd,libgalera_smm.so} fi ./freebsd.sh $GALERA_VER else # build RPM ./rpm.sh $GALERA_VER fi local RET=$? set -e popd if [ $DEBIAN -ne 0 ]; then mv -f $PKG_DIR/$ARCH/*.deb ./ elif [ "$OS" == "FreeBSD" ]; then mv -f $PKG_DIR/*.tbz ./ else mv -f $PKG_DIR/*.rpm ./ fi return $RET } build_source() { local module="$1" shift local build_dir="$build_base/$module" pushd $build_dir if [ ! -x "configure" ]; then ./bootstrap.sh; fi if [ ! -s "Makefile" ]; then ./configure; fi local src_base_name="lib${module}-" rm -rf "${src_base_name}"*.tar.gz make dist || (echo $?; echo "make dist failed"; echo) local ret=$(ls "${src_base_name}"*.tar.gz) popd echo $build_dir/$ret } build_sources() { local module local srcs="" for module in "galerautils" "gcomm" "gcs" "galera" do src=$(build_source $module | tail -n 1) srcs="$srcs $src" done local ret="galera-source-r$RELEASE.tar" tar --transform 's/.*\///' -cf $ret $srcs \ "source/README" "source/COPYING" "source/build.sh" # return absolute path for scripts echo $PWD/$ret } pushd "$build_base" #GALERA_REV="$(svnversion | sed s/\:/,/g)" #if [ "$GALERA_REV" == "exported" ] #then GALERA_REV=$(bzr revno --tree -q) || \ GALERA_REV=$(svn info >&/dev/null && svnversion | sed s/\:/,/g) || \ GALERA_REV=$(echo "XXXX") export GALERA_REV #fi popd #if [ -z "$RELEASE" ] #then # RELEASE=$GALERA_REV #fi if [ "$SCONS" == "yes" ] # Build using Scons then # Scons variant dir, defaults to GALERA_SRC export SCONS_VD=$build_base scons_args="-C $build_base revno=$GALERA_REV" [ -n "$TARGET" ] && scons_args="$scons_args arch=$TARGET" [ -n "$RELEASE" ] && scons_args="$scons_args version=$RELEASE" [ "$DEBUG" == "yes" ] && scons_args="$scons_args debug=$DEBUG_LEVEL" [ -n "$EXTRA_SYSROOT" ] && scons_args="$scons_args extra_sysroot=$EXTRA_SYSROOT" if [ "$SCRATCH" == "yes" ] then scons -Q -c --conf=force $scons_args $SCONS_OPTS fi if [ "$SKIP_BUILD" != "yes" ] then scons $scons_args -j $JOBS $SCONS_OPTS fi elif test "$SKIP_BUILD" == "no"; then # Build using autotools echo "Error: autotools not supported anymore! Nothing was built." exit 1 fi # SKIP_BUILD / SCONS if test "$PACKAGE" == "yes" then build_packages fi if test "$SOURCE" == "yes" then build_sources fi percona-xtradb-cluster-galera/scripts/mysql/0000755000000000000000000000000012247075736021506 5ustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/openrep/0000755000000000000000000000000012247075736022011 5ustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/packages/0000755000000000000000000000000012247075736022117 5ustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/source/0000755000000000000000000000000012247075736021641 5ustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/mysql/LICENSE0000644000000000000000000000006612247075736022515 0ustar rootroot00000000000000See ./mysql/LICENSE.mysql and ./galera/LICENSE.galera percona-xtradb-cluster-galera/scripts/mysql/LICENSE.mysql0000644000000000000000000004517712247075736023675 0ustar rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble ======== The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a. You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b. You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c. If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a. Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b. Accompany it with a written offer, valid for at least three years, to give any third-party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c. Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs ============================================= If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. ONE LINE TO GIVE THE PROGRAM'S NAME AND A BRIEF IDEA OF WHAT IT DOES. Copyright (C) YYYY NAME OF AUTHOR This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19YY NAME OF AUTHOR Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. SIGNATURE OF TY COON, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. percona-xtradb-cluster-galera/scripts/mysql/QUICK_START0000644000000000000000000000732312247075736023267 0ustar rootroot00000000000000MySQL/Galera Demo Quick Start Guide =================================== version 0.8.x This is an informal guide to quick setup of MySQL/Galera demo cluster for experienced MySQL users. For more detailed information please see README. MySQL/Galera cluster is a multi-master MySQL server cluster. It provides - True high-availability (no committed transaction is lost, no single point of failure) - Performance scalability even for write-intensive loads. - Semantics of a single MySQL server. This distribution is all you need to install and run MySQL/Galera cluster. You don't need to be root or to uninstall/disable any software. MYSQL CLUSTER IN 3 EASY STEPS: Suppose you have three networked computers Node1, Node2, Node3 of roughly the same performance and Node1 has IP address 192.168.1.1 1. Copy and unpack this distribution on each node to the directory of your choice (hereafter INSTALL_DIR). The location must have at least 1Gb of free space for database files. 2. Start first MySQL server on Node1: /mysql-galera -g gcomm:// start 3. Start remaining nodes: /mysql-galera -g gcomm://192.168.1.1 start NOTE1: First node of the cluster is started by supplying an empty gcomm address. When you want to add a node to a cluster, you must supply an address of any working cluster node. NOTE2: Beginning from the second node, each node you start will automatically copy the contents of the database from one of the working nodes. Allow some time for it to complete - it depends on the size of the database. mysql-galera script will return when copying is done and node is ready to use. See README for details. That's it. You've got a multi-master MySQL/Galera cluster. Node1, Node2 and Node3 will now synchronize all writes with the rest of the cluster. Root password is 'rootpass' and there is also 'test' user with password 'testpass' and privileges on 'test.*'. You can now populate your database with mysqldump by loading your database dump to (any) one of the nodes. LIMITATIONS: 1. Currently replication works only with InnoDB storage engine. Any writes to tables of other types, including system (mysql.*) tables are not replicated. (GRANT command is an exception and is replicated) 2. Rows in tables without primary keys may appear in different order on different nodes. As a result SELECT...LIMIT... may return slightly different sets. DELETE operation on such tables is not supported. 3. Unsupported in this release: commands: LOAD DATA may result in a transaction that is too big and will be rejected. features: LOCKed sessions and locking functions. Use at your own risk. QUICK ANSWERS: 1. Yes, it works anywhere TCP works, including Amazon EC2(TM). 2. Yes, it'd be faster if there were a dedicated network for replication connections and a separate network for MySQL client connections. 3. Yes, it is highly configurable and flexible. For more info see README, mysql-galera --help and my.cnf that comes with the distribution. 4. If your application cannot utilize more than one server, you can use TCP connection balancer like GLB (http://www.codership.com/products/downloads) or pen (http://siag.nu/pen/). Note however, that SQL traffic balancing is very CPU consuming (due to high number of small packets), so it is recommended to have a dedicated machine for load balancer. 5. The patch for MySQL can be obtained at https://code.launchpad.net/codership-mysql 6. Galera 0.8 sources can be obtained at https://code.launchpad.net/galera Please direct all your suggestions, opinions and inquiries to info@codership.com or (better) our mailing list at http://groups.google.com/group/codership-team percona-xtradb-cluster-galera/scripts/mysql/README0000644000000000000000000006067112247075736022400 0ustar rootroot00000000000000Codership Oy http://www.codership.com DISCLAIMER THIS SOFTWARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL CODERSHIP OY BE HELD LIABLE TO ANY PARTY FOR ANY DAMAGES RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE. Trademark Information. MySQL is a trademark or registered trademark of Sun Microsystems, Inc. or its subsidiaries in the US and other countries. Other marks are the property of their respective owners. Licensing Information. Please see ./mysql/LICENSE.mysql and ./galera/LICENSE.galera Project source code can be found at wsrep API: https://launchpad.net/wsrep/ MySQL patch: https://launchpad.net/codership-mysql/ Galera libs: https://launchpad.net/galera/ ABOUT THIS DOCUMENT This document covers issues specific to this MySQL/Galera demo distribution. It does not cover the use or administration of MySQL server per se. The reader is assumed to know how to install, configure, administer and use MySQL server. MYSQL/GALERA v23.x demo package CONTENTS: ========= 0. WHAT IS MYSQL/GALERA CLUSTER 1. CLUSTER SETUP 1.1 INSTALLATION 1.2 CLUSTER URL 1.3 STARTING THE FIRST NODE OF A CLUSTER 1.4 STARTING REMAINING NODES 2. USING THE CLUSTER 2.1 LOADING DATA TO A CLUSTER 2.2 CONNECTING APPLICATION TO A CLUSTER 2.3 LOAD BALANCER 2.4 ADDING NEW NODE TO A CLUSTER 2.5 A "REFERENCE NODE" 2.6 "SPLIT-BRAIN" CONDITION 3. CONFIGURATION 3.1 MANDATORY MYSQL OPTIONS 3.2 OPTIONAL OPTIMIZATIONS 3.3 WSREP OPTIONS 3.4 CONFIGURING LOCAL MYSQL CLIENTS 3.5 FIREWALLS 4. Using MySQL/Galera in Amazon EC2 5. LIMITATIONS 0. WHAT IS MYSQL/GALERA CLUSTER MySQL/Galera cluster is a synchronous multi-master MySQL server cluster. Cluster nodes are connected to each other in a N-to-N fashion through a group communication backend which provides automatic reconfiguration in the event of a node failure or a new node added to cluster: ,--------. ,--------. ,--------. | mysqld |----| mysqld |<---| client | `--------' `--------' `--------' \ / ,--------. ,--------. | mysqld |<---| client | `--------' `--------' With few exceptions each cluster node can be used like a standalone MySQL server and supports most of MySQL features including transactions, triggers and stored procedures. Node states are synchronized by replicating transaction changes at commit time. The cluster is virtually synchronous: this means that each node commits transactions in exactly the same order, although not necessarily at the same physical moment. (The latter is not that important as it may seem, since in most cases DBMS gives no guarantee on when the transaction is actually processed.) Built-in flow control keeps nodes within fraction of a second from each other, this is more than enough for most practical purposes. Main features of MySQL/Galera cluster: * Truly highly available: no committed transaction is ever lost in case of a node crash. All nodes always have consistent state. * True multi-master: all cluster nodes can modify the same table concurrently. * Highly transparent: the cluster is intended as a drop-in replacement for a single MySQL server. (See LIMITATIONS below) * Scalable even with WRITE-intensive applications. This demo distribution contains all software you'll need to setup MySQL/Galera cluster. It is essentially a self-contained MySQL server installation with its own configuration file, data directory and preconfigured empty database. You don't need administrative privileges or to uninstall/disable previously installed MySQL server to use this distribution. 1. CLUSTER SETUP To setup MySQL/Galera cluster you will need several networked computers - one for each mysqld instance you plan to use. For best performance those computers should be of approximately same configuration: Galera replication is synchronous and one overloaded machine may slow down the whole cluster. This however depends on load distribution. The node that does not handle client connections can be considerably slower. It takes 3 steps to set up the cluster: 1) Copy this distribution to all prospective nodes of the cluster and unpack it to location of your choice. 2) Start the first node to begin a new cluster. 3) Start remaining nodes pointing to the first one. (NOTE: You can easily set up the cluster on a single computer. However this makes little sense, as you won't see the the benefits of high availability and scalability. Hence it is not covered by this document.) 1.1 INSTALLATION Just copy and unpack the distribution on the prospective cluster nodes to wherever you have privileges. The distribution was designed to be able to run on most systems without reconfiguration. It is a self-contained MySQL installation and comes with its own data directory and a preconfigured empty database with users 'root' (password 'rootpass') and 'test' (password 'testpass', privileges on schema 'test'). As a result default installation will require at least 1Gb of free space for InnoDB files (will be created on first start). This requirement, as well as other MySQL and Galera options can be changed by editing configuration file which can be found at /mysql/etc/my.cnf. Please see CONFIGURATION chapter for the details on editable parameters. 1.2 CLUSTER URL Cluster URL is a connection handle that will be used by a new node to connect to the rest of the cluster. Its form is backend-specific and backend is determined by URL schema. Default is 'dummy' which means no replication. This demo comes with a distributed group communication backend which schema is 'gcomm'. 1.3 STARTING THE FIRST NODE OF A CLUSTER /mysql-galera is a special MySQL startup script that sets proper options (including data directory path) for mysqld. If you're running it as a superuser, you have to make sure there is 'mysql' user in the system and it has sufficient privileges on the installation directory (see MySQL Manual about running mysqld as root). The first node of a cluster has nowhere to connect to, therefore it has to start with an empty cluster address (note that it still initializes gcomm backend): /mysql-galera -g gcomm:// start 1.4 STARTING REMAINING NODES To add another node to the cluster it must be given the address of one of the existing cluster nodes. Thus, if the first cluster node has IP address 192.168.1.1, then the second will be started like this: /mysql-galera -g gcomm://192.168.1.1 start The third node can use either the first or the second node address and so on. It might take few minutes to start mysqld for the first time as it will have to create required InnoDB files. For full description of mysql-galera options and commands see: /mysql-galera --help 2. USING THE CLUSTER After you have successfully started all cluster nodes, the cluster is ready to use. From the client point of view each cluster node works like a usual MySQL server - client-side application does not have to be changed in any way. Each node can be accessed independently and asynchronously. Just direct SQL load to any one or more of the cluster nodes. For most practical purposes you can treat MySQL/Galera cluster as a single MySQL server listening on multiple interfaces with the exception that you might see transaction deadlocks where you previously didn't. 2.1 LOADING DATA TO CLUSTER Initially distribution database is empty. You can populate it by loading the dump of your data to any one of the nodes. It will be automatically replicated to others. Please note that this release supports only InnoDB storage engine. 2.2 CONNECTING APPLICATION TO CLUSTER As was mentioned above, for the client application each node looks like a normal MySQL server and can be used independently. This creates considerable flexibility in the way the cluster can be utilized. The approaches can be categorized in three groups: 1) Seeking High Availability only. It is similar to traditional MySQL master-slave replication. In this case client application connects to only one node, the rest serving as hot backups / read-only slaves: ,-------------. | application | `-------------' | | | DB backups/read-only slaves ,-------. ,-------. ,-------. | node1 | | node2 | | node3 | `-------' `-------' `-------' <===== cluster nodes =====> In the case of primary node failure application can instantly switch to another node without any preparations. This is also a most transparent mode: COMMITs will never return deadlocks and table locks can be used too. 2) Seeking High Availability and improved performance through uniform load distribution. If there are several client connections to the database, they can be uniformly distributed between cluster nodes resulting in better performance. The exact degree of performance improvement depends on application's SQL profile. Note, that transaction rollback rate may also increase. ,-------------. | clients | `-------------' | | | | ,-------------. | application | `-------------' / | \ ,-------. ,-------. ,-------. | node1 | | node2 | | node3 | `-------' `-------' `-------' <===== cluster nodes =====> In the case of a node failure application can keep on using the remaining healthy nodes. In this setup application can also be clustered with a dedicated application instance per database node, thus achieving HA not only for the database, but for the whole application stack: ,-------------. | clients | `-------------' // || \\ ,------. ,------. ,------. | app1 | | app2 | | app3 | `------' `------' `------' | | | ,-------. ,-------. ,-------. | node1 | | node2 | | node3 | `-------' `-------' `-------' <====== cluster nodes ======> 3) Seeking High Availability and improved performance through smart load distribution. Uniform load distribution can cause undesirably high rollback rate. Directing transactions which access the same set of tables to the same node can considerably improve performance by reducing the number of rollbacks. Also, if your application can distinguish between read/write and read-only transactions, the following configuration may be quite efficient: ,---------------------. | application | `---------------------' writes / | reads \ reads ,-------. ,-------. ,-------. | node1 | | node2 | | node3 | `-------' `-------' `-------' <========= cluster nodes =========> 2.3 LOAD BALANCER If your application cannot utilize several database servers (most don't) you will need to use SQL proxy or a TCP load balancer to distribute load between the MySQL/Galera nodes. This is needed not only to increase performance, but also for a quick switch in case of a node failure. If performance of your application is DBMS-bound, you can run the balancer on the same machine as application/client. Be aware, however, that SQL load balancing might be a CPU hungry operation: usually SQL traffic consists of many small packets. For best results we recommend to carefully examine CPU consumption of the balancer and if needed dedicate a separate machine for it. Unlike traditional MySQL master-slave cluster, MySQL/Galera cluster does not require any SQL traffic filtering, it is highly transparent and plain TCP connection balancer will suffice. TCP connection balancers that were successfully used with MySQL/Galera: - Pen (http://siag.nu/pen/) - GLB (http://www.codership.com/en/downloads/glb) 2.4 ADDING NEW NODE TO A CLUSTER With 0.7 series of releases Codership removes the main obstacle towards using MySQL/Galera in production: inability to add/replace nodes in the working cluster. This distribution features automatic state snapshot transfer to newly joined node. Until node receives state snapshot it won't execute any queries. Detailed state snapshot transfer sequence diagram can be found in http://www.codership.com/files/presentations/Galera_OSC_2009.pdf The process of joining new node into the cluster consists of two phases: 1) State snapshot transfer (SST) from an established cluster member. Depending on the SST method neither joining node, nor SST donor can apply any transactions for the duration of this phase. Transactions replicated by other nodes are buffered in the queue. 2) Catch-up phase when both donor and joiner try to catch up with the cluster by applying transactions buffered in the queue. Using them as working nodes should be avoided. Duration of this phase depends on the load profile and duration of the first phase. NOTE: Transaction buffering is currently happening in memory, so prepare enough swap space. By default cluster chooses the most suitable node to receive state transfer from. There is also an option wsrep_sst_donor to specify desired state snapshot source in my.cnf or on the command line. See CONFIGURATION section for descriptions of all relevant configuration options. In most situations (like cluster on EC2) this distribution should work with default settings. At this point there is only one state transfer method supported and it is based on mysqldump. Although it is relatively slow, it provides complete cloning of the donor state, including system tables and thus is most compliant. 2.5 A "REFERENCE NODE" For practical purposes we recommend to reserve a "reference node" in the cluster. A "reference node" is a node that does not receive SQL load. Having such node in a cluster serves several purposes: 1) Data consistency: since this node does not process any SQL load on its own, it has the lowest probability of transaction conflicts and therefore - indeterministic conflict resolution. In the event of discovered database inconsistencies in the cluster this node will have the most relevant database. 2) Data safety: since this node does not process any SQL load on its own, it has the lowest probability of failing with catastrophic consequences. In the event of total cluster failure (e.g. blackout) this will be the best node to restore cluster from. 3) High availability: a reference node can serve as a dedicated state snapshot donor. Since it does not serve any clients, they won't experience service interruptions and load balancer won't need reconfiguration during SST. Even with the current TCP-based group communication the overhead of having one extra silent node is negligible for most loads. 2.6 "SPLIT-BRAIN" CONDITION Galera cluster is fully distributed and does not use any sort of centralized arbitrator, thus having no single point of failure. However, like any cluster of that kind it may fall to a dreaded "split-brain" condition where half or more nodes of the cluster suddenly disappear (e.g. due to network failure). In general case, having no information about the fate of disappeared nodes, remaining nodes cannot continue to process requests and modify their states. While such situation is generally considered negligibly probable in a multi-node cluster (normally nodes fail one by one), in 2-node cluster a single node failure can lead to this, thus making 3 nodes a minimum requirement for a highly-available cluster. Dedicated Galera packages (not this distribution) contain a lightweight "arbitrator" daemon which can serve as an odd node substitute in situations where cluster size is limited to 2 real nodes. 3. CONFIGURATION Each MySQL/Galera node is configured just like the usual MySQL server, we just added some configuration variables to my.cnf. In addition some options can be passed to mysql-galera startup script (see mysql-galera --help). 3.1 MANDATORY MYSQL OPTIONS binlog_format=ROW This option is required to use row-level replication as opposed to statement-level. For performance and consistency considerations don't change that. As a side effect, binlog, if turned on, can be ROW only. In future this option won't have special meaning. innodb_autoinc_lock_mode=2 This is a required parameter. Without it INSERTs into tables with AUTO_INCREMENT column may fail. autoinc lock modes 0 and 1 can cause unresolved deadlock, and make system unresponsive. innodb_locks_unsafe_for_binlog=1 This setting is required for relaiable parallel applying operation. Mandatory options are hardcoded both in distribution's my.cnf file and in mysql-galera script. 3.2 OPTIONAL OPTIMIZATIONS While not required for correct operation of MySQL/Galera cluster, the following options may be safely set due to the guarantees of synchronous replication: innodb_flush_log_at_trx_commit=0 3.3 WSREP OPTIONS Here WSREP stands for Write-Set REPlication - a synchronous replication API that Codership is developing for transactional databases. Galera is a library that implements WSREP services. Default values are shown. All options are optional except for wsrep_provider and wsrep_cluster_address. wsrep_provider=none A full path to the library that implements WSREP interface. If none is specified, the server behaves almost like a normal mysqld, with slight overhead. mysql-galera script automatically substitutes it to point to Galera implementation shipped with the distribution. It can be overridden with WSREP environment variable. wsrep_provider_options= Provider-specific option string. See http://www.codership.com/wiki for details. wsrep_cluster_address="dummy://" Group Communication System address. Depends on the WSREP provider. This distribution recognizes "dummy://" and "gcomm://
[:port]" Default port is 4567. mysql> set global wsrep_cluster_address=
; will (re)establish connection to ADDRESS. This can be used to change cluster connection in runtime. wsrep_cluster_name="my_wsrep_cluster" Logical cluster name, must be the same for all nodes of the cluster. wsrep_node_name= Human readable node name. Defaults to hostname. wsrep_slave_threads=1 Number of threads dedicated to processing of writesets from other nodes. For better performance we recommend few per CPU core. wsrep_dbug_option Options for the built-in DBUG library (independent from what MySQL uses). Empty by default. Not used in 0.8. wsrep_debug=0 Enable debug-level logging. wsrep_convert_LOCK_to_trx=0 Implicitly convert locking sessions into transactions inside mysqld. By itself it does not mean support for locking sessions, but it prevents the database from going into logically inconsistent state. Disabled by default because of possible memory issues with DB dumps that contain LOCK statements. wsrep_retry_autocommit=1 Retry autocommit queries and single statement transactions should they fail certification test. This is analogous to rescheduling an autocommit query should it go into deadlock with other transactions in the database lock manager. wsrep_auto_increment_control=1 Automatically adjust auto_increment_increment and auto_increment_offset variables based on the number of nodes in the cluster. Significantly reduces certification conflic rate for INSERTS. wsrep_drupal_282555_workaround=1 MySQL seems to have an obscure bug when INSERT into table with AUTO_INCREMENT column with NULL value for that column can fail with a duplicate key error. When this option is on, it retries such INSERTs. Required for stable Drupal operation. Documented at: http://bugs.mysql.com/bug.php?id=41984 http://drupal.org/node/282555 wsrep_sst_method=mysqldump What method to use to copy database state to a newly joined node. Currently supported methods: - mysqldump: generally slow (except on small datasets), but most tested - rsync: the fastest method, especially on large datasets - rsync_wan: same as rsync, but uses deltaxfer to minimize network traffic. wsrep_sst_receive_address= Address at which this node wants to receive state snapshot. Defaults to mysqld bind address, and if that is not specified (0.0.0.0) - to the first IP of eth0 + mysqld bind port. wsrep_sst_auth= Authentication information needed for state transfer. Depends on the state transfer method. For mysqldump-based SST it is : and should be the same on all nodes - it is used to authenticate with both state snapshot receiver and state snapshot donor. In this distribution it is preconfigured to "root:rootpass". wsrep_sst_donor= A name of the node which should serve as state snapshot donor. This allows to control which node will serve state snapshot request. By default the most suitable node is chosen by GCS. 3.4 CONFIGURING LOCAL MYSQL CLIENTS This MySQL/Galera distribution runs mysqld in the "sandbox". Thus mysql clients won't find mysqld socket at default system location. Running mysql client without explicitly specifying socket or port (via --socket or --host/--port options) may, therefore, result in the following: $ mysql -uroot -prootpass ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2) Most applications that use libmysqlclient to connect to MySQL server can be instructed to look in the correct place by adding a following section to system-wide my.cnf file: [client] socket = /mysql/var/mysqld.sock 3.5 FIREWALLS If there are any firewalls used, they should be configured to allow connections between the nodes at the following ports: 3306 - for mysqldump state snapshot transfer 4567 - for replication traffic E.g. to configure iptables to allow connections from a local subnet: iptables -A INPUT -i eth0 -p tcp -m tcp \ --source 192.168.0.1/24 --dport 3306 -j ACCEPT iptables -A INPUT -i eth0 -p tcp -m tcp \ --source 192.168.0.1/24 --dport 4567 -j ACCEPT Substitute real values for IP address of your node and netmask. Better yet, use VPN. 4. Using MySQL/Galera distribution in Amazon EC2 MySQL/Galera works anywhere TCP/IP works. Therefore using MySQL/Galera distribution in Amazon EC2 environment is no different than in LAN. Just launch several instances of your favorite AMI, copy and unpack the distribution, and start the servers. Don't forget to use external addresses if your nodes are running in different accessibility zones (obviously running in different accessibility zones degrades performance somewhat). NOTE: this distribution may be binary incompatible with some older Linux distributions. Please use CentOS 5.0 or newer. 5. LIMITATIONS 1) Currently replication works only with InnoDB storage engine. Any writes to tables of other types, including system (mysql.*) tables are not replicated. However, DDL statements are replicated in statement level, and changes to mysql.* tables will get replicated that way. So, you can safely issue: CREATE USER..., but issuing: INSERT INTO mysql.user..., will not be replicated. 2) DELETE operation is not supported on tables without primary keys. Rows in tables without primary keys may appear in different order on different nodes. As a result SELECT...LIMIT... may return slightly different sets. 3) Unsupported queries: * LOAD DATA size is limited to ~1Gb * lock functions (GET_LOCK(), RELEASE_LOCK()... ) 4) Locking sessions (LOCK TABLES...UNLOCK) are not supported in multi-master mode. However if there's only one node that executes locking sessions, then it'll work. 5) Transaction isolation level should be REPEATABLE_READ (the default). Galera implements implicit snapshot isolation for cluster wide transactions. 6) Due to cluster level optimistic concurrency control, transaction issuing COMMIT may still be aborted at that stage. There can be two transactions writing to same rows and committing in separate cluster nodes, and only one of the them can successfully commit. The failing one, will be aborted. For cluster level aborts, MySQL/galera cluster gives back deadlock error code (Error: 1213 SQLSTATE: 40001 (ER_LOCK_DEADLOCK)). 7) Query log cannot be directed to table. If you enable query logging, you must forward the log to a file: log_output = FILE Use general_log and general_log_file to choose query logging and the log file name 8) XA transactions can not be supported due to possible rollback on commit. percona-xtradb-cluster-galera/scripts/mysql/build.sh0000755000000000000000000006266012247075736023156 0ustar rootroot00000000000000#!/bin/bash -ex if test -z "$MYSQL_SRC" then echo "MYSQL_SRC variable pointing at MySQL/wsrep sources is not set. Can't continue." exit -1 fi use_mysql_5.1_sources() { MYSQL_MAJOR="5.1" export MYSQL_MAJOR # for DEB build export MYSQL_MAJOR_VER="5" export MYSQL_MINOR_VER="1" MYSQL_VER=`grep AC_INIT $MYSQL_SRC/configure.in | awk -F '[' '{ print $3 }' | awk -F ']' '{ print $1 }'` } use_mariadb_5.1_sources() { use_mysql_5.1_sources } use_mysql_5.5_sources() { export MYSQL_MAJOR_VER=`grep MYSQL_VERSION_MAJOR $MYSQL_SRC/VERSION | cut -d = -f 2` export MYSQL_MINOR_VER=`grep MYSQL_VERSION_MINOR $MYSQL_SRC/VERSION | cut -d = -f 2` export MYSQL_PATCH_VER=`grep MYSQL_VERSION_PATCH $MYSQL_SRC/VERSION | cut -d = -f 2` MYSQL_MAJOR=$MYSQL_MAJOR_VER.$MYSQL_MINOR_VER export MYSQL_MAJOR # for DEB build MYSQL_VER=$MYSQL_MAJOR.$MYSQL_PATCH_VER } if test -f "$MYSQL_SRC/configure.in" then use_mysql_5.1_sources elif test -f "$MYSQL_SRC/VERSION" then use_mysql_5.5_sources else echo "Unknown MySQL version in MYSQL_SRC path. Versions 5.1 and 5.5 are supported. Can't continue." exit -1 fi # Initializing variables to defaults uname -m | grep -q i686 && CPU=pentium || CPU=amd64 # this works for x86 Solaris too BOOTSTRAP=no DEBUG=no DEBUG_LEVEL=1 GALERA_DEBUG=no NO_STRIP=no RELEASE="" TAR=no BIN_DIST=no PACKAGE=no INSTALL=no CONFIGURE=no SKIP_BUILD=no SKIP_CONFIGURE=no SKIP_CLIENTS=no SCRATCH=no SCONS="yes" JOBS=1 GCOMM_IMPL=${GCOMM_IMPL:-"galeracomm"} TARGET="" MYSQLD_BINARY="mysqld" OS=$(uname) case "$OS" in "Linux") JOBS=$(grep -c ^processor /proc/cpuinfo) ;; "SunOS") JOBS=$(psrinfo | wc -l) ;; "Darwin" | "FreeBSD") JOBS="$(sysctl -n hw.ncpu)" ;; *) echo "CPU information not available: unsupported OS: '$OS'";; esac if [ "$OS" == "FreeBSD" ]; then CC=${CC:-"gcc48"} CXX=${CXX:-"g++48"} LD_LIBRARY_PATH=${LD_LIBRARY_PATH:-"/usr/local/lib/$(basename $CC)"} else CC=${CC:-"gcc"} CXX=${CXX:-"g++"} fi CC=${CC:+$(which "$CC" 2>/dev/null)} CXX=${CXX:+$(which "$CXX" 2>/dev/null)} export CC CXX LD_LIBRARY_PATH EXTRA_SYSROOT=${EXTRA_SYSROOT:-""} if [ "$OS" == "Darwin" ]; then if which -s port && test -x /opt/local/bin/port; then EXTRA_SYSROOT=/opt/local elif which -s brew && test -x /usr/local/bin/brew; then EXTRA_SYSROOT=/usr/local elif which -s fink && test -x /sw/bin/fink; then EXTRA_SYSROOT=/sw fi elif [ "$OS" == "FreeBSD" ]; then EXTRA_SYSROOT=/usr/local fi usage() { cat < --last-stage -s|--scratch build everything from scratch -c|--configure reconfigure the build system (implies -s) -b|--bootstap rebuild the build system (implies -c) -o|--opt configure build with debug disabled (implies -c) -m32/-m64 build 32/64-bit binaries on x86 -d|--debug configure build with debug enabled (implies -c) -dl|--debug-level set debug level (1, implies -c) --with-spread configure build with Spread (implies -c) --no-strip prevent stripping of release binaries -j|--jobs number of parallel compilation jobs (${JOBS}) -p|--package create DEB/RPM packages (depending on the distribution) --bin create binary tar package -t|--tar create a demo test distribution --sb|--skip-build skip the actual build, use the existing binaries --sc|--skip-configure skip configure --skip-clients don't include client binaries in test package --scons use scons to build galera libraries (yes) -r|--release , otherwise revisions will be used -s and -b options affect only Galera build. EOF } # Parse command line while test $# -gt 0 do case $1 in -b|--bootstrap) BOOTSTRAP="yes" # Bootstrap the build system CONFIGURE="yes" ;; --bin) BIN_DIST="yes" ;; -c|--configure) CONFIGURE="yes" # Reconfigure the build system ;; -s|--scratch) SCRATCH="yes" # Build from scratch (run make clean) ;; -o|--opt) OPT="yes" # Compile without debug ;; -d|--debug) DEBUG="yes" # Compile with debug NO_STRIP="yes" # Don't strip the binaries ;; --dl|--debug-level) shift; DEBUG_LEVEL=$1 ;; --gd|--galera-debug) GALERA_DEBUG="yes" ;; -r|--release) RELEASE="$2" # Compile without debug shift ;; -t|--tar) TAR="yes" # Create a TGZ package ;; -i|--install) INSTALL="yes" ;; -p|--package) PACKAGE="yes" # Create a DEB package CONFIGURE="yes" # don't forget to reconfigure with --prefix=/usr ;; -j|--jobs) shift; JOBS=$1 ;; --no-strip) NO_STRIP="yes" # Don't strip the binaries ;; --with*-spread) WITH_SPREAD="$1" ;; -m32) CFLAGS="$CFLAGS -m32" CXXFLAGS="$CXXFLAGS -m32" CONFIGURE="yes" CPU="pentium" TARGET="i686" ;; -m64) CFLAGS="$CFLAGS -m64" CXXFLAGS="$CXXFLAGS -m64" CONFIGURE="yes" CPU="amd64" TARGET="x86_64" ;; --sb|--skip-build) SKIP_BUILD="yes" ;; --sc|--skip-configure) SKIP_CONFIGURE="yes" ;; --skip-clients) SKIP_CLIENTS="yes" ;; --scons) SCONS="yes" ;; --help) usage exit 0 ;; *) echo "Unrecognized option: $1" usage exit 1 ;; esac shift done if [ "$PACKAGE" == "yes" -a "$OS" == "Linux" ] then # check whether sudo accepts -E to preserve environment echo "testing sudo" if sudo -E $(which epm) --version >/dev/null 2>&1 then echo "sudo accepts -E" SUDO_ENV="sudo -E" SUDO="sudo" else echo "sudo does not accept param -E" if [ $(id -ur) != 0 ] then echo "error, must build as root" exit 1 else SUDO_ENV="" SUDO="" echo "I'm root, can continue" fi fi # If packaging with epm, make sure that mysql user exists in build system to # get file ownerships right. echo "Checking for mysql user and group for epm:" getent passwd mysql >/dev/null if [ $? != 0 ] then echo "Error: user 'mysql' does not exist" exit 1 else echo "User 'mysql' ok" fi getent group mysql >/dev/null if [ $? != 0 ] then echo "Error: group 'mysql' doest not exist" exit 1 else echo "Group 'mysql' ok" fi fi if [ "$OPT" == "yes" ]; then CONFIGURE="yes"; fi if [ "$DEBUG" == "yes" ]; then CONFIGURE="yes"; MYSQLD_BINARY="mysqld-debug"; fi if [ "$INSTALL" == "yes" ]; then TAR="yes"; fi if [ "$SKIP_BUILD" == "yes" ]; then CONFIGURE="no"; fi which dpkg >/dev/null 2>&1 && DEBIAN=1 || DEBIAN=0 # export command options for Galera build export BOOTSTRAP CONFIGURE SCRATCH OPT DEBUG WITH_SPREAD CFLAGS CXXFLAGS \ PACKAGE CPU TARGET SKIP_BUILD RELEASE DEBIAN SCONS JOBS DEBUG_LEVEL set -eu # Absolute path of this script folder BUILD_ROOT=$(cd $(dirname $0); pwd -P) GALERA_SRC=${GALERA_SRC:-$BUILD_ROOT/../../} # Source paths are either absolute or relative to script, get absolute MYSQL_SRC=$(cd $MYSQL_SRC; pwd -P; cd $BUILD_ROOT) GALERA_SRC=$(cd $GALERA_SRC; pwd -P; cd $BUILD_ROOT) if [ "$MYSQL_MAJOR" = "5.1" ] then MYSQL_BUILD_DIR="$MYSQL_SRC" else [ "$DEBUG" == "yes" ] \ && MYSQL_BUILD_DIR="$MYSQL_SRC/build_debug" \ || MYSQL_BUILD_DIR="$MYSQL_SRC/build_release" fi ###################################### ## ## ## Build Galera ## ## ## ###################################### # Also obtain SVN revision information if [ "$TAR" == "yes" -o "$BIN_DIST" == "yes" ] then cd $GALERA_SRC debug_opt="" if [ $GALERA_DEBUG == "yes" ] then debug_opt="-d" fi scripts/build.sh $debug_opt # options are passed via environment variables GALERA_REV=$(bzr revno --tree -q) || \ GALERA_REV=$(svn info >&/dev/null && svnversion | sed s/\:/,/g) || \ GALERA_REV=$(echo "XXXX") fi ###################################### ## ## ## Build MySQL ## ## ## ###################################### # Obtain MySQL version and revision number cd $MYSQL_SRC WSREP_REV=$(bzr revno --tree -q) || \ WSREP_REV="XXXX" # this does not work on an unconfigured source MYSQL_VER=$(grep '#define VERSION' $MYSQL_SRC/include/config.h | sed s/\"//g | cut -d ' ' -f 3 | cut -d '-' -f 1-2) if [ "$PACKAGE" == "yes" ] || [ "$BIN_DIST" == "yes" ] then # fetch and patch pristine sources cd ${TMPDIR:-/tmp} mysql_tag=mysql-$MYSQL_VER if [ "$SKIP_BUILD" == "no" ] || [ ! -d $mysql_tag ] then mysql_orig_tar_gz=$mysql_tag.tar.gz url2=http://ftp.sunet.se/pub/unix/databases/relational/mysql/Downloads/MySQL-$MYSQL_MAJOR url1=ftp://sunsite.informatik.rwth-aachen.de/pub/mirror/www.mysql.com/Downloads/MySQL-$MYSQL_MAJOR if [ ! -r $mysql_orig_tar_gz ] then echo "Downloading $mysql_orig_tar_gz... currently works only for 5.1.x" wget -N $url1/$mysql_orig_tar_gz || wget -N $url2/$mysql_orig_tar_gz fi echo "Getting wsrep patch..." patch_file=$(${BUILD_ROOT}/get_patch.sh $mysql_tag $MYSQL_SRC) echo "Patching source..." rm -rf $mysql_tag # need clean sources for a patch tar -xzf $mysql_orig_tar_gz cd $mysql_tag/ patch -p1 -f < $patch_file >/dev/null || : # chmod a+x ./BUILD/*wsrep CONFIGURE="yes" else cd $mysql_tag/ fi MYSQL_SRC=$(pwd -P) MYSQL_BUILD_DIR=$MYSQL_SRC if [ "$CONFIGURE" == "yes" ] then echo "Regenerating config files" time ./BUILD/autorun.sh fi fi echo "Building mysqld" export WSREP_REV export MAKE="make -j$JOBS" if [ "$SKIP_BUILD" == "no" ] then if [ "$CONFIGURE" == "yes" ] && [ "$SKIP_CONFIGURE" == "no" ] then rm -f config.status BUILD_OPT="" if [ "$OS" == "FreeBSD" ]; then # don't use INSTALL_LAYOUT=STANDALONE(default), it assumes prefix=. CMAKE_LAYOUT_OPTIONS=( -DCMAKE_INSTALL_PREFIX="/usr/local" \ -DINSTALL_LAYOUT=RPM \ -DMYSQL_UNIX_ADDR="/tmp/mysql.sock" \ -DINSTALL_BINDIR="bin" \ -DINSTALL_DOCDIR="share/doc/mysql" \ -DINSTALL_DOCREADMEDIR="share/doc/mysql" \ -DINSTALL_INCLUDEDIR="include/mysql" \ -DINSTALL_INFODIR="info" \ -DINSTALL_LIBDIR="lib/mysql" \ -DINSTALL_MANDIR="man" \ -DINSTALL_MYSQLDATADIR="/var/db/mysql" \ -DINSTALL_MYSQLSHAREDIR="share/mysql" \ -DINSTALL_MYSQLTESTDIR="share/mysql/tests" \ -DINSTALL_PLUGINDIR="lib/mysql/plugin" \ -DINSTALL_SBINDIR="libexec" \ -DINSTALL_SCRIPTDIR="bin" \ -DINSTALL_SHAREDIR="share" \ -DINSTALL_SQLBENCHDIR="share/mysql" \ -DINSTALL_SUPPORTFILESDIR="share/mysql" \ -DWITH_UNIT_TESTS=0 \ -DWITH_LIBEDIT=0 \ -DWITH_LIBWRAP=1 \ ) else [ $DEBIAN -ne 0 ] && \ MYSQL_SOCKET_PATH="/var/run/mysqld/mysqld.sock" || \ MYSQL_SOCKET_PATH="/var/lib/mysql/mysql.sock" CMAKE_LAYOUT_OPTIONS=( \ -DINSTALL_LAYOUT=RPM \ -DCMAKE_INSTALL_PREFIX="/usr" \ -DINSTALL_SBINDIR="/usr/sbin" \ -DMYSQL_DATADIR="/var/lib/mysql" \ -DMYSQL_UNIX_ADDR=$MYSQL_SOCKET_PATH \ -DCMAKE_OSX_ARCHITECTURES=$(uname -m) \ ) fi [ -n "$EXTRA_SYSROOT" ] && \ CMAKE_LAYOUT_OPTIONS+=( \ -DCMAKE_PREFIX_PATH="$EXTRA_SYSROOT" \ ) if [ $MYSQL_MAJOR = "5.1" ] then # This will be put to --prefix by SETUP.sh. export MYSQL_BUILD_PREFIX="/usr" export wsrep_configs="--libexecdir=/usr/sbin \ --localstatedir=/var/lib/mysql/ \ --with-unix-socket-path=$MYSQL_SOCKET_PATH \ --with-extra-charsets=all \ --with-ssl" [ "$DEBUG" = "yes" ] && BUILD_OPT="-debug" BUILD/compile-${CPU}${BUILD_OPT}-wsrep > /dev/null else # CMake build [ "$DEBUG" = "yes" ] \ && BUILD_OPT="-DCMAKE_BUILD_TYPE=Debug" \ || BUILD_OPT="-DCMAKE_BUILD_TYPE=RelWithDebInfo" # like in RPM spec [ "$MYSQL_MAJOR_VER$MYSQL_MINOR_VER" -ge "56" ] \ && MEMCACHED_OPT="-DWITH_LIBEVENT=yes -DWITH_INNODB_MEMCACHED=ON" \ || MEMCACHED_OPT="" if [ "$MYSQL_BUILD_DIR" != "$MYSQL_SRC" ] then [ "$BOOTSTRAP" = "yes" ] && rm -rf $MYSQL_BUILD_DIR [ -d "$MYSQL_BUILD_DIR" ] || mkdir -p $MYSQL_BUILD_DIR fi pushd $MYSQL_BUILD_DIR cmake \ ${CC:+-DCMAKE_C_COMPILER="$CC"} \ ${CXX:+-DCMAKE_CXX_COMPILER="$CXX"} \ -DBUILD_CONFIG=mysql_release \ "${CMAKE_LAYOUT_OPTIONS[@]}" \ $BUILD_OPT \ -DWITH_WSREP=1 \ -DWITH_EXTRA_CHARSETS=all \ -DWITH_SSL=yes \ -DWITH_ZLIB=system \ $MEMCACHED_OPT \ $MYSQL_SRC \ && make -S && popd || exit 1 fi else # just recompile and relink with old configuration [ $MYSQL_MAJOR != "5.1" ] && pushd $MYSQL_BUILD_DIR make -S > /dev/null [ $MYSQL_MAJOR != "5.1" ] && popd fi fi # SKIP_BUILD # gzip manpages # this should be rather fast, so we can repeat it every time if [ "$PACKAGE" == "yes" ] then cd $MYSQL_SRC/man && for i in *.1 *.8; do gzip -c $i > $i.gz; done || : fi ###################################### ## ## ## Making of demo tarball ## ## ## ###################################### install_mysql_5.1_demo() { MYSQL_LIBS=$MYSQL_DIST_DIR/lib/mysql MYSQL_PLUGINS=$MYSQL_DIST_DIR/lib/mysql/plugin MYSQL_CHARSETS=$MYSQL_DIST_DIR/share/mysql/charsets # BSD-based OSes does not have -D option on 'install' install -m 755 -d $MYSQL_DIST_DIR/share/mysql/english install -m 644 $MYSQL_SRC/sql/share/english/errmsg.sys $MYSQL_DIST_DIR/share/mysql/english/errmsg.sys install -m 755 -d $MYSQL_DIST_DIR/sbin install -m 755 $MYSQL_SRC/sql/mysqld $MYSQL_DIST_DIR/sbin/mysqld if [ "$SKIP_CLIENTS" == "no" ] then # Hack alert: # install libmysqlclient.so as libmysqlclient.so.16 as client binaries # seem to be linked against explicit version. Figure out better way to # deal with this. install -m 755 -d $MYSQL_LIBS install -m 755 $MYSQL_SRC/libmysql/.libs/libmysqlclient.so $MYSQL_LIBS/libmysqlclient.so.16 fi if test -f $MYSQL_SRC/storage/innodb_plugin/.libs/ha_innodb_plugin.so then install -m 755 -d $MYSQL_PLUGINS install -m 755 $MYSQL_SRC/storage/innodb_plugin/.libs/ha_innodb_plugin.so \ $MYSQL_PLUGINS/ha_innodb_plugin.so fi install -m 755 -d $MYSQL_BINS install -m 644 $MYSQL_SRC/sql/share/english/errmsg.sys $MYSQL_DIST_DIR/share/mysql/english/errmsg.sys install -m 755 -d $MYSQL_DIST_DIR/sbin install -m 755 $MYSQL_SRC/sql/mysqld $MYSQL_DIST_DIR/sbin/mysqld if [ "$SKIP_CLIENTS" == "no" ] then # Hack alert: # install libmysqlclient.so as libmysqlclient.so.16 as client binaries # seem to be linked against explicit version. Figure out better way to # deal with this. install -m 755 -d $MYSQL_LIBS install -m 755 $MYSQL_SRC/libmysql/.libs/libmysqlclient.so $MYSQL_LIBS/libmysqlclient.so.16 fi if test -f $MYSQL_SRC/storage/innodb_plugin/.libs/ha_innodb_plugin.so then install -m 755 -d $MYSQL_PLUGINS install -m 755 $MYSQL_SRC/storage/innodb_plugin/.libs/ha_innodb_plugin.so \ $MYSQL_PLUGINS/ha_innodb_plugin.so fi install -m 755 -d $MYSQL_BINS if [ "$SKIP_CLIENTS" == "no" ] then if [ -x $MYSQL_SRC/client/.libs/mysql ] # MySQL then MYSQL_CLIENTS=$MYSQL_SRC/client/.libs elif [ -x $MYSQL_SRC/client/mysql ] # MariaDB then MYSQL_CLIENTS=$MYSQL_SRC/client else echo "Can't find MySQL clients. Aborting." exit 1 fi install -m 755 -s -t $MYSQL_BINS $MYSQL_CLIENTS/mysql install -m 755 -s -t $MYSQL_BINS $MYSQL_CLIENTS/mysqldump install -m 755 -s -t $MYSQL_BINS $MYSQL_CLIENTS/mysqladmin fi install -m 755 -t $MYSQL_BINS $MYSQL_SRC/scripts/wsrep_sst_common install -m 755 -t $MYSQL_BINS $MYSQL_SRC/scripts/wsrep_sst_mysqldump install -m 755 -t $MYSQL_BINS $MYSQL_SRC/scripts/wsrep_sst_rsync install -m 755 -d $MYSQL_CHARSETS install -m 644 -t $MYSQL_CHARSETS $MYSQL_SRC/sql/share/charsets/*.xml install -m 644 -t $MYSQL_CHARSETS $MYSQL_SRC/sql/share/charsets/README } install_mysql_5.5_dist() { export DESTDIR=$BUILD_ROOT/dist/mysql mkdir -p $DESTDIR pushd $MYSQL_BUILD_DIR make install popd unset DESTDIR } install_mysql_5.5_demo() { export DESTDIR=$BUILD_ROOT/dist/mysql mkdir -p $DESTDIR pushd $MYSQL_BUILD_DIR cmake -DCMAKE_INSTALL_COMPONENT=Server -P cmake_install.cmake cmake -DCMAKE_INSTALL_COMPONENT=Client -P cmake_install.cmake cmake -DCMAKE_INSTALL_COMPONENT=SharedLibraries -P cmake_install.cmake cmake -DCMAKE_INSTALL_COMPONENT=ManPages -P cmake_install.cmake [ "$DEBUG" == "yes" ] && cmake -DCMAKE_INSTALL_COMPONENT=Debuginfo -P cmake_install.cmake popd unset DESTDIR pushd $MYSQL_DIST_DIR [ -d usr/local ] && ( mv usr/local/* ./ && rmdir usr/local ) # FreeBSD [ -d libexec -a ! -d sbin ] && mv libexec sbin # FreeBSD mv usr/* ./ && rmdir usr [ -d lib64 -a ! -d lib ] && mv lib64 lib popd } if [ $TAR == "yes" ]; then echo "Creating demo distribution" # Create build directory structure DIST_DIR=$BUILD_ROOT/dist MYSQL_DIST_DIR=$DIST_DIR/mysql MYSQL_DIST_CNF=$MYSQL_DIST_DIR/etc/my.cnf GALERA_DIST_DIR=$DIST_DIR/galera MYSQL_BINS=$MYSQL_DIST_DIR/bin cd $BUILD_ROOT rm -rf $DIST_DIR # Install required MySQL files in the DIST_DIR if [ $MYSQL_MAJOR == "5.1" ]; then install_mysql_5.1_demo install -m 755 -d $(dirname $MYSQL_DIST_CNF) install -m 644 my-5.1.cnf $MYSQL_DIST_CNF else install_mysql_5.5_demo > /dev/null install -m 755 -d $(dirname $MYSQL_DIST_CNF) install -m 644 my-5.5.cnf $MYSQL_DIST_CNF fi cat $MYSQL_BUILD_DIR/support-files/wsrep.cnf | \ sed 's/root:$/root:rootpass/' >> $MYSQL_DIST_CNF pushd $MYSQL_BINS; ln -s wsrep_sst_rsync wsrep_sst_rsync_wan; popd tar -xzf mysql_var_$MYSQL_MAJOR.tgz -C $MYSQL_DIST_DIR install -m 644 LICENSE.mysql $MYSQL_DIST_DIR # Copy required Galera libraries GALERA_BINS=$GALERA_DIST_DIR/bin GALERA_LIBS=$GALERA_DIST_DIR/lib install -m 755 -d $GALERA_DIST_DIR install -m 644 ../../LICENSE $GALERA_DIST_DIR/LICENSE.galera install -m 755 -d $GALERA_BINS install -m 755 -d $GALERA_LIBS if [ "$SCONS" == "yes" ] then SCONS_VD=$GALERA_SRC cp -P $SCONS_VD/garb/garbd $GALERA_BINS cp -P $SCONS_VD/libgalera_smm.so $GALERA_LIBS if [ "$OS" == "Darwin" -a "$DEBUG" == "yes" ]; then cp -P -R $SCONS_VD/garb/garbd.dSYM $GALERA_BINS cp -P -R $SCONS_VD/libgalera_smm.so.dSYM $GALERA_LIBS fi else echo "Autotools compilation not supported any more." exit 1 fi install -m 644 LICENSE $DIST_DIR install -m 755 mysql-galera $DIST_DIR install -m 644 README $DIST_DIR install -m 644 QUICK_START $DIST_DIR # Strip binaries if not instructed otherwise if test "$NO_STRIP" != "yes" then for d in $GALERA_BINS $GALERA_LIBS \ $MYSQL_DIST_DIR/bin $MYSQL_DIST_DIR/lib $MYSQL_DIST_DIR/sbin do for f in $d/* do file $f | grep 'not stripped' >/dev/null && strip $f || : done done fi fi # if [ $TAR == "yes" ] if [ "$BIN_DIST" == "yes" ]; then . bin_dist.sh fi if [ "$TAR" == "yes" ] || [ "$BIN_DIST" == "yes" ]; then if [ "$RELEASE" != "" ] then GALERA_RELEASE="galera-$RELEASE-$(uname -m)" else GALERA_RELEASE="$WSREP_REV,$GALERA_REV" fi RELEASE_NAME=$(echo mysql-$MYSQL_VER-$GALERA_RELEASE | sed s/\:/_/g) rm -rf $RELEASE_NAME mv $DIST_DIR $RELEASE_NAME # Hack to avoid 'file changed as we read it'-error sync sleep 1 # Pack the release tar -czf $RELEASE_NAME.tgz $RELEASE_NAME fi # if [ $TAR == "yes" || "$BIN_DIST" == "yes" ] if [ "$TAR" == "yes" ] && [ "$INSTALL" == "yes" ]; then cmd="$GALERA_SRC/tests/scripts/command.sh" $cmd stop $cmd install $RELEASE_NAME.tgz fi get_arch() { if ! [ -z "$TARGET" ] then if [ "$TARGET" == "i686" ] then echo "i386" else echo "amd64" fi elif [ "$OS" == "Darwin" ]; then if file $MYSQL_SRC/sql/mysqld | grep "i386" >/dev/null 2>&1 then echo "i386" else echo "amd64" fi else if file $MYSQL_SRC/sql/mysqld | grep "80386" >/dev/null 2>&1 then echo "i386" else echo "amd64" fi fi } build_linux_packages() { pushd $GALERA_SRC/scripts/mysql local ARCH=$(get_arch) local WHOAMI=$(whoami) if [ $DEBIAN -eq 0 ] && [ "$ARCH" == "amd64" ]; then ARCH="x86_64" export x86_64=$ARCH # for epm fi local STRIP_OPT="" [ "$NO_STRIP" == "yes" ] && STRIP_OPT="-g" export MYSQL_VER MYSQL_SRC GALERA_SRC RELEASE_NAME MYSQLD_BINARY export WSREP_VER=${RELEASE:-"$WSREP_REV"} echo $MYSQL_SRC $MYSQL_VER $ARCH rm -rf $ARCH set +e if [ $DEBIAN -ne 0 ]; then #build DEB local deb_basename="mysql-server-wsrep" pushd debian $SUDO_ENV $(which epm) -n -m "$ARCH" -a "$ARCH" -f "deb" \ --output-dir $ARCH $STRIP_OPT $deb_basename RET=$? $SUDO /bin/chown -R $WHOAMI.users $ARCH else # build RPM echo "RPMs are now built by a separate script." return 1 fi popd return $RET } build_freebsd_packages() { echo "Creating FreeBSD packages" # Create build directory structure DIST_DIR=$BUILD_ROOT/dist/mysql MYSQL_DIST_DIR=$DIST_DIR/usr/local MYSQL_DIST_CNF=$MYSQL_DIST_DIR/share/mysql/my_wsrep.cnf MYSQL_BINS=$MYSQL_DIST_DIR/bin MYSQL_CLIENT_LICENSE_DIR=$MYSQL_DIST_DIR/share/licenses/mysql-client-${MYSQL_VER}_wsrep_${RELEASE} MYSQL_SERVER_LICENSE_DIR=$MYSQL_DIST_DIR/share/licenses/mysql-server-${MYSQL_VER}_wsrep_${RELEASE} MYSQL_SERVER_DOC_DIR=$MYSQL_DIST_DIR/share/doc/mysql${MYSQL_MAJOR_VER}${MYSQL_MINOR_VER}-server_wsrep cd $BUILD_ROOT rm -rf $BUILD_ROOT/dist install_mysql_5.5_dist > /dev/null install -m 755 -d $(dirname $MYSQL_DIST_CNF) install -m 644 my-5.5.cnf $MYSQL_DIST_CNF cat $MYSQL_BUILD_DIR/support-files/wsrep.cnf | \ sed 's/root:$/root:rootpass/' >> $MYSQL_DIST_CNF pushd $MYSQL_BINS; ln -s wsrep_sst_rsync wsrep_sst_rsync_wan; popd install -m 755 -d "$MYSQL_CLIENT_LICENSE_DIR" install -m 644 ../../LICENSE "$MYSQL_CLIENT_LICENSE_DIR/GPLv3" install -m 644 freebsd/LICENSE "$MYSQL_CLIENT_LICENSE_DIR" install -m 644 freebsd/catalog.mk "$MYSQL_CLIENT_LICENSE_DIR" install -m 755 -d "$MYSQL_SERVER_LICENSE_DIR" install -m 644 ../../LICENSE "$MYSQL_SERVER_LICENSE_DIR/GPLv3" install -m 644 freebsd/LICENSE "$MYSQL_SERVER_LICENSE_DIR" install -m 644 freebsd/catalog.mk "$MYSQL_SERVER_LICENSE_DIR" install -m 755 -d "$MYSQL_SERVER_DOC_DIR" install -m 644 README "$MYSQL_SERVER_DOC_DIR" install -m 644 QUICK_START "$MYSQL_SERVER_DOC_DIR" # Strip binaries if not instructed otherwise if test "$NO_STRIP" != "yes" then for d in $MYSQL_DIST_DIR/bin $MYSQL_DIST_DIR/lib $MYSQL_DIST_DIR/libexec do for f in $d/* do file $f | grep 'not stripped' >/dev/null && strip $f || : done done fi pwd ./freebsd.sh $MYSQL_VER $RELEASE rm -rf $BUILD_ROOT/dist } if [ "$PACKAGE" == "yes" ]; then case "$OS" in Linux) build_linux_packages ;; FreeBSD) build_freebsd_packages mv *.tbz ../.. ;; *) echo "packages for $OS are not supported." return 1 ;; esac fi # percona-xtradb-cluster-galera/scripts/mysql/centos/0000755000000000000000000000000012247075736023001 5ustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/mysql/debian/0000755000000000000000000000000012247075736022730 5ustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/mysql/freebsd/0000755000000000000000000000000012247075736023120 5ustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/mysql/freebsd.sh0000755000000000000000000000546312247075736023467 0ustar rootroot00000000000000#!/bin/bash -eu if [ $# -ne 2 ] then echo "Usage: $0 " exit 1 fi MYSQL_VER=$1 RELEASE=$2 MAJORMINOR=$(echo $MYSQL_VER | awk -F . '{print $1$2}') # Absolute path of this script folder SCRIPT_ROOT=$(cd $(dirname $0); pwd -P) PBR="$SCRIPT_ROOT/dist/mysql/usr/local" install -d "$PBR"/{etc/rc.d,libdata/ldconfig} install -m 555 "$SCRIPT_ROOT/freebsd/mysql-server.sh" "$PBR/etc/rc.d/mysql-server" install -m 444 "$SCRIPT_ROOT/freebsd/client-ldconfig" "$PBR/libdata/ldconfig/mysql${MAJORMINOR}-client" shopt -s nullglob for i in {1..9}; do for f in "$PBR/man/man$i/"*.$i; do gzip -c $f > $f.gz done done shopt -u nullglob install -m 644 "$SCRIPT_ROOT/freebsd/server-"{plist,descr,comment,message} "$PBR" sed -e "s!%{SRCDIR}!$PBR!" -e "s!%{RELEASE}!$RELEASE!" -e "s!%{MYSQL_VER}!$MYSQL_VER!" \ -e "s!%{MAJORMINOR}!$MAJORMINOR!" -i "" "$PBR/server-"{plist,descr,comment,message} \ "$PBR/share/licenses/mysql-client-${MYSQL_VER}_wsrep_${RELEASE}/catalog.mk" for pkg in $(grep '^@comment DEPORIGIN:' "$PBR/server-plist" | cut -d : -f 2); do if [[ "$pkg" != *_wsrep* ]]; then pkgdep=$(/usr/sbin/pkg_info -q -O "$pkg") if [ -z "$pkgdep" ]; then echo "ERROR: failed to find dependency package '$pkg'" >&2 exit 1 fi sed -e "s!^@comment DEPORIGIN:$pkg!@pkgdep $pkgdep"$'\\\n&!' -i "" "$PBR/server-plist" fi done /usr/sbin/pkg_create -c "$PBR/server-comment" \ -d "$PBR/server-descr" \ -D "$PBR/server-message" \ -f "$PBR/server-plist" \ -m "$SCRIPT_ROOT/freebsd/server-mtree" \ -v "mysql-server-${MYSQL_VER}_wsrep_${RELEASE}-$(uname -m).tbz" install -m 644 "$SCRIPT_ROOT/freebsd/client-"{plist,descr,comment,message} "$PBR/" sed -e "s!%{SRCDIR}!$PBR!" -e "s!%{RELEASE}!$RELEASE!" -e "s!%{MYSQL_VER}!$MYSQL_VER!" \ -e "s!%{MAJORMINOR}!$MAJORMINOR!" -i "" "$PBR/client-"{plist,descr,comment,message} \ "$PBR/share/licenses/mysql-client-${MYSQL_VER}_wsrep_${RELEASE}/catalog.mk" for pkg in $(grep '^@comment DEPORIGIN:' "$PBR/client-plist" | cut -d : -f 2); do if [[ "$pkg" != *_wsrep* ]]; then pkgdep=$(/usr/sbin/pkg_info -q -O "$pkg") if [ -z "$pkgdep" ]; then echo "ERROR: failed to find dependency package '$pkg'" >&2 exit 1 fi sed -e "s!^@comment DEPORIGIN:$pkg!@pkgdep $pkgdep"$'\\\n&!' -i "" "$PBR/client-plist" fi done /usr/sbin/pkg_create -c "$PBR/client-comment" \ -d "$PBR/client-descr" \ -D "$PBR/client-message" \ -f "$PBR/client-plist" \ -m "$SCRIPT_ROOT/freebsd/client-mtree" \ -v "mysql-client-${MYSQL_VER}_wsrep_${RELEASE}-$(uname -m).tbz" exit 0 percona-xtradb-cluster-galera/scripts/mysql/get_patch.sh0000755000000000000000000000354312247075736024010 0ustar rootroot00000000000000#!/bin/bash -u set -x usage() { echo -e "Usage: $0 " } if [ $# -lt 2 ] then usage exit -1 fi #set -x set -e OS=$(uname -s) function MD5SUM() { if [ "$OS" == "Darwin" -o "$OS" == "FreeBSD" ]; then md5 -q $1 else md5sum $1 | awk '{ print $1 }' fi } # Source paths are either absolute or relative to script, get absolute THIS_DIR=$(pwd -P) cd $2 WSREP_REV=$(bzr revno -q --tree) WSREP_PATCH_SPEC=$1-$WSREP_REV # Check existing file # This is done to not to depend on LP operation, however it looks like # any changes uncommitted locally might go unnoticed as revno stays the same WSREP_PATCH_FILE=$(ls $THIS_DIR/${WSREP_PATCH_SPEC}_*_.diff 2>/dev/null || : ) if [ -r "$WSREP_PATCH_FILE" ] then WSREP_PATCH_MD5SAVE=$(basename $WSREP_PATCH_FILE | awk -F _ '{ print $2 }' ) WSREP_PATCH_MD5TEST=$(MD5SUM $WSREP_PATCH_FILE | awk '{ print $1 }') if [ $WSREP_PATCH_MD5SAVE = $WSREP_PATCH_MD5TEST ] then # to be safe we better regenerate the patch every time echo $WSREP_PATCH_FILE > /dev/null # exit 0 fi fi # Existing file either not found or corrupted, try to create a new one rm -f $WSREP_PATCH_FILE #MYSQL_BRANCH="lp:mysql-server/5.1" #MYSQL_LP_REV=$( bzr tags -d $MYSQL_BRANCH | grep -m1 "$1" | awk '{ print $2 }' ) #if [ -z "$MYSQL_LP_REV" ] #then # echo "No such tag/revision: $1" # exit -1 #fi WSREP_PATCH_TMP="$THIS_DIR/$WSREP_PATCH_SPEC.diff" # normally we expect bzr diff return 1 (changes available) bzr diff -p1 -v --diff-options " --exclude=.bzrignore " \ -r tag:$1..branch:$2 \ > "$WSREP_PATCH_TMP" || if [ $? -gt 1 ]; then exit -1; fi WSREP_PATCH_MD5SUM=$(MD5SUM $WSREP_PATCH_TMP | awk '{ print $1 }') WSREP_PATCH_FILE=$THIS_DIR/${WSREP_PATCH_SPEC}_${WSREP_PATCH_MD5SUM}_.diff mv $WSREP_PATCH_TMP $WSREP_PATCH_FILE echo $WSREP_PATCH_FILE percona-xtradb-cluster-galera/scripts/mysql/my-5.1.cnf0000644000000000000000000000145712247075736023133 0ustar rootroot00000000000000# Default mysqld options [mysqld] core-file innodb_buffer_pool_size=420M innodb_log_file_size=100M innodb_flush_log_at_trx_commit=2 max_connections=1024 # # Here are options to load innodb plugin. Uncomment, if you want to # load innodb plugin during server start. # Note, mysql-galera start script has --plugin option, which sets these # plugin options on command line. Use one of these methods to load innodb # plugin, but not both # # MariaDB uses xtradb as a built-in, so no need to load any plugins ignore_builtin_innodb plugin-load=innodb=ha_innodb_plugin.so;innodb_trx=ha_innodb_plugin.so;innodb_locks=ha_innodb_plugin.so;innodb_lock_waits=ha_innodb_plugin.so;innodb_cmp=ha_innodb_plugin.so;innodb_cmp_reset=ha_innodb_plugin.so;innodb_cmpmem=ha_innodb_plugin.so;innodb_cmpmem_reset=ha_innodb_plugin.so percona-xtradb-cluster-galera/scripts/mysql/my-5.5.cnf0000644000000000000000000000023212247075736023125 0ustar rootroot00000000000000# Default mysqld options [mysqld] core-file innodb_buffer_pool_size=420M innodb_log_file_size=100M innodb_flush_log_at_trx_commit=2 max_connections=1024 percona-xtradb-cluster-galera/scripts/mysql/mysql-galera0000755000000000000000000004141712247075736024041 0ustar rootroot00000000000000#!/bin/bash -e SELF=$(cd $(dirname $0); pwd -P)/$(basename $0) # Copyright (C) 2007, 2008 Codership Oy # # This file is free software; as a special exception the author gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY, to the extent permitted by law; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # WHAT IT IS: # This script starts mysqld from Galera test distribution. It may be adapted # to be run from /etc/init.d directory for automatic server startup. # # USAGE: # GCS_ADDRESS=
mysqld_galera start|stop|restart|check|create # # By default empty backend is "dummy". # # 'check' command takes options: [database [table_to_ignore] [table_to_ignore]...] # # USEFUL ENVIRONMENT VARIABLES: # # MY_CNF - specifies the configuration file # GCS_ADDRESS - overrides setting in my.cnf # MYSQL_PORT - port to listen for client connections (default: 3306) # LIBGALERA - location of the galera shared library file # # Convention: mysql pid file is stored in mysql data dir under name 'mysql.pid' # # Normally only the following parameters need to be changed: # Where mysql data directory is located (by default - inside mysql installation) # MYSQL_DATA_DIR # Where mysql installation is located (by default determined from this script location) MYSQL_BASE_DIR=${MYSQL_BASE_DIR:-"$(dirname $SELF)/mysql"} GALERA_BASE_DIR=${GALERA_BASE_DIR:-"$(dirname $SELF)/galera"} # MySQL configuration file MY_CNF=${MYSQL_CNF:-"$MYSQL_BASE_DIR/etc/my.cnf"} if test -s "$MY_CNF" then DEFAULTS_OPTION=" --defaults-file=$MY_CNF " my_cnf_datadir=$(grep ^datadir $MY_CNF | sed s/[^/]*//) else DEFAULTS_OPTION=" --no-defaults " fi # If it was not given explicitely, use the one from my.cnf MYSQL_DATA_DIR=${MYSQL_DATA_DIR:-"$my_cnf_datadir"} # If it was not found in my.cnf, use distribution default MYSQL_DATA_DIR=${MYSQL_DATA_DIR:-"$MYSQL_BASE_DIR/var"} MYSQLD_USER=$(whoami) #======================================================================= ## ## Tweak the following if needed ## # use mysqld server directly, better not have automatic restarting MYSQLD="$MYSQL_BASE_DIR/sbin/mysqld" [ ! -x "$MYSQLD" -a -x "${MYSQLD}-debug" ] && MYSQLD=${MYSQLD}-debug MYSQLADMIN="$MYSQL_BASE_DIR/bin/mysqladmin" # Port, socket and pid files MYSQL_PORT=${MYSQL_PORT:-3306} MYSQL_SOCKET=${MYSQL_SOCKET:-"$MYSQL_DATA_DIR/mysqld.sock"} MYSQL_PID=${MYSQL_PID:-"$MYSQL_DATA_DIR/mysqld.pid"} # Shutdown wait timeout. MYSQL_SHUTDOWN_WAIT=60 #============= Nothing servicable below ================================ # User to run as if started under superuser #if test "$MYSQLD_USER" = "root" #then # MYSQLD_USER=mysql #fi ROOT_USER=${ROOT_USER:-"-uroot"} ROOT_PSWD=${ROOT_PSWD:-"-prootpass"} # Mandatory stuff INNODB_OPTS=" --default-storage-engine=InnoDB " # --debug terribly affects performance #DEBUG_OPTS=" --debug " #DEBUG_OPTS=" --debug=d,galera,wsdb:t:i:o,$MYSQL_DATA_DIR/mysqld.trc" SKIP_RECOVERY=0 START_POSITION="" err_log="$MYSQL_DATA_DIR/mysqld.err" mysql_log="$MYSQL_DATA_DIR/mysqld.log" usage() { cat - << EOF usage: mysql-galera [options] command Options: -c|--command : command to execute (status) --data_dir : location for mysql data directories --db : name of database to check -d|--debug : enable debug output --donor : desired state transfer donor -g|--gcs_address : address of gcs server (spread://locahost:4803) --gdb : run under gdb -i|--ignore_table : table to ignore in checking -l|--log : enable mysql query log --mysql-opt : an option to the server to follow -p|--password : mysql root user password --plugin : use innodb plugin --slave_threads : number of consurrent ws appliers (1) -u|--user : mysql root user --valgrind : run under valgrind --ws_level : RBR (default) or SQL -P|--port : port for MySQL client connections -S|--socket : location of mysqld socket --skip-recovery : skip recovery phase --start-position : start position passed to server Commands: check : check cosistency of database given with --db option start : start servers stop : stop servers restart : stop and start servers status : show running servers EOF } # Checks if a process with a given PID is still running find_pid() { ps ax | grep mysqld | grep -w ^\ *$1 > /dev/null } get_status() { local stat_var=$1 set -o pipefail mysql $ROOT_USER $ROOT_PSWD --socket $MYSQL_SOCKET --skip-column-names \ --reconnect -Be "SET wsrep_on=0; SHOW STATUS LIKE '$stat_var'" | \ cut -f 2 } # Loop until wsrep_ready == ON wait_for_wsrep_ready() { echo -n "Waiting for wsrep_ready" local pid=$1 local ret=1 local ready while find_pid $pid do echo -n "." sleep 1 # mysql connection can be interrupted by SST code, # don't fail on it rigth away ready=$(get_status "wsrep_ready") && \ if [ $ret ] && [ "$ready" == "ON" ]; then ret=0; break; fi done if [ $ret ] then echo " Done" else echo " Failed" fi return $ret } check_gcs_address() { local addr="$1" if [ -n "$addr" ] then case "$addr" in "gcomm://"*) # we could be checking address here, but generally it does not # have to exist yet. ;; "dummy://"*) ;; *) echo "Cluster address should start with either 'gcomm://' or 'dummy://'. Server not started." >&2 exit 1 esac fi } wsrep_start_position_opt="" # Run mysqld with --wsrep-recover and parse recovered position from log. # Position will be stored in wsrep_start_position_opt global. wsrep_recovery() { cmd="$@" wr_logfile=$(mktemp -t wsrep.XXXXXXXXXX) echo "WSREP: Running position recovery" set +e [ "$OS" == "Darwin" ] && export LD_LIBRARY_PATH $cmd --log_error=$wr_logfile --wsrep-recover [ "$OS" == "Darwin" ] && export -n LD_LIBRARY_PATH rp=$(grep "WSREP: Recovered position:" $wr_logfile) if [ -z "$rp" ]; then skipped=$(grep WSREP $wr_logfile | grep "skipping position recovery") if [ -z "$skipped" ]; then echo "WSREP: Failed to recover position: " \ `cat $wr_logfile`; else echo "WSREP: Position recovery skipped" fi else start_pos=$(echo $rp | sed 's/.*WSREP\:\ Recovered\ position://' \ | sed 's/^[ \t]*//') wsrep_start_position_opt="--wsrep_start_position=$start_pos" echo "WSREP: Recovered position $start_pos" fi set -e rm $wr_logfile } galera_start() { local failed if ! test -x $MYSQLD then echo "$MYSQLD executable not found" exit -1 fi if test -f $MYSQL_PID then echo "Found existing '$MYSQL_PID'. Please run '$0 stop'" exit -1; fi # if [ -n "$MYSQL_LOG" ] # then # LOGGING_OPTS=" --general_log=1 --log_output=FILE " # fi if [ -n "$WS_LEVEL" ] then RBR_OPTS=" --binlog_format=$WS_LEVEL " fi WSREP=${WSREP:-"$GALERA_BASE_DIR/lib/libgalera_smm.so"} if test -f $WSREP || test $WSREP == "none" then WSREP_OPTS="--wsrep_provider=$WSREP" else echo "WSREP driver '$WSREP' not found" exit -1 fi if test -n "$GCS_ADDRESS" then check_gcs_address $GCS_ADDRESS WSREP_OPTS="$WSREP_OPTS --wsrep_cluster_address=$GCS_ADDRESS" fi if test -n "$WSREP_SST_DONOR" then WSREP_OPTS="$WSREP_OPTS --wsrep_sst_donor=$WSREP_SST_DONOR" fi if test -n "$SLAVE_THREADS" then WSREP_OPTS="$WSREP_OPTS --wsrep_slave_threads=$SLAVE_THREADS" fi if test -f "$MYSQL_DATA_DIR/core" then mv "$MYSQL_DATA_DIR/core" "$MYSQL_DATA_DIR/core.old" fi echo -n "Starting mysqld instance with data dir $MYSQL_DATA_DIR and listening at port $MYSQL_PORT and socket $MYSQL_SOCKET..." ulimit -n 4096 # This is normally allowed for non-privileged users if test $SKIP_RECOVERY = 0 then wsrep_recovery $MYSQLD \ $DEFAULTS_OPTION \ --user="$MYSQLD_USER" \ --basedir="$MYSQL_BASE_DIR" \ --datadir="$MYSQL_DATA_DIR" \ --plugin-dir=lib/mysql/plugin \ --pid-file="$MYSQL_PID" \ --port=$MYSQL_PORT \ --socket=$MYSQL_SOCKET \ --skip-external-locking \ --log_error=$err_log \ $MYSQLD_OPTS \ $INNODB_OPTS \ $WSREP_OPTS \ $DEBUG_OPTS \ $LOGGING_OPTS \ $RBR_OPTS \ $PLUGIN_OPTS else echo "skipping recovery" if test -n "$START_POSITION" then wsrep_start_position_opt="--wsrep-start-position=$START_POSITION" fi fi [ "$OS" == "Darwin" ] && export LD_LIBRARY_PATH if test -z $GDB then nohup $VALGRIND $MYSQLD \ $DEFAULTS_OPTION \ --user="$MYSQLD_USER" \ --basedir="$MYSQL_BASE_DIR" \ --datadir="$MYSQL_DATA_DIR" \ --plugin-dir=lib/mysql/plugin \ --pid-file="$MYSQL_PID" \ --port=$MYSQL_PORT \ --socket=$MYSQL_SOCKET \ --skip-external-locking \ --log_error=$err_log \ $MYSQLD_OPTS \ $INNODB_OPTS \ $WSREP_OPTS \ $DEBUG_OPTS \ $LOGGING_OPTS \ $RBR_OPTS \ $PLUGIN_OPTS \ $wsrep_start_position_opt \ 1>/dev/null 2>>$err_log & else $GDB --args $MYSQLD \ $DEFAULTS_OPTION \ --user="$MYSQLD_USER" \ --basedir="$MYSQL_BASE_DIR" \ --datadir="$MYSQL_DATA_DIR" \ --plugin-dir=lib/mysql/plugin \ --pid-file="$MYSQL_PID" \ --port=$MYSQL_PORT \ --socket=$MYSQL_SOCKET \ --skip-external-locking \ --log_error=$err_log \ $MYSQLD_OPTS \ $INNODB_OPTS \ $WSREP_OPTS \ $DEBUG_OPTS \ $LOGGING_OPTS \ $RBR_OPTS \ $PLUGIN_OPTS \ $wsrep_start_position_opt fi my_pid=$! [ "$OS" == "Darwin" ] && export -n LD_LIBRARY_PATH # echo "Waiting for pid file" while ! test -r $MYSQL_PID do sleep 1 if find_pid $my_pid then # process is alive, wait for pid file echo -n "." else failed="yes" break fi done if test "$failed" != "yes" then echo " Done (PID:$(cat $MYSQL_PID))" else echo " Failed (PID:$my_pid)" return 1 fi wait_for_wsrep_ready $my_pid } galera_stop() { # check pid file if test -r $MYSQL_PID then # check if corresponding mysqld is running # if ps axc | grep mysqld | grep $(cat $MYSQL_PID) >/dev/null 2>&1 local my_pid=$(cat $MYSQL_PID) if find_pid $my_pid then echo -n "Killing PID $my_pid" kill $my_pid # wait for pid file to disappear for second in $(seq 1 $MYSQL_SHUTDOWN_WAIT) do echo -n "." sleep 1 if test ! -r $MYSQL_PID then break fi done echo "" if test "$second" = "$MYSQL_SHUTDOWN_WAIT" then echo -n "Failed to stop mysqld safely. Killing with -9... " kill -9 $my_pid fi else echo -n "Removing stale PID file $MYSQL_PID... " fi rm -f $MYSQL_PID echo "Done" else echo "PID file not found: $MYSQL_PID" fi } galera_restart() { galera_stop galera_start } galera_status() { if test -f $MYSQL_PID then local my_pid=$(cat $MYSQL_PID) if find_pid $my_pid then echo "mysqld running with PID: $my_pid" else echo "Found existing '$MYSQL_PID', but mysqld is not running" fi exit 0; else echo "no PID file: '$MYSQL_PID'" fi } dump() { #local ROUTINES="--routines" # don't dump routines yet, will cause false err. #local OPTIONS="--create-options" gives false positives on AUTO_INCREMENT tbls # --flush-logs --lock-all-tables # this blocks waiting for all trx to complete # thus impossible to use with -STOP/CONT local DUMP_OPTIONS=" --skip-opt --compact --quick --order-by-primary \ $OPTIONS --set-charset --skip-comments $ROUTINES " DB=${DB:-"--all-databases"} #set -x mysqldump $DUMP_OPTIONS $ROOT_USER $ROOT_PSWD --socket $MYSQL_SOCKET \ $IGNORE_TABLES $DB #set +x } wait_for_last_committed() { local lc local new_lc lc=$(get_status "wsrep_last_committed") while [ 1 ] do sleep 1 new_lc=$(get_status "wsrep_last_committed") if [ "$lc" == "$new_lc" ]; then break; fi lc="$new_lc" done } checksum() { wait_for_last_committed set -o pipefail if [ "$OS" == "Darwin" -o "$OS" == "FreeBSD" ]; then CS=$(dump | md5)" -" || return $? else CS=$(dump | md5sum) || return $? fi echo $CS } # to use valgrind or not VALGRIND=${VALGRIND:-""} # write set level, SQL, RBR or ROW WS_LEVEL="" #DB="test" # use 'test' database if none given # in 5.6 the following tables are non-deterministic IGNORE_TABLES=\ "--ignore-table=mysql.innodb_table_stats --ignore-table=mysql.innodb_index_stats" # to use innodb plugin or not PLUGIN_OPTS="" if [ $# -eq 0 ]; then usage; exit 1; fi while [ $# -gt 0 ]; do case $1 in -h|--help) usage exit 0 ;; -d|--debug) DEBUG_OPTS=" --wsrep_debug=1 " ;; --dbug) DBUG_OPTS=" --debug=d,galera,wsdb:t:i:o" ;; -l|--log) LOGGING_OPTS=" --general_log=1 --log_output=FILE " # MYSQL_LOG="log" ;; --valgrind) VALGRIND="valgrind --log-file=$MYSQL_DATA_DIR/vg.log --leak-check=full --track-origins=yes" # to force deallocation in std::string and STL containers export GLIBCXX_FORCE_NEW=1 ;; --gdb) GDB="gdb" ;; -g|--gcs_address) GCS_ADDRESS=$2 shift ;; --donor) WSREP_SST_DONOR=$2 shift ;; --slave_threads) SLAVE_THREADS=$2 shift ;; --db) DB=$2 shift ;; -i|--ignore_table) IGNORE_TABLES=" $IGNORE_TABLES --ignore-table=$DB.$2 " shift ;; --ws_level) WS_LEVEL=$2 shift ;; -u|--user) ROOT_USER="-u$2" shift ;; -p|--password) ROOT_PSWD="-p$2" shift ;; --plugin) PLUGIN_OPTS="--ignore_builtin_innodb --plugin-load=innodb=ha_innodb_plugin.so;innodb_trx=ha_innodb_plugin.so;innodb_locks=ha_innodb_plugin.so;innodb_lock_waits=ha_innodb_plugin.so;innodb_cmp=ha_innodb_plugin.so;innodb_cmp_reset=ha_innodb_plugin.so;innodb_cmpmem=ha_innodb_plugin.so;innodb_cmpmem_reset=ha_innodb_plugin.so " ;; --data_dir) MYSQL_DATA_DIR=$2 shift ;; -c|--command) COMMAND=$2 ;; --mysql-opt) MYSQLD_OPTS="$MYSQLD_OPTS $2" shift ;; -P|--port) MYSQL_PORT="$2" shift ;; -S|--socket) MYSQL_SOCKET="$2" shift ;; --skip-recovery) SKIP_RECOVERY=1 ;; --start-position) START_POSITION="$2" shift ;; 'dump') COMMAND="dump" ;; 'check') COMMAND="checksum" ;; 'start') COMMAND=galera_start ;; 'stop') COMMAND=galera_stop ;; 'restart') COMMAND=galera_restart ;; 'status') COMMAND=galera_status ;; 'create') COMMAND="create_data_dir $2" shift ;; *) # must be command echo "error parsing: $@" usage exit 1 ;; esac shift done if [ -z "$COMMAND" ] then usage >&2 exit 1 fi OS=$(uname) export LD_LIBRARY_PATH=$MYSQL_BASE_DIR/lib/mysql:$LD_LIBRARY_PATH [ "$OS" == "FreeBSD" ] && LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib/gcc48 [ "$OS" == "Darwin" ] && export -n LD_LIBRARY_PATH export PATH=$MYSQL_BASE_DIR/bin:$PATH $COMMAND # percona-xtradb-cluster-galera/scripts/mysql/mysql-plain0000755000000000000000000000660412247075736023710 0ustar rootroot00000000000000#!/bin/bash SELF=$(cd $(dirname $0); pwd -P)/$(basename $0) # Where mysql installation is located (by default determined from this script location) if test -z "$MYSQL_BASE_DIR" then echo "MYSQL_BASE_DIR is not set" exit 1 fi # MySQL configuration file MY_CNF=${MYSQL_CNF:-"$MYSQL_BASE_DIR/etc/my.cnf"} if test -s "$MY_CNF" then DEFAULTS_OPTION=" --defaults-file=$MY_CNF " my_cnf_datadir=$(grep ^datadir $MY_CNF | sed s/[^/]*//) else DEFAULTS_OPTION=" --no-defaults " fi # If it was not given explicitely, take it from my.cnf MYSQL_DATA_DIR=${MYSQL_DATA_DIR:-"$my_cnf_datadir"} # If it was not found in my.cnf, use default MYSQL_DATA_DIR=${MYSQL_DATA_DIR:-"$MYSQL_BASE_DIR/var"} # use mysqld server directly, better not have automatic restarting MYSQLD=${MYSQLD:-"$(dirname $SELF)/../sql/mysqld"} # Port, socket and pid files MYSQL_PORT=${MYSQL_PORT:-3307} MYSQL_SOCKET=${MYSQL_SOCKET:-"$MYSQL_DATA_DIR/mysqld.sock"} MYSQL_PID=${MYSQL_PID:-"$MYSQL_DATA_DIR/mysqld.pid"} err_log="$MYSQL_DATA_DIR/$(hostname).err" # Checks if a process with a given PID is still running find_pid() { ps axc | grep mysqld | grep -w ^\ *$1 > /dev/null } mysql_start() { local failed if ! test -x "$MYSQLD" then echo "$MYSQLD executable not found" exit -1 fi if test -f $MYSQL_PID then echo "Found existing '$MYSQL_PID'. Please run '$0 stop'" exit -1; fi echo -n "Starting mysqld instance with data dir $MYSQL_DATA_DIR and listening to port $MYSQL_PORT and socket $MYSQL_SOCKET..." set -x nohup $MYSQLD \ $DEFAULTS_OPTION \ --basedir="$MYSQL_BASE_DIR" \ --datadir="$MYSQL_DATA_DIR" \ --pid-file="$MYSQL_PID" \ --port=$MYSQL_PORT \ --socket=$MYSQL_SOCKET \ --skip-locking \ --log_error=$err_log \ 1>/dev/null 2>>$err_log & my_pid=$! set +x # echo "Waiting for pid file" while ! test -r $MYSQL_PID do sleep 1 if find_pid $my_pid then # process is alive, wait for pid file echo -n "." else failed="yes" break fi done if test "$failed" != "yes" then echo " Done (PID:$(cat $MYSQL_PID))" else echo " Failed (PID:$my_pid)" fi } mysql_stop() { # check pid file if test -r $MYSQL_PID then # check if corresponding mysqld is running # if ps axc | grep mysqld | grep $(cat $MYSQL_PID) >/dev/null 2>&1 if find_pid $(cat $MYSQL_PID) then echo -n "Killing PID $(cat $MYSQL_PID)" kill $(cat $MYSQL_PID) # wait for pid file to disappear for second in $(seq 1 $MYSQL_SHUTDOWN_WAIT) do echo -n "." sleep 1 if test ! -r $MYSQL_PID then break fi done echo "" if test "$second" = "$MYSQL_SHUTDOWN_WAIT" then echo -n "Failed to stop mysqld safely. Killing with -9... " kill -9 $(cat $MYSQL_PID; rm -rf $MYSQL_PID) fi else echo -n "Removing stale PID file $MYSQL_PID... " rm -rf $MYSQL_PID fi echo "Done" else echo "PID file not found: $MYSQL_PID" fi } mysql_restart() { mysql_stop mysql_start } case "$1" in 'start') mysql_start $2 ;; 'stop') mysql_stop ;; 'restart') mysql_restart $2 ;; 'check') shift; checksum $* ;; *) echo "Usage: $0 start|stop|restart|check" esac # percona-xtradb-cluster-galera/scripts/mysql/mysql_var_5.1.tgz0000644000000000000000000047454712247075736024661 0ustar rootroot00000000000000LJ]|ՙ\10 =]K!!hwvwٙ" c/ I68){R 1_r$Ws\p-.wi%wmzX}zo8zhK48ȏ)TT(, RRiXR&srjiwNlvϵ'&OA'O'$8/ .JzW*"@u[/fV"7LvLluucr,xUx?rՐ2ǒ!Cs,ǿt_= U(PO| *pɳeGmW<?_iG=R?jYD/'NRW:E~ZRn){)H_1>>4=፝~ƸECټ#ȑ#G9rȑ#GY8։;r ~cѢ>ln!ypZT?uJ{ (+V(eHsȑ#G9rȑ#GG|NS.o)P^Up~зg g+{n-E20my\8 +Gmb ɑfF kjjvBΑ#G9rq\ph87'oho96)PdmmAZQ)٪5ՙE ˯:byoRrK;6ok9|jZ6X-?2%jqyk{rjj^isg0XTlI_Xtxo wb2eLhaqkקNU_Ԓ'$zu QfLXf /P^9r|/ە8] ;ڰL'%(7&t6h}6IV ^}Pת^Vk|;V U(kBz(0% P`h:7Yî5ApH D|N庖dN%t k`!Dd\LPv #;h*[YSX3V5%=K4etmR&;$fRĘdMH/ɚԐ{NB_dM%j&/kMi&l5O(X0&jLvµU p!p\iT+ytU Ȭu[NaJ,:5!g4j{sz֔ˇ]Ϛ^hMM)k)0kjONĄTh&b˂5 \k+RCi&m+h ޢ`҉2Sִt \hmR&% Z3q)$j \Fv-z(pfnք\ &Jxd<hV3Aqa5t: rʰg$ē앍gX,9i&fΒ}حȗjGcƬk(մǯdM%Zضk>'VHĤ59Ą뇂g -fPڬ XE3㠙XMu[} lQǵkPvdMIIhZ#X|s،On{WZ~@sdS|Lh:顰H7['w>bͮN$Za x#^^:7IY@%qݘ?aqTg%@fF#q4ur8`5t-d$ɲW<0BLAuVdrfHb;86kb<;5+rc4'xt;c!U/Z5-]{T$R bz{2'd:9R %jQ>҂-YS%0F#TXGӰjH8؍/"y0MdwY'7Rƍx>;cpWl,ʹ;ՅgXe6x!Iubk`=IWqe:M'fbCR*Y0:'{&4uYSp+ź%͂ȴ 6%G'`3V-ya)E$;KڡqZc٭oiikh-[p+o-wEh&%xd vb"b~ִt -VE3q;R$xQ`s*a,X31V˚^I(JʵZ$vaΤ6fCr>٥QeM207'/-\K:I#A&;eIS &dMH/h:#7ć5G-joG5#- |v'׷$xSDٳaSe;Xք|RM_-7N~]AE=}\/VNdӬV*-7 LK,|'&#>ϜzΕlЈ9QI=BbƴȚ/ \|`ײi4;dճa |VEHxV3b'&'+<1# N+K>+2*pFkbܳ\70>D!7x:\f⛔8. |Xdn`m\Xvj;i*4*Y39Yb0,:5Ϛ=$v8!:a'9Q4p4k:z~ N(7@?dǣ O،9j/:86  Ыصx&{ ߳'~mLu#;^$tbǒ?Ț?N:+Iw5}`aGu.ቤd٪ ǜsKTә}ܪZW 9!;?K$M\}vNU;Ŏ,9 \ r/S&B㕣@+&`?bHBRjyoQb  (ogsIYlldgP `M, ڏCѲ|N-9#gX,XhJY ׵&]WZ& .2kBzt$LdUp~CWY49dkveKq8E3Q-ɡ&+^8U31lGc f2P2 k9r:'Hk,pr8"KQgi&F-7\U,v@txî*(*ɽkS3q>Nu@3!0q!-ʭh *nyPx^4훸Xjv֤t\l'RbP+ \n\5)W]BWI@ii5)WƱB5O7b x9Y*9UGj) fSf0t[eY6еFV84[e?D X_x"3U&4PLۖs0R3M ѕm28btAb3S2,5-]אۤLL\NFrwg&;9fg_͌9=$:Y^KGGritźqMr5n -Aۑ0f\(Ϊ,3 -M+ɆVĊOXk?ɁM6a,k5BdWIhVf핀8I,eMH/(O'7%!R2F©`cEEAܔJJi[|Y,Z5H,^`p8~sPU-2ce)5P/"Kر^31b]E ?[XZ=6PXD>'6&E$K&tNH?pkj˜"kmtN{q`F,lf?vD3/oXK+nvc7k[83^t\}*58K+=\;hww-وH=E'#/<;Z6^[ldX_9õl,w<|b2!:UIz6HeN,M}VB7'a)IQ4֨&^G]5k2z^INCwB3b"+7e%&<*meMLH^͜,f-IܓNoe!z(d$'~N S3u{~l"vKՈ4iCxOZ*xo{*"t'Ĉ.4W3A^5U~8kb>LVX 1J6> Z@n>F fMI97h8%egۖ&[$vr/'S쌏@Yg^;y5)T)\W& r$>Cx]4>2Q|fMF'8T4b )<d;/-$Ҷ/r'ys2%b%gkugMFo$ //wjgMKWʽcr8cRy0ff <&LڊU{#]2jKz·!AJGT</xs~v`~Vm#8VuQ=RH,ڟLw+y'UQ&X4t IU5R4FDT}XmkݜO %O^%v(_oRϤԋtS!oRH ?`~7󞥀D-l\M@VdT67mogXaPQ5怋`Xg,&s%<0.3<p9bx993Y9೙>9s>9_W92|1s0|)s1|9sW0|%s_c*怯f怯eȫ99-m]=}?C?#?>cq怿?S?3?smx?s3|9~9s/3M怿sҚ$zn 6I6)F #"7L.noBrh :|#pz:HSmO ~ ?2cJ\|j~uU6eU6kiﵷ97eJl1ʞxȗ`ЦT|B"mƈ(Ǔ¥=u:k룴C:Sy҄0ʷ&pWUDV8)CѤ6=ɖ5~aj~7~Ҧ8#v0-?JUy2;j߃%:]GZF d2Nұ=}O+`S7]v0UϏz*9[nɌMOMfʌzʖUGSvx;+m#/l_WТJ' !OeG] Ct*mQ iM04;%:(wJM⮥@MT.˗u!B[Ne*RTt=E>Why,!vhRn59EƇsC=ίp%|v:{Srj:F;{vxk'vȒ7bW#PT {l;Y&W}"~B eл5;i9 = V3);) ~0`IhDRwnh75uJ&9حfݬ{a nIIlTܙoRTYcT=ZY=8F~w[ EXms^I*rvtNr7cK([Dh&ETks.gxS!dM AaRm_-._9﫪?``?d#*,0_?fVd"i#lJJJ6%%%%%%%%%%%%%%%%%%kP@i2G@@@@@@@@@@@@@@@-u>EpV! w'Jw~ vdXLWiRp.݋b:䵼/T MJ@N§[8N3SKSt鑩$%hO;Kx/^JSjrR/a,#d4,M:W^aǡ8h;A:aZ*=*NX4< >XGEEEj?X]{%qZyV-?SF䔛AmIrh:J"X%&W.3/%1MMxr·:t+63]'c%K%3MK!gsɮXT]-Q$bNSnɬ6^jݼ\׺ob7r-GPwp*"FUI^N6jCJC)`La7SԽJE} Ϫڭ/hmkIuJGyL5IF'8G ~i}pJDICa!WmIgK+ʟ%pORH:>mw!y|ޮO8靴w(;IVJjl6qm.{_t-Ԃj mI8[mA˔#(iG}RH-tQϤ^~3x֜JJl'7&0iEBU7#rr#[ᐫt|U}w= Z~êB_Vv;%x_;\4읂-[ߤ=w6*sP}A}A}Azv#o%%%%;,s%TUK}bX1G-tRa@37Y9Bɾ@57 P<ZRMH}AoPiz" Ԯ4+(\ T(((oeP/(P/(P/(P/(PP>0P>0Pb%%%%%%%%%%%%%%%%%%%%%%%}`}`}`}`}`}`}`}`}`}`}`}`}`}`}`}`}`}`}`}`}`}``k.a~C=<- gsI%qԤ-3Q_?W4|ƴ)Y~ A^~Jl|wI5"E7HDs}Y|g!c)ś.Q)gc_i]gik#^svD&=Xŧ;ŖL:u8dX's"%mpu:t!&\-.- kA=)dv]ǧGd¾`@ɾ`@ɾ`@ɾ`@ɾ`@ɾ`@ɾ`@ɾ`@y/sB`P/P/P/P/P/P/P/P/P/P/P/P/P/P/P/P/P/P/P/P/P/P/P/P/P/P/P/P/P/az~_?O;|zi~ayyFBЏ-A!vH>@K[8;|W:aHs"h.O>O>O>O>JB" WgïŪj`9M|pĝa M#+O>O>O>6p'S;<*!' 46 z\H:LK",0l_tW x?r>~L^cKgvZ<<3_ .iMC#a/?_W!N<9os?z=7?^{uzi}GӪRRZq|':MuGPoYo +K'|'|'|'q$%>w߇|Z}G/ ?$}'a|'|'||<߰fɚ-W fV}$EHi!bK*(ɠ6}"TK-͕EK`? J[J|W} 'Fwq!a WKU*VX-խB읆[?Zeiژ^Ol\:mox =*Wڳ #';InOh k:[@]}N:DBU8w=!m㡶+O|'|'|'~*4 }kȷ\O>O>P3 rŚRtg: g 5N0>CL'&)LŚ況=bGuEhC}'{CCm4:{k~O@϶m6:`qEz|m'!Cx*J*ܫnW=%^Yb*=\/p1cZuZ'Z_MCq\qAt9{πl I>~0~w# DdS!).ۄnO). ]#.'O>O>O>O~Z|ݷǷ\j?@ 9_{'|'|'|'q \'\8ρ~ut^6kuϧ HZ|r'|'|QJ;(G#"TiK3V+)EwBF[/jE*zDd^׺E@3e ZchŔ7/]G_qKMY YSjTf ;¹ȠnF]ω^ {uSJzGG{xC' ٷ=^a rx{i)SOyLbj"K\%.u;'#4f t(=V*fX\k]:3ջ,M'~%ya'}K:*z-VU(ZN&7wSVzwpx(yoUzhT"֞FpdnF&Ϳ"TeX̹a`J^ snDM o5Z!;V26_^Pt\`Uo4М/4@cR.TY4feUڂEa92 .vb(IwĿ}s:fai\ƕ}̴Zdܱ := z)N7ijmUOFV,6A3JXTU-<&)>s'bsssAhlZ\c(}媕a7lGF̑^Zi=i`c$Ʋ0OSgO9Nj.Xg2Tn:Ldsɩ_Ϊ&IӝAƮ&ur?$V]YPFTg P$Zk:YlRnTOv<}R.<ϥ3+Đh5-+L4V^,Ԗ<M|zyf&a˵鱶T)EzԷ]wC:ZMnRlhmF 2ʩ,]PIdu+#=XE3h*!&p6X-P(L.kGlUWU7tI%Jo{0^4B*-b?_xGW3)3'O 6Q3q$e&L2VQU->rYfIq tL(y7500$*ŏ4 G7S4kLk 5G7zKZi:E\GAUEMr rt=6+Y+h"DsF|敢ST~g%އ2'&X&AZ>ѹc@Ay 0bP_\4ۢm>ឩPlt˵fMKGIԜ|f|rr5A9&Gl:K8lvu. %5j$6B,T?cNv T~iI`b6.ZfcUPY8p`$288 +:m! ڒ睽d +|ᄅ 4ۘ~rua7D,3($\[eiNXY5&p3;H+ U/.X6r[EX-m?Q+#7ȫb\\YS@Mkbl>ZŌfx4aHwvmEx_c ;? yUeIpGs{wwd-tF@d,L2G$bQS|#RwU D0x6˵,ZE6s-ZNb3==>I z s" Ⅲ֌3])"O *N\i#WI~Y[Ujseҽaa԰54m`~pnYM5\ B\{q\cyӀ*D ϖh٥lkEXRQzemVA愽JDV뮷i[*wD-ހpSg0q!Sh%QCűnAzVYco\[*6J;.e>_ oP#Ӭ?˺Pԫwtz AjBOA-.OSxcӤPZu5#k%ISQq {٪G9m˶ʪiMQ!ThX;ۆ.*B+:&/,@x2\/r;E!KU!<*UӪLRn>[#ku_6(~-K'10_W6YI|*ZK9Ort*+э$2rN`IB~Տh\⾳szTug7XӻSÜQuv!4͖ˋk".rWx쵶`Ũ",D) n2j3&HxBNYXj^sz3%a\'?sж Ok<ue)N f۟e&u5cFGHfW'%(J䪤(a%*|MgDwBnSEFGG#\3ݿ\!5MN~У e"#nOVuS?:o+;^/TXgachߋ%D#͑>u\:\˃3WFQɁx?09y 5#b,5,B)h 4kr :UNfê̪C!iQ(\f2(Tgs>4k̖Jɵb%NN&-6f\}z2y' QwW@m4XgmgmXS[Q[onkod>ֹ)3)e;'k0G7҈^ߒ/!8+ee[xnm787br{sY/^[ȥ;Jr ./=7tT69Jĉ%I&'&Yg)Jw/y4|+<>\%Ү}gGQS%ل!H2h`{֜3Y(fa.xJMRZOA>W\;bl&!P.˩ޜ3\28)ݱT( ]~~OYEVg-*6oyQ+=@nLl:~K)0bIe7Xvml=#Wʙ73UvfoZ6^n 6)ZL*zu6Pz<<5㼦RtNWf(2-G.yQu@k;ܻI_giW;~qp^\X'SkXޜ3S8 -NFsFBtd7#sQIl,'eyzAvhȭ% p"AU5 pq($1l\8Sqɑرcy}qN'c=ǻx8^Xϱomko{/"XWSE{{KJQ ۲Y j] =zƼhjS+m{'rC/ ZjQE+q)H hht^Vekހ5Li׍<ԂU' Cf-8hTM2jNq=C.*%^9GTlǺ:b;߉=Oj K>v-jя<>Tk乷7^h[൴ڱKwfylCl7 Xbw<wjXR\2 KOI R݆ZE%ltYK( *Mx \uR?P%X,LKvL!_(Ank1S%aSQz5Ogp ء}ƙ]X}Zպ(BUQŶ0jV7/ɘxKv=LR.@r丅bn>=Cmt<" -jη H@eh6*ڷR`I5ުz')YE*ow<|/P_v1.2 CdS{t JO*)N0tqbD w* 4u`S|KH a@3=2׺::;I2CNv`?K(1<6G.k'8h@ǃ^ǽ C14-PrővBJWO֮Q7 jG:驚 af[yU߯֔ܳU zN9|L9 ^`.S)K6u;F3i' ^%t=25FjAfzfəzѻgs\swmYG$6[N49),!LEb'W\Φ~ș6mmk{o8L#A q>5`E9K+M*I< UCE_tJTGl/;9Ao{jR~z6kWHc)j2!񍠳(AY5M!_ N3Fq„WzDPxQ z;a*Tdʕξ)Ϋl 4O;?QCWfAJP<4! t zj=V(}<_adtunGy0l) hS9j$BSͧ=~KqWvU2Bvnۇt':L]+Seo&:s09F̹Y(gr^uGDYܨ#p^]&;hZ2 shA-uۍϡyh*:hpF. -A %`H矆!q$ /n3$RǷG,G ?rw8Q"F^RW7Ss 7VX_%nF5pYTC %@22y\/&)%3!NWapDh!C(K B2I5U* Y/.;e(?muBg%ykrZƷumBAC=]NyrGyľZи@OXc']Y?8r0FkQ𶒘@ǡ׽ lz.&}?Gx<\K$H @3#}io(hT"S3fq#<ax_8ҫFNR{YzKgߝƧO:{t_p_'v4C1Au_ kr_y|~q W]˱dBu^]ETR4T崴zNjkݫ} If_lV.xu[Rw\ Ow0rw69NBg8Pd~=\_Q=A{<"1;_6v K5EV@rjQoDpcôҁNaU]Y5` :"Җy(ֆUu0V-=:T&:O~5oqiZw'zcutGm(q,z/d E=Il7/}I6I+>pʃ;L$ z!nk6*VZF~QsIȰGr3 ieJDuB5 a2탎K.2d4zP-rЃ3,g?N1+诨.u-y޿,NS!JjB5j΢aer,zBKeaL(ZJpu[(6n$ ]H/]qxʍZv?;xȣ2R5R0KU,eҕͦ&PPux.[H8TT,0GE夨+_l#GGJ ´ ȠZ삳gJ߭1ۛ;wlo)@io|a&~9+}#X]PCz,o6C˝k}A-[PYV>x7[FnūTlT1dSe$"zeY,xY|^zkzv,W2izΔݨlxE?[fARR 9c?3i ]G Ηuu Y!20r+(=ƪFYWD(x{rI0Uh]tgvK D*5#UD JlǠZ0qh|P U01˥%굈L|(t6]XK 38e{:1h)vJ ۉO.nzCcc}Q#¬ۅZ2vTcQXsNóFO0#@7 @j4(@;@V$FRPWO<6DsX@c ǹ|I YDooHii@ ]#vMAǟrw&m 30 ͏z ΣOLM7~6ޭyAH0&^n%uw7* oWXޤۍjQm=*cjv\'!n3_ nddZ5#%Txx:C{ B0`Ywr{qweƍ'M;U cWR{E5ѳ%2!,35iظnaүfgo?TKi,̦~&'?c/mҼ8%^ 䈱QT=yH7}xP')} pe@ݬWoAQ2}dUnV\kkx2!.2ɾ a8 zYnVl2&u(&aaPy9 (;SSYqÀK)/]XQCdKb9nx ]( ;w9]ݿbMY BԿVP{~Ȅnz|S=7 A}*F\iR[`'֖X@6,`ӇKU4$FFD):Zq%EPNuf~n|#'QKjv秼 /2h$-^wGp>K.C9D+lRo ol9s=+2&?[H zUpFtZ5c6?THȬFvPLkR˚ []8FFB񜎓z}؍NFۍw_fGc #cf_`h0/E8G»*qA2>L]"a \#%WT̙=QGF[7ڀ.H_\>!@'C`pu++t^ֱ骇/h9 ,W-vP l 1m] *KD..V+s>/d"g5yiILׂ$cZcH] 3Ӧ1-RX[A@RJlA8D=|u5PA4?Ľ0Ja8t/d=ăL-$6GB{qB)ք@m\ uYvx4p[? G\4t;>IXDچYuJVQ)iYN- 5R$r.s# #G99ٙSϫ}hgfnu秢hw+聻}W͆@KUh8= j **!SbNnŮ/KR޻Di.Yۄ˝Tcub LߑJĀ~ W AByUn%ܺQZ\YXBv.,,ޜ9ZFmSNR?\JinmBeza1 %n a|ᚄ9bD0|㹅ILl#7юӧcF] M(0WGP# F "*瓰EG>h;!N=¢fSs3snUk;οmHIqnC*{Y ʵnmi!?jaZN37Ni^F՝~ q>鞣[/3Ỷo: *. O&xv7zB 3_оVkOG̼@BY,?l0]ڳ ʙaעD__ܩ4U^k^x w6[_ pսR Ҏۉa72ĢTH& b*iVS ۔'kKɑ—Q)t Ƙ3픋SLg #ƝlFY_3=EL h2n9,qzN:qyN(;]ބ=/* 5.r~3fT5͋ Ą++7A7ItRc_ ix$b)1HRaНLJĦfw׉ y[ZKTpeWp n1W ?Tˑ{LQ뙝 4Ҵ%d>Kqn={wS K-;/"x``sH;Փ}KjSZB'~\`O;;ՍO; ~X"]<Qns^[0vwnCb5X&ӺZ s^ 3}rˬSDn;{M-q:>uc BDuh2{qe军9&I7 YĞ>}0h=?e]UnUybNT_ K'.o:)L3~Ƙ6C YGhɱ#/n[[|BP/@e`ɝIFϒU|K7Յk$ߪo74>Ůd@)㺣FDM%燐;Prm'Qrc Xx:Q5A'Boux'7{YAaaשd~T `?Y]zW)Q tMs(xRx7 %5 24uԇB5{GYAC|cԑ۰PG:+01V\^q*'ʚ!ckr*RgjX 8 )!!D-5J 6O-"S4X|(EWR8x)n{8f}sfSZj]I^*'ۯn|)a4lHQ\ghy%E(gUVxXxdC55BS80ƾ=J\p9ϯՙ;S*n$ם?j9AQ bH/T?TV^44 L9}_ SꍍJ[{UmlO=j+5) {v{ 5=.#Hw4EC"sqk(tF09OsSmVҿ w'0 KdW\pD@E|`D j_1_P\ WZ!24Ujʍ6 QF&}I"j/*0Z8(>0XAez5 BΪw ӱY:´2J'ݑ;MgXᑥfLZ4Yu((:t>óXE['($—FEu׍MV;Xp*RxŤs%aޚ`X|f4HTQlAPV(ꕵrBUl+wr=0dS!,qm xe,G^YvڢaL47q* ]BGXcec%zFz{A6+@OK!ī'5 6ԖsO˃ p>O GG;B_ox25Ěu#rq| TI¯C 1:GA_Mc#!R-w?΂0}"$M@n$?4dK`w(ɑI75j`pA-_-ܒohU*;wPzQJ:{ᤢ0ͤz-Pn{)Q>CiJ+j}ΆS@׭ŗ| Mz'!_[!v,P F]{ko_H^v2թPƂƂnZ:t,fG/[*/_'5V_ oK$? tXuHB 'x:\Cr դ٘|fIݩv&(=G XOl;cH2t &wdfA +I|U9&U0sGaH^(eBn !=oTjUv/gD,V2?#2Ъ>L[fqa:ϛ{O+ikW4/2~wqNȯ(øjk;0$Ftjʍփgo.57HPGxa4bl,IiN%ja3aȔzjt/2_@+>ˌ ze(_@bZ~Y%teqNGr383RD !^Rx=wJVo:hpxf?L}^.64A޵a/ɈCZ, (f}Z,- Wj |Y,{н]0Ƹ+X+CiS[ e"uY_FfցN"10 Fd4 z@庎T댗z;އ} tP;C 2}ӌ}پzg^g xKẸ̆UGOnk+{VVM Ϸ66j6cØGo<:v!-( F=7ǠMi"`knhRo2w}x mM;a?Պݲu_rK "aZ}аuzczm1St+ˋRq%y S"Յjq%~:$X%(X\( l݈^s-!t|ֆ֖[շBg7ڜk'Bq˖VtL}L `/솯9JQNgݓG#0Y\޸q#[*.yRiyy5GWXcV VlP&ىYrI Vqw<J4/nN[83ߒ@;p?'15LTCJNyNx鲈 mBq1Kk1hm.c1;O_R軅Ҧς00uaT(ԾVz٨ ywpx잍etD(k"ab}P1DrBC wen>5L$gLFF K{;7 \}K7!^r CE @PWNV>rݺ:9P>=A}1*ER$G''"|ۇ/y7zQf$)W-@&Q[ic <mC,?Da2U"uB ]aܱGtNGoDp;|R'fjvPxBq€SuicE Oǖ/VBIA(~NX.jb—A9}i܏M}ə[آ?4ۮ[h&zADP1i7NI./LPRc]C~gjnlJYy5GЏO ?e N/s76^ƴЎO~Il ʦMXs~zJMt~m6b6"N-@ҽ)_zl=U}H:לO- ԐG%1L |Gf i"OܸPg ]8-2Z&+_}}.Jۥ?WčrmW^s~*WP<:n-58rfʃFת#f=sW%Iw^ i&KJ]MhHR40f 1 xoj,̀CBT!4;&< Jvb {Sf4A\+= ću,qJKj9Y0[gzo3dߣ q0drRd {{jc%58IpdN1V7*VNyfA祅5'ѿ.bJ JL`* Ҵ;B;7C٥K|dfH0&X ؔ ,ȕg#K9b#EiC YMw5#atiR+͍ 9+ЕFfݬoxN_Y4IZX\Lዎ6EbXȗb'roMac ]FŠUru=?JTWɆk*bK;J#%E)%21m.Iv84QTw.3( m &k܅P>ub`gL Gcd`O,+jWS鸇lj--~V؟# \]c3S0N bc^de&قsh~:D\93sNf>l8q~g[ծd Yr}"-!1Pc?c01B C5@&@\"0T[xPo`c͊fܨܩ`3AXmvKl Nq}ZSh!"W[B :?~"NYDI`6؍mXC얋JNXX\\4G1ӑ&}-řR29C޻>@3) ꤷbS/zJ׮Rܩ)`? K̹dkG%|T2)_?zQu.:# rPb)%~C%5m*͹.PuP\-zٗh_e0ŝśG^Dž#⾘Dž NMqi y\xee@S]ӻ]')]ZVØ ',SB7 YZc.ākz5I c@}kXtQ +~G7:+Td.J: *|JIV@2Ltئ@)lFV3LT}h#)AH\\ќ.)n2|4*ȏk/bRAcz)MS XTj~jjS&|!K@e_\ ՒWEITk7>o8mP> !@-3,la@A@3rs~t ~QBF?[R@wF&u\&ۃDU.Ⱦ[=̜~Gtu 2V&6|Ekǹr9B7!IE*ЈO,v9Mu6| aRhD zA".JܫwZ!ZlF.1>٢c и _nI}r::scy4̀X@]Hw4f6sD.TF]>S98*[ܭpl=>F|ׁq٨<ēn+;x;̻TFe}< 0{[ԡO".м=EO#^OT,8{nѯ!vC u0Ki #{:" PԖ7"-M6ZnGFH38ef< rs( xŰ@@K Ԗmz$X;Xn$B9x`i_;(7iƋx Bnիg8/sgTVwZQO^G!fC~5-#s8pk'Bѐ qOuUMPՑ :@^| 4񟀒Ѓؼ u< |<Ýud m;"DZӕʶn结ځe]ه#v(Ht MtK:f.(%!' .Y&XQ=E8[Fv. eK_eՍ};\_&l3`׍睟t^j;w=DO>Z(QSlVK(V#~ 1XYTL]3if D[=|\5[o: S̪U* =y,ɕQq*CpξD ox.Ǔ=?:AyDse.ycJ7>QWnd0`_;fi$E~$,}ϧy,0 È?9uzH݀0qd\diTI}#ȊCt+NJup3dd*Px֓=t@ydJOޕw#?'d:t?֠u#ZyBP5RVUhzԮ,S1+`<I^njӜ ;VDC)MQLho Bw" ]amb]:kTZZqb?1LtթV"#6:Ω ڧ0ڝp&\1/y̫Rо.=(LB4NH<5\ "TC"q8{yc@\88 SJ^Dm횴T 榾Twz}Ry[fZs?6Pę-A=!&OKL %aʸ_j&7GjrsZqLnde6r`) b:k SA 6M30y/1N VZQ+vMo\S͑a $_TOe`(O[;4JvP[ K%4 zz=67>X7_v"7"I"j`xPmSii'E١Tȥ-j|+@ Kdl[?mH7!ATOg!Z$$߼jԷ~yUC<PMN^gjV5tq "H[%Z(]G qLtDGp yv~=Axk=^ \4jNMXְ /6!7vaMIyVH rvKa? aoSR> \W[@xܽȚb@_;djL YU[ﳱE Zy O/c#m5U 7O8ҩO3[$zkխG>Q\? zvCht9ժnl`K|*y}4o{AMAsa=Md2JRF<#&au;Q_' V[YXb"ԌI^}AM 8ΈB0R.bwv)4w:Zl G㎑Ԫq5s1CZr Wwmp+u`rgSOf:oa[q6фcR׊7 ŅKśŕo' 1anqxsiiaybiBqiP*,X.--'(.QG7Hs{B:SSyUZЮGr@dvZҥZXq |7$̲,ҊlUfM@4 y`7uua\A֨5D4p "u`BO\gņsu.Ib(ēM=fqlT}iTZքO̵3Q;(DRJ%ٻGJ0{\v׆E,> 0"\8-1^G[:*n.[p#>`*7!ub62mG7{,/OsM~Y$,:f1ƆIկa -D6kG5$yAғ~S6:o /"n,yl7>jS§2,f?qׅ|i!ckfGY&AD^ye/g/*ivΔFehoI8}4*~KFϭz Ԩy#L'YE"MmK9/,6 jêMA8N֙];6imx2=e `Z)TqPZ uKc8+0…6%"ȊE);t>3Bيci vԺ\Nmmė.3Lzv-{y'!˪(rNbS N}RPcyz5>A| a(d`ѥNp .+[pRSs6F囜TbR酱[q\YAnӛ*҃Cs҆} ^Ԝ)zl+e4#Ꭿ#Qαʻ/S܄Hr症{|yktgv?)1\ugo8F`~> v81uhhX`;9U~GuX*Þ佺8+N֠@nJFE^ztLkN[un^FWըlWʭLOg控mދ)/⽎e9XVq9K"|S:@a#5}f!0fQDջ&0ج֪YGFqtF@/o(Y?||mnŧk87<`F6v7P'>s"f1 <=voMN|h:5[ @d!{YNFfESB 4\50 -@ `4u]5&>)R6vPKAEĽc-L{7lagTiwlNoqNIfbe\ls p,Op~5ۨ紙F^؁g[V DUMdcM6Fb\`ɶQ=KAXxmJm=iv:\ج4^wl)JM~LӨ7l߯s}=SGĄ)MYKyhTm5d|Ͳz*V˕YygvSUQR`{$p!ڱ)aL]äֵ w~fn`Ӭbڅ+BV8:]V<}<2x(;UŤD1–:PCr$@5 ^ 5zIT<*+n @2acK/^"K>. (z]7lvQƌ XbEتX|%ͻƊZ>%,+'dI ɢp׹7w0*++TO_X`'.pnTnk\-A9h BqYT ֯uB b]fzy|0g}~J;~'|9^nnE?;vVFJuimi'_t 8;!Ǭx%u`^^ꀨ;-^荤ڟ={^SB -#Wd Y/ _,)}[3]ƅ\K0WūvBy7_vBKk&j5vȍNX` ^8 ]r\t^ȺOGAK@ޚ8ՔO-X.Kt}ɩwznhԋ2eL/]q:gxzxA!%g#2=H[,Bf1:nY*1tjtQ(zAPIbdȐ3t wt q:E T (YNN\\2Tjg G8?c$Hbeq s4I`ԏ #rLof} ڴFUoתݽEݺy $Y ЩHPA( ca]Z؏Bӹt"md$ۯyGl;&rD1`N_^/;!3iΔNQYoT_s4guF9' 2Vdh˻&Ih԰9hq\\8s}8p߻,+E\w.O|tH5bC_uHWqAs.m9PG? /N~~&y"eۺۺM:U~m^ޠh *7[`QZ&#jRA)>J s"O<Ёe:"(X᪍@BljqJu8GҖiRLĮvgBHඅ!:Wv{A#}Cj8z; Ď-*wn`"'>Kfk!@ؒXLM}eACrp[H)ǽDC 08<KL8VGg|rxx0jNDGJ'v0 "7Nfco66V b 8}̮g}~. 9 nO\-"HD' vO7ꀖlBAp61OĿY4z\kţ^(/1}7xϫ;rihhI3LCR_9]LrU RšW2& g;VE(P?B"ౠMg"V×*۹ 68G}T+FpKAaXD'~Xl? +SMG$hбMpz>t;Y:ҽL\fus"G'jUz:Y0A sl4@K=y\|5-{c"&T#@<zM#Z#x8xc+"<4]9e rβ7Zbq;Iͧ~tXHoG ? Aq9q^OMKo{2M-"(KI dZe4Ra,'`3[i)O;߮# 3pɲa]ښ'!mR^*sX(D]=&a(v*hHm <YV=P,A*,-}.3킨SVW;ǒ$P3O3xi#&s WlʢGe^bC;`+TM|&p$KR dF>,Djj5> bi~˷}s֤f1ƈ4(Yb"[kOe&LEu=CG]ޫiK9 λSEvn#q}8 0ǬsTZ؏O`NU$='zI8J],[80;hxnzژhU'An/V$vzt%p6>Tszt2Ypt<Rg;'|#Xn)CQa{Sx0 6qsuwqN { גqy Y a9_`ߠ 8g1ihX/4Ww8Gҋx"ˁ:M8) 8@7h3>Ȑً~'/Ɣ"VW˩mSM}$GFm7(2.uuU-&BVk#=oPPvfC>\yLq(f]~UCGb(ڲ0~f|VʩT1+}|Yos @.1`":=:.Ms s!g :Hy= běCIYog=,C 1"ȏ`3`tpwt?)۝D΀gN1R+hSRV)R|Jzt<:nA KOMTǙ3+NnYO{źACTY"zDᣁNU\1CV H *i ЁEͬ*kjc;"}!;vOaW"Ea? .ѿ nǠkUrQ.>(K;|̗|&ԥsb} ;bʀThʱ,<麚 eF́K[ }K eūAp%V&`i\|Y+w6ADp@:DV"h\ 1Ţ]2&y0r#|BNckpUᑗ抋rx} GT"n-GP%u*溍 ŲDga * [sbEe!Apw.B U>Q2+KKT@\4C"BHʨFU˥b*`Ժލ+7J7c)BŅ待zWɠGs6oKB?!@ MڦD(q4u+{_0P{7 N&AXG|X`Ĺsg)RڃE[ ],dE6`bQI$B%4%v<ۿG,F NM.̳>ggj3+i$<H$ivO[j*SW&׺p[U`Y €'݉ZX=oϬz^C]^oN;:qʄ˙ #2T9^iN%CDI]hFDڳ^w~{{̻its/T8ƨ  Sv:q lczjqせ֗&n Z}>J@㰻dɒ|DfO͘|DS5{cv8eL18y )᠏W=ƶ [G!0(z{JszjըlpAE=.7ȗ\Wf"0zt .X;qNA'GLvKO}n߬xՕd}OH>YPfQ bidOԃBBv h݊ 1`߁6;F5*.d? ߍ-i´\´\+"Eh \4=>i_o$86~Ծ^fsh!kRnR^cWT=TP{ 2U/&EPoGrDՃH[7@Cg ]t1@!|yMߎNaۧ7+֙Ȭ1@m%{u:vfό[e/Mʽ0}nyF,re^OyZD,?Rd_feV2Sqewd^5s?0wꍭrM3rEO2ȨCbȍ~3oo7\ښ6\5Hf/K'-u @p$v'i a,HaLyk3eL1wKPʫbS8'?{^B!/,eʙ~9ljfk{l!덬n&yIn; ДN [v}P@ cO8av-gR [.a%5 {=J=.'^ # "),CȏiGR$L T։z6acZķlKF33 L3E xs<Qob+ϑGj5.'%Ŷn[ϧ.+7 Eu.z]/zea ~]D`NSK&%|*{!;"CuC{ĮԆ#@ԋ@wÁ+!wFPummp`S`ťZSػbK90ÝsxqYA{J%Ṳyk0#i1uWdxQJP=d͌ 4ozFGfkJ J}T]}48Dax^dZA] > ƙ7վ3rZq77}Pmψfgf֞ZAtdK߼rmۭcZSs͵Mq-S+aѠ Ǚr(ÅO9ؤ˂BZ ZE~I 7Eڹ+NاF vDr_ , @ox hQMqpFbc(]*߈B8bKI,#jǨ-%'4b}bz#cv%^'5)44ur"r 1k&'D8N D(H =fRQw w4V'_'E jp{nE$,WCH5at# YZ`fI#Kh飏AI~˔AAj/Y >53,F#-NA]r1ͷ9t G+wQLCMA(P8,i! QpOQ8a2[0ڗjm8u7}vqPyX H4_KU%ԙPq¨r*OC'+ #kޒ6&ƒ u42^mpn}s %%ge ڄ)CYI|e͝L<^no ޕȪ1VbXR[yo65p^T*5o?礴 Mp10;BX* ـb|H*Qϲא5P Π<]&sP{l/Fꁪmռѿ.r@<ս0 s`l`7hj Qw=5piaB/T!&J/J(eJ؝ jti>1GPQ\Ր^2\,GbJ?]ַ9رPZߥ,:@d <\}!ID5<1w{XӋQ=^4 '| {U3s_ 绵Ez,S$9Drz$@̀?\;7RF8V:3vUY2-nTA <+Q־{[[J a$"WC'ZrkC jNQGQl`ˍXcӇ?T]&{Il8qx䭗UiPj$Gct =Q8rŨ̂mG+f. v 򎷉'e ([-1D(%Ƕ2Q l(ߦt|3T %4]oG~3h=ڮܔzIQLgRO˅t'yy8< ?$ycz9e%!C+{)C%U%,/ =&H:+X^q=/O 8oX wk]{,BgQoID'@/lgsiޜaq<3E IUh,0lVpD@49@]'M1@=+ś˥B2 ,ť7oba1p< KK BaALqś7o&/ծk]/9]xw9~' !sy5唂r-V ;A>\r'JHJ+Z>Kq _Be'eyYya3ҿ3L2+o>nH*@[y;HWyXm{BC|}o_w 2rr4BX"]I11Y sاqT@آt=ǟ YeTv4pb$.uYK:ֺi-9]MDnpˁpnJnG! N*tNae bq/TL}w{d:{88dd#Oiח9?5Sr߮oAQ=tp:fc>|! z:jH Cw)ymг ɒ,{R*kȏMtN9r@ykf߶ra2ogAqS 8AUa AvYs 1Rsʦ~l2ڟW Z`5-Q bH&X0+~jy޴`z[EKqgx6jes"85W}b:2_cUݑGzwS%,NQ7*NS=t`jVԫjJ*ٞ{&!4΃͆PU>6OIM&ՁPrsF8+2[^"`|ˈ oAfxCvOԶ>$7t?Glą.DSY^v?=6b7m/ݩJ xIcE'W)[ȷtWo3PMt4S+qb)\U:g{%etR?\U.|ugu6F (hkf2 ȭ wc٭9&R!D r.7ʝʳ .kE Gy0gq-.46UVgYwICbceGո8I4Ys6q>ja9(-#:FD~ o'Eŵ!HyVbLUjzx1^=GSsZgOkP$>^7AeIo%F}F/I|A5ѪkuyN>Èߤ|RݪQ$h˨ ܙ6~.)ň H ռQ݅dR=G" y͗s׻Մkrp;IttV'K7+m!)D6_y1/ؘ s`j_%판 V:6llH4PЁa XR$‚VhbbS}hu]5\SS29ĆQ] {ս~Lq>)/"9fֽpm6& WUocL`@ZrK4c2--Lx" !SU)b|٥?=)ZglƙRnVk8?E1=0m-aI]3ĎiJyE( :(b31lu)J򥸹T(.,X)n:$ f^0Y ^&N3jnl[X7R @_[tVa5%laz:mvϔ}g߰Y%cŒu-fK+ٕ%wF^סkU|gp1Bi _P⚺fymUQ!wlhw7:sBV]h.0Ot8.>- cA8&>fPBd|0I87jg A *u8V'4!&ǖZB~{SVM[pð(3YH RDQ `Acd^ h7:qw`):6%i1D 'm 0@wܡˁݎtpx"[7p zVzC.Mb>/ؗc 7JRW<;gZ.|Q_O!hbD$(>?+*}`;*<}sX1yggәofY=_-KX@S K|߫D t\vԶ`D7oZɂ -Wτ bNl( IpdF,_ZSLOL;4&!,ϽrmcSI0Ѹq"4훎$@>KGJ]ay5 %Gxy"f̝(d 9M Ҕ8-6PWw9T]5矆S `F.aB.F Qwgs!_ -ڼq9.b95 }?%Vv6>f1h #}Z;jO4%uIG(F"K&lIf_:"k@@Oۗ # \`K\Mi)> 1SOԄn!v}do~X&Lpsb!^X f-yZ=V3eg@MNQ,Z:%C*ӟ z4hpe. ,ֈ.o$HYc"%j>׋rHw}>b\wZ_p~;ӶֽZЈkzLI4#N}%3Y}r=[,Q?jWHiZX{ ȵO~12m7shEmzxı3uٿw;v4=i ~K G`8vԫ{WVn(\+o>jVyE2~, 2'}Ӿ\'쌇8L:,& T\KDu$JB`).@bQL* Jʕ7z_DVbuw EF|7U!S'h0!M eῴ,"ڲlCMUGvm)6s+ya(TV #k6 tN^>.,_G*tc?e|À }xd6>&}5޸r=M+jrT:UpZkTS$\cAL耆|s8vR3tG%I6ʄoXND\K9N;a nFk_w|Fco_7uߘ(< e_6WCMB'"SA%"qz ɷRHhz =m_gNH&)i[hUʭmqN; 4;78J6mכmJ6wLӨlEq'm8V ~]Tou x{z =Dj0 $O6XOtCB\1AkL㾄H)ȖK8\6fĥ:-595aV@6>Vwdu㘍W660qqCvXT(ȖAFDGfL^\S?trڢy5!Xqs0Rzx&`J8˓@9>Zԋ7O>X~mzwGw;l6|Z.R86PcDSPυbD\z67myzG.кlXx@axۙgjrljNy4#i3f3 I\W%r39#!K|"C칠dW&AߛHH:(j4ҥ3ŔR uԽXI ZWD5eII;Xu+y%L ;C7 Lj -.Q1>h#-POupkշ)-5ǡy ̡JvZV#}Dʵ)pŻ?(3 Ⴝe@ɺ0gZϛE#kFiFL(45jm5(gdX Z~W%l:ok$K mr,EPI!,ei(΍M` %.ߟk`լT={IZۈˎ%g)DdK{[I JĢhJjdBD x7^J`h3^A_6oQv*ѡ̔P^ђhfgl +'v®8wjkr~!hMɸD,G0L"2k8P=pn#OE_M}b\aShE478WA-&u)B{bb@Kbieqeq]wVWE5G!)(-B1IC1=A ˞%j5,Թ)YM}@ c⍃nQS0#F#%G2SZ+6(5ǰ[Uf}ɜ{݊ž,F̛1`GQo(-0Yrп%לEZ4|s} K8NmkBE($́N#xȑf 1Q>UobFbk 8JenJ-uӄb"8g{b8N1X)Z(2ZJnZ-̃)j\PW8^C b=iH6}Tސ2N'份HDGҠe;BĈdħ95|hc՟zE8Dqn8 [:)8`jݘb"/J,oʉC>r7SXBz={7J pT͛遃~8pGHR>73,I5%t3בeI!. ͲKY6"CQ/qm QT#TZxrhh;AAtG Ex8yՍ[k L֪ k/Fl$^pGwo}T-QTߏieampx*p ]LFKoȶuXWn;y7}pg*M 5Τ;-i.|I֬~7]]k5Ŭ#Æ0WmM;=lx&B̚)hLh4Jn$M^SAHZY%)#/NpcHuw3"-3eeRv {Q;۾>f(" ^/#_jC8ZsYu2fsSMÖZr[ޝz "K-vz, \0Ն'F n\\4sa=`ՈT;C'4}|hugLju~ӄ@ 8pɼtTXicE䯰˾|wKۤaRۺ+"9;fI<Jѯ8_}BAp)3|5I{W2`i7pT[zv8Y1c[F1xGCl뇖|,ATԨGh=}g|s֚`edҦC%˜?Hks:qu ;h5]& YI~+-6Z>em$WlV@炫o0= Fo|Fz@"xR8˶'dysnT+1nkAKv1{(J,AD=k,L025i2hk' s]2 qյW-7>ExIhwWCsik4#f 7~#} A πM >Y'@{AʝD`(&F>qtk1]8:,@4Q @55P9TZ/!$%B$)'Wn_PUQ oƈKÀPe{P<#]9ƠrE q+WDSǔ'N}|w>#VQ||9`%ۨl`<< Ty؂Ҭ~ C}fZU-o$*i;:a݂B)M@wZj 5^pj\omspqak%EG?R뉯I % ;|MhYџT{ĪAdEmXgllٚMxa;mrf>upf +L/#x}'R1#pCGOv`UNڑElZTOC@.; խ dfZ0_y7fL },51R6S8,"UȖ@I %(ƚ3R]Pfki++G6̥8jd! L&QaCp.BC<ViJw`=D(쨓R B )~}ݦSy3nCMrɘȑIR{aS'd)BU@}Yujyy<]]e;nM/zy^ \V޾T!w=%p 1ӭ{5hK^Y%5;O0bBܥ $Cg#) )S!u 9'e:F A 9Z ~YG) ܯԒNbߚ_ E9EP4`#x9yܲ3"$nJq E…PCfE"l  ?>-q#ǥesXT`nn&rF1t/ɀ ˔I5\Spi|" B.8fn$G[n[e퓪6y0`Gpx~OPW'tYp<-_/ [E,;}d4|l! $ySZy0_:,f*3Xڜ"VΕ2 8"N.ĎMX񢺅mNMj\rݑ&t9 :oŻy76>zR쉻(_ XhE/:#Xw6y|Rf82k&P{BДyp0 t}MU]0AyqܝOa޻KN{gV[͠/T .;8qRܥG]y'sb-d=2k\OQC *=Ml9|`ܧȫ#N}fAY8Mн#|`ȵǺ0Xlݙ1"Z#!` F<ϾS[Vu!g6zh~e+m!F,<; WxgvEK;_^5/EsomP7}+YK,|554 [2 \%vXu l:O4ar%.צ9Hz^!Z|]'矝3s|Q,o:@[hUC 㠚(3A+~} aA;,J?"TK;DGHYU,5˸*Y=XL'󈊅۽P#ctl*&'*p$2 ;1i/|_}y$_!b@[zh8}q9G$% R * 6%M^ȵX_(,@zr:&Ym_]Յ>\;*[$Jo" ?BSW_" Ɗ34џQ$҆$8.ex>33#˅BU͸`Ȏȃ)!U/Oԅ&mvOO #Ԣ93tA)7fJ뛏~NT?lmzgzyx |C;>'@J|mR ߭xڑr2>Νf1-e7̩F$)^*k>%ADXͧԠF}X2D%{|jfOB4:Jj5a=}~յwݷߞ*;cy*L呗J^_ßZuVV7*VN=v{b1_"]`w6*ZVQG.sRakVe+ì2io\سWx ;k wZ]ah{,}fٵ~fcN6SHk#wvF1#}R4t^}pZHنs6c/iS }:9% qJd> PđJ+oůd>hpu,p GROĺƻ1بܯXJȮ*V`7d -uQpq]Z*[=3, `" k 31!xu?lY&F@.`K1hN0(G|9i 1A @jQX;s \TzjJwA pN.Ĉkb|= uY]s&-  H-jY5s|ZV>;  8> :6ȜpD>$Ʋ&u;_φWc 0 ըK Z^X>P=2b; m)XO7C(A|(8}:y0dGM "jm3gY|NOԿ=E8"LkٞxS|!%}VSٸima8O}/dQʤ qyy@߭ >g k; lhtHfUZ0(F4;ObWC>5ع'gv2Ĝ}tXXl_ Ikt D,"H =ah׉V->ipu-˅Dl:kӍjAA㺅 QE0`>$2:)ZC fny=k/?^UNfƲQiUk_ؤ<*$S8`B//soOK8Ew:ͯ ZkވL!HhyJ={ْ !4"3MF-ڧSېðBm/ }=f㜘e*L[*5񼢢 ,Djb?O1My)SXupk*Dh@b]4lg:?9תzAz"aۂt\$@b~== p"3kջjet3f0>$-K:uN!B*pZT.(H֋=t"gONwO1Q<tOwS>CS{+X pzEy1݉c̚aNm]Zpdn@ \͘uVer9µ:)a![Z} z ]I9w?c[WM 㢙eZy"H5k_6E̦3s~ g`0@%|60W$|+gqSɺwʛ ;kKSO}oY=Ql 8?dƷZ%pREO`}x`((E\!P(J+++K +7Uq[\rt/1bo/b{wÏ~@Td.G'~ cZ0M)gҢ8~_SҏFeU|K3nfk77Ԛջʆ*`J"oN= % }ے"ʜPMhJzA}SWg! Ju8sY/׍x򛷛!#isu6$TʼEpl~uը[rD_&ZJaf}CC,DgL+bض `C! Wv#扢o[)kJ@H~"'An(Ut !`v͠ |b#$wd*ڐ!c .jV5ݿ7?̧7YPJ3AAC3$qnQxՆGH;h4['Tu sn$U<77q5#UW"G:Bc (T3@pLx*r^P1C"QҊ9NY(mq`U̓Ex饖2%UlG/6O{m>Gt~Tvh %j\ L#1yP4;SE?=њuY @Vh!c_@j.g$Ѧ̻Gvg{N) ,ѳD +5"0dq1~x@o ,/`8' VrUp80ݏ說3\l9ILj=HLqC*;\&}Rjb 2]@ _6 '!2!2_ JIQ =2'nObfedME_lo7Ye`Zh>&G '-y#*P 'bv7_@_O1?wkguv^Rm= 6_tltns% Qs%أ\ޙK=H@<9df-lӬE  rP(uKD`r`<4ht0j=mG4/:L,HUb?yO|d&{;;W׊p`w1DE9Z8gVb-Մ 4XIKC@(JLSB^ȉNE/`gSJΝyoqk {n{&\@ `Sy/8rM0ǾW.l!Y~ee4[Nug"Ko)t"2f JK2-ϝPsyK _qhhzK2ɱUڮ7M"fy1!!@]C}t/<DFZZ.:Y.㲓`y39@}LEPO(K=jtΧ%S;A[f ;|Unȗ=5O`o? [K|_%P@'<QJK;2+H(RGb[:Jqm]į mǀFckTr88=Jp~o2Y/ma|X1h DґEp'8j?hnrz5_(cP?BI0ޞyE?aCz+;Čj/5F۸j]]hG:eC Cvڤ^$6?'z$ *,/.J8K,D乑+s XX](WC)jZPpn3瀷e~g^k{z1Oe6"tu/;"̃bp']+'! Mh= Zr#Qղ\Q) qR~3\Vt>Oo|I21#F%Q6]L1烓љwE'$jj<˝0"G"zӞƄMb_WtTγ+^2T3dW_mS:\P4rfހ$wT;l|'Kz?&.-[?F/D)G `=>nbms1u0h7t6G ., w8d*F#Se $1Ca J%w[N_fWx]9uÈK.ox]yKrϋe8N 9?0SC~uH۽!ܘ֦H@kjs# JLB !-Wޗ`z>18^ -<ȃHUhX5Ct}?^1 Dup_7SV~w<T?[lū^'QbVri)2~*;٬zG`p VAXKf\XM7j#Th{/$ɢ.icNzUEkgS{2|ApB:gAţa ,cJ$;j:Xe'J;?PPnd_tЁ:I^N}xjs_"SV3Iۗt w}\=uGYsg`L0Yo')JչN%K ? [ $셗0R\ˠVެ¨hz3jG>OD{ h;Lubӵ~lzvhW4/p6g?c;Nj[؀ i&?"F$_D(FT[㟅Mtze nuBa~"x~Le|\+U{-7>xJ/O-"qoN3DM%Zo1x#D틨6Bl5$ pCP5s %  JC9d bV)ʤ톺4k[3]ٍ: hK}JN\KVWABT5J/7l {!Hd<驄"6 4 39s lJ{lQ2zyu DUƤͺ6 dfPv`8B⍮1ltk[F)kw2~j%%Ǜ(;A!8~-h$LKa8Ro$lqui٬oWKUԵ78 2G1̌^fk1Mڛ}Cτbl9" |r0"0mո!)p -:rЖLfZ{詿a#&~T rt~6|wR1єuG4blM^i'ԩ$%v~y:@3gBFqPbcT1 aZ}I7lJI~O~.i5PLE{TS#RXc~﨤Dާ"%F\uwա6/߸0L e&L!tڥTO9RWs0 s|햚3{X3͔ ?]Uͦ~GjoZ˭zwXQ0%$P ;cۣp0|  H?i)# 2D r/ouMҥJT/8zT(,oe:3Nmf')"O3*bpQAѿL`KkG&τx|bؒdhUIWF׵|R|fB%$w_Q^}nj =Qup8] k4 ^qG,*N1$Kj;?eU߯ (B~RzTS@aM؉HBd~ݎ3h>rhJ<}rx4ȩthkIh:*՝-1RRn0K"/̻y\N]7ٻ_ܩ4=̝IwjܲtOѸW?s _<={4npm2qM.4{ATa٬nmoϛM,]+D0yoQ+Z|Ic5FZpJ sꮂ࣡zwR\l I>R|̺"A0E<jKOh݀;`%$tC~BCd~D0 -r* E,`V۠o1a¥7Xig|N,!vsUkppݡj:~fo\1p4&`DSPx|w|XEaCpGQwS۠PPi,pr$%b 蛗~l":2U;pAK7*w*|Ȭcx=PX>D2*yWtXG&қ' h(QyՎ@S2yT"gNC|z]ϳ@ ITc9sRtK6P@O9Ÿs\H% LK d]`=//yŢؾ0N/۳bf "wg2/LfEΙ,ν3u&sv&dn̙eXˈM ےނW\[qoqP]P,2#Ft)V)H98f})0]/0 3x{gZ?LB}JguLEoڧ~u3Η4gf xƞ^: ]_X5Y\P. 򅎯pq#1Wn?Ȝ&vVfwz3-ijǃ2>bL116&|=*_粊C< KHZtp[Zc F(Eba 5+d]ĺ HaIhZtm(SXLC"FxLfjL4L!GnRBI{8}z.]` !6sk+7D)0Di& LJb{Mp K|22*@5nx˴"=Uny1g(E(b'BEk:l VQ *I?1Q6DO]Sn1q{c}b6Fߋ 5e3}z B[;:ۙO4dQ@$C~cYQeDad:ۏ8`Λ &>z!@ꮸV^3ȍGv 80z[dݤGin1B$$ف:gdF\2:&,u-SHѰ{tG"(r!˛V@}.3:JFRSF0JT7Z"w4VQ&4 הAJB"w9  z!FM䃉J<9:Uٺ#$mX/]9Bj=Fs"Rz&J-1r锐Uۆٮ z7ǵA3MIn,Xn_\ăW˗{t3n^<77)(7Ws Q ]-Qhhl|۳6ϼ[uOra+6Dͅ7  jb(|ƍRMtpj!+/ 8{jIi%sz1EA-5ǝ.L+]G4w!]ըn;>2 *K1CcBv`)C մ9PXRP$E堡+)*7w:?Wk>QuvǻJm=(--T7t w4">#VިzI<"虠RGkf %'DU!zHI'l՚˃ّK( J*11~C1n\Qqp0vRUjt[5FDCixvWer`< tQQYH+Z fqn3+ 5 ˙%p2I r wvg 3WC甊ٛQ^Tڋ 꾓Xpq126t@?Q\7FNf'2@ӗs[-';"7Pzy\KKzDU{휡pp:.q(V36qNxMIfL i{Ӟfq E]/NQv7*g<+MX"uM"?Ɍ4+>]cdD+rQZi#x>: j]n~[9SWyS(_e;D2l UNIkdݑMX4:VQ, {%f =~z#7v Do"d Fܸ~"ޅJ/nXQ{qq=~W HN`E瀂'!7XZȅ_O0?>Zr;8Jl QEwH_u,F.wD]*s P:hOu_c aVJ`an\TN(HAԩVH!:X%Q߇` R ;C j l`b V0t;Yı>˞7GQ۬nU[^֥^9z2Slm|y[o}W>bt1j=HLps!>XyaDz3imlToׁNR]Yg&jkCCqiT T%>g_ק C Kuޜұ=ڕ5ף?Bj ;ίJ03KDQ0@.vjvDA~[ +Ai5JrM9ۥ0SpZݻƷ:OJϽl51lRmi^  'p"^Ęg^(ry1sRxEqMlk&{U 635E>| t6ȭi'%aLu(㺿,݃+YV {Dـ!b-!z[Kܨ+Ls4ȯE؛źm2} 0#=W 4Ts2@ENdMe QVr;ј2箻NUTy"q c#@gpM `XR(pOL|j*%}:jK8Z+Ώ.R&/r^ؾ tz?rayai)߉KK7 7nK+פ L3gftB0iѢ+&:.`~D M+XucP0M"s wx}/⨋[K+ n2;R3)siY] oXY(q̥x*wUʍ7< KqDzoXx8A^Z{VDH쮣AXGpb=ZAክ#ɅGƲکKq b"Ψ1I]cs<Lj]x7yV/_>&ⅾg_HfSpYQYo;}MOGcT (rJa0 J>Ds^e'r*pڹKoڬ*-xv,Dk~<}|`\@h8&#nlP|SIѵ|׮%}gS}Bu+WhR \dr%h]r'^3oV9rLЈK$0W,& ǮLy35IOl #cYhkKr%s$ $̘ ?:OB2աӺj?/hҊ/t+TZ]7i] ʫK~<$^FZu]7y-EYmF5u쓒<#~O8vw~Ir}#E3k& Mcr"Gҧ@$-q|W#*]};,eI#vg8-^}s>_/oT\Ba(ᰈa=v m#0Sk2٪ w3㰪VT}7H&Pe!2_7$݇,%Ͱϰ4 Zт'$mt.5n7bp tqݷ'^%0X|m/OvNкs"kViVSKH7e=kj;7LfQy4Hv6W|'z'vN/-ZسV jw3yfT/4ҚxDeuv:;B8:0,YN  '(6./TzY 8UX`֐GNꒈ>\˭(iy vJ*A1F^-J\ndfynP\o&u D5!L*KK#7JgSԦz^#A_Z@w" btB$}Nȇ&x DSie }q.T\.="2uY GP}+-j *\F:6@~ֆ`]*oey~D !bO2˅hJx t.)>tQ 6[3oNyu緤1mJbHB^%Uv`㤵j'^dʇ*b]Ya;ӆ)Jupg@C1d8 =bLlQ;*(tBFobS>QKsvŕ0S2?X@_ggY՚:t^87ieA-EuNv# :e (#'ŝN C|ę,#sAx}7Ϯ% M`/YtI_|pgr$:L}Z{zT1^Lr)Z= b #'[Zm6ӮZy|kkc޽f3??Ȳ.MH.rc 5ZԣY}Zj'`˿kUhp~ji9\jg\ cT\*-,Jl.ïqJe'tL4_w"nȻ=Ivz3!jq 1o:DA"fE 0T;*;"X/h?Fw~ Bs(ڬ7ⰷ N#O_hB>,0A/!6m85չς ۟skۮ{Gc75/ 0R\XZXX*\(-,/=Y!K!zO9ϥ^Y_{)h_0arԻg1[ZY̮,P.A/]2p ITFi!r=D2fX)q.mb8@C0Ƿd0X"sp0u!a\o0;s+5-`ֵor~k^t-Z[X8kJA7ꆠ< _ië́?=:[|+8E]:, >[&DQs/ 4{p4x fJ , Ρ77f y0p%Ebj W E8W@uAq 1mya)7o &3|JW(Mo"gapqt>9n.Bpfw#i|BgbM54k ;4w>ܲ[ݍjUI3Ip4CT'AO*n6-DQ*dMnd@%_)s0cK`kZUK !ou'CbW;$c:ȧx{0jp P;рf7Prꉦ vg̨_SmrY[7O]2FFlPs%I>, "CIk$Mm:p #DklV;Oտ3;?;oWgwj`@%Ey1}&$G܁>Vf .v㖓>dF}hKJi҄ҠA@O_jɃ lbJ]ːdOKPȮ^49Gd֩aTJ-%)lɶdɭ=]@H.!a $l@HI ɗpYnHB.…ܛ ]Α䲪< 3:>yyTW&Q(5,#=íóCL•?8PUUzxoGJƃs;U_r+%ýz^uP%2㜫( ݔL8\ސ ׉<:2e0^|[Wm;^rB'^"nwprGhΞP"nn˰^WMIdca|i)=ڕ8M~aPR(#,۰pcϥXc<] 箪jEQ#$)zgj>q%ݼ/i[,*gZ2LYG!lv bf!0Fmj A02=QLNFƃ>w}΂$yhm4u1߹Pj+l\ YVg'uoB%5\dӥ&J^TZmA"6q]&H^DGoJa]cɫ3pk,F5jKR(o7&)Y~,tH5 /-em1ʊ_5sRb93y؟sY"%M6G]\J IzlЪ)[\P~ 7#)hNbO[Qe sRU7l.G^{cfESy 99=6g~Q\F/zle5JT׫l~E Si45vg5˙xBSkWm"aCtP*NEP5Ԑ-q.%i;AԡO7AL !!:Pa&drrlTDbpe"jmUaXBP[>o0`)a9,szaZ2P8hr!U{hb6K (3\Rc0=BiPyB dZ}Gęif>7I}j$9BW{/fg YjEΓHMgǻth4uշh|eR67z:t)ų٥'>~! 6jisz^ma[XFO6'yt]C!? C5Z ՛M"1f6U}vcҚD}iN4>%1uz}~,KwCkAxaj!=kEl;};cijU`\!DY7`X80ژQc\.r\~;N_ 6;wAsڸ-l,f㭤żPllh{P$}BxY|"'&/"SJ~u;1^đ/+)M}`7+[3i˚pBA0\VJh}I7В Gs/5i?$q:FGgصa׮<ڭd9o&VϘl2EOVҪq=eak` lBj1)e5T[f2GZc[-%/Z{&b]I|7ۤ=)P;XrJJ(^!B]'0{1[jf9(6|ںRV=HVG% {-*֙9҄ԉ-RK$uMEtWӖߺlZm@(1\bߪ wث)V3 jӂuq(:| 6кpfF@aEClx?.4r]%% >?\x֧Ni#h%ox {'Gq9?Z#o7iR'eQ2-p ~Bl>Pw3yޥ0duZ}1RݬQ5rN܊ ]zZc-q5W2KWwԱnd*7kMx1 v|"46Ԏ|E$hii/ۅ=k%<Hp..+-Zz+'WIƥe2|>ouh ȇzm jF'_=},CE.$[VMn'UBGyS@T}Api_=NQ~]#WqVnv6P-RL8ge964 !GQ.N FddNȌ8|j1{Z3#Xamgh@}WLBT%S  gĪԇx?9Y&>0L8p.0תMjdf)㰞L8N6-=Sm_.W)&q d}N'=,ov=3cN^Ƥx{?bFP#zuVpF➇r$ :y~IKJ^X+򆰞67E $[$Y{Yak.7A>~ĝ#6y@'P// hVjZfnQ~Y#Q/Tǿ|(~Ji2Gi?{35.v#Wf]ڕ(rz9/:=܅(ב$ܷt*}.snqq193l{d k[/zehI#I![+DRञe{w ~RXGŏdC s3Ԍbw8p}Ć6gE~2u`pl &ػ)UZ~jJʼz[ թyuP @}v[-BE-GcҘW %A GQ~Һ-5m쑺kLuuc<ξIwxsP3\`Mf_qo}@ 9o骍j6/?x_%o<870Yc>C*_Hj`e{7ⵏ-Z!NjLʿ+vڤ~-yٗ!-nom7#y,|#~j,fI8Y.ʒUX F 46Y?HJIL7n`4p92} RǼM*kFkU.q|~{z 8?ͥ#wz?Yj2xu(f@:Q{!Ç(?vL'$=#>uY!; 5qJ6"cdF?i$2Q W"_;)JbV+k9u+/y]9o7{5:{Rh /d;8y*TbZHvV, ~ۄ!@!?W֮o7` ~5#tMbPj)1t Ѭ}\: LOw7S=~%7)DmcĮ{ŮqTe6OO49c#Uψn^luCZ4c|@R6ܔFHV S[KF4"iBx4d{R'D€Mkyo>.vPZ:p ja&sjކhW>ktMFCK+ v[&gd6Qa8SQcl(dO=0>DZiMPIN't8!b2!4G&?$G֤C ] 25AbL4%N4ISkS5 R4N_KR;췏o Z1ܥG͛*cSLboGG}5V0CSM]wHJKŬIf-=%!u.h.fU=H DTh뇇N|. wYTud7ޟBjyNv{qQKBݙuX(dI`a ^ hսi-'3GK̓ndvH`2]Kxod&d໑!C#W%>ISMi9h*! adGYa D#OX>vf_˾jiS z=WwfRV̯ˏ˥5v@Nz"ʜ>! :\C洿,2 .;{a1tlv1|.bx~',SYO&&#ٝYe[lOM=9X-M{ Pÿ30 ၣ&:xjhsP1A܀gs3a:IV<+~ٛˀteڲo9!j+kuڱ4C '0QaGP: Y_:sl=FF0 Ƽr^9&#= >OϘ}7N}w+qʾ[!q׀ ]di,'Q( C| ŅťQ[1p`2RD8ǤX="l|$Uʥ_2Nx.s{b#,h- /~i+" CLn 0$I'muoY7;v6oWwe^ }HUi|6N&Eg&)S ):x08|>5?˞iQY\qϾݸ<'wіND[$&[d!}e\up冷#n TB-ap$.L2B/@zz;S%B]I/rųh1i0^lWK:c;r-]` KlFOÂSBQNݴSG``x9ְS1V #Q:G.˜տv-gfT+Wy¡=O2"&p#O斡r|,Wd}R?j:,ٌ 6Q m;MJ['&vgD%NkjHCCA/m7Su :P$g+!ڦH( FTCA~Qr&`axnY,=2i3_e/CZ:Զ;}U;="%aءס^fVYHSTZZjA-ժ=LO7J\ίnO> ;J/D~Zw[}-Plwf+Qx$(р4dX˱ImF88h\r Î\AOBV.Eٽ}?i6A+Gi:I }%W`(ڽnT)p[.L}FSϐ#8eaO ׄl0!=;@oC:}6!PI(4 phDi -$OHrN5n~څۚ#qhQt\G{iI1Zh%II% wٗ!Fa/;F\wnwaVD2xJً(R)y7x"eo@߰G@)7\eqw|~XSj`WAkΏAҼCB=<@)tG4\ yR۫Étc]̀/LDj/IqHR0d'цfyRmr;pWN]&ؒ|7Jʼ\⠒Ex[qK<Ǎ_ eZ$s 82@K;s#[}oIx,DJ4ZRMrMTdEZ~t?~_W6+ןȚq08+9h,ZTR(*A2v~MJhrlޜmya Fu}>C^*w,Ǘd!_{nC;-[ˈ׷%Diؙ.x/mYKߴo<0@o'aF0Nqtx}Zb- g .N+hG0B]'NئYA"Xq$x~B  IPƒNk1ѵC}P|w'ʐS u~V%&6AyVc.j*8W|4o ŀS |1{p{#zGQE\SEޅ%j"KֿThmTX{爛 ~h4D$B-<_s&X_s*g雷$]$~$@RZ$eӞ x 7 %$)#&s{Щ ؈W dKe$=8bWdj}tݏ|}w\Y,.@d0T]2%L9wuX3z ޢ8# $/ dg$Ȇf:x-e+aj1> E\8 a`ǒԈ#Ż!>PK\'H֜CaӔ_͇[A :$<0fc! QUPER #6ctS +Wү!Kշ%{mѐdгi*Zo47ԡBp++•zO%M}xZ-cF3G[톒TwW}^Sxs GimsX!WHi2|{Bԋ);'Y"q5He)8jWx9ZAiTneE4MTTEڝBVA.@⻳3V(-{+: LL QqsT/hxCP=G!]&( OXRVP #g'z~h!+ ϴ˷ ] NO"ڍ /]Q8ςO=J+\n<" hq P#$(hyM>7PP 8O!A-PDFDEHz.S]VYtH,f+7O.G(?bR$y89hgǖ"oMΩRx2H-Fi/'h1%ZEQ{C/ B%Q==+HER*ybvO 8k'ˬNBgG)[?С+)aqyϳEKc,9syU}7{D"vA浽ݬ 3:Ӧ~?͗#\:5eNO4"gc_^řPTrhE}yޏy\;.\Z-i,}NM|.Q7֛^FlSX3ʜ>#uEwEYOWbaH RODDDirZ䃑ÑXogՇS_zsrZ ۏP9k+U=K)nfl{6|g\brf_q:;@M⨄+AnJhb2dJ  յI,PC qNl/Y@Am&$HOd6a/{WʫԺ<^XA_cCdP$2TcmʟJ>\8s .;̂w <85AĢjz h%'KښepHR:rKz&G0YOc:la~"_'O4R6'Ib8xZW2bkS8.i19^I-CyUZ$&Er$廦MuTj%xSʹR;vib2Mvzr;3o(|2DҾHxY>\m+aSǕzvg FNjh=7I7I %nGSȑԺI{M̄vsw`εx%_}G{ܧ|~[朷upDX3f r蹅NEn(S||0~Ew楉cKEo<1[W+۽|bJQ&wveX:zÏFJ`#yMh!527˚6ɽߕ\>Y_ (\u]ƿ ps?mފ|M tCedXcbb`x Q1xLC'zA!1x;/ ,E-djr =6t3Q3xx;[M ,֔}Fji\!DNUV_2י$1iVڏ\|ij3[ϗ?ecQĭjdح2 VhUMp,+|:0. =Ւgrw9oU:[_4]GJŪVt鴂TsiB6𚨛Qb= F! ?Լ}٤/hpQyʓ77;"b[7r/x,İ$騧@1@!xcx50Y|DZ! ʗO0m,&5x?zC>/3m7BQ~j9RAy'ZkL. M\/mjv2Ͽ\ nϿϕl)nª/K3j Z/"> M})s,Yv!HQ ]ĴdȠc9"X0A$7<6IWs\ԪB-dS,&@Lu$h6SL*jn \LBg(Ox!~J%.SxޛyBc'1P 694& lZnC)|fnR2Ɲ%nmֺ}8X }|:o<'sNʿNW6P<͸>bM:l rrhУr5V =B&9b;t5*㫟/^+8ƭf4tEPZ~KHcqƣCr$i&a=H2W!+ N*KWt"*mMhm!+ntJQJeP}Hn-kk+4c?ƣLqN.2LOrȕ q)NR2[Q٬)aܱϞ\KampR Œ &$EfZgu|VkB1WM Gh6I:wL0bR!bWH(* 'P tx581@`6 `O^(vw)nYۧ|w@ kJ< WIbYYyGqxn Hl8h&8C>ѷSڒLدxs8^-R}E1Ah&IdVaҺc[\ۃ`B0SKc {`Lf̰yR"H0Ū"e韎H 0z4o..-e߹" ֤+d<[ +SS}ڟɾi f}BNǭ'9gۊ m#}VSQQ94(!ꍃVM e=x|t@(UhilH8-gA@G1:C@`{'t6wR[_L/Єj/ 2JO.Ϲ=kPMH㨓oƏ(@@2R"{x;f4%ŗ)9@_*< kz dfKws5_~9#\QR;ٕ9ԩĤb -#sKF9]n'5ޠ[KjnK}ES]S)C( S?۽~Uu ؐ/j#%XExa,4AOMU,57;z`dRNcӲ>Wاqا9Xsuy$ZIIKJ jzy r5rYpjWfU.[<^^xFl g\5W̞Ơ[ɀu꽝EMk>i[t8}-ZoƔln^|z}99nbx |mئrnߨRd@J/;x|sKQUjBb~B|.}s.m 63hrbi1)av&ogE PN=V1$[&$SB8{r±kau1aCdd@1A&2 +Ǔ*Yu.ITظ̃m.Ɍ'ln@ ҇m `Y[Ki Bb 츘,E:M5GZa]nNETm:#nK*^퇱dGZG 5iDv\VHH#sS<3=>=BZ(˲vdw]m rz%jk׫5ڻ**Z9_\n-%Enl^/{n@+K~"d|Z%[j('֘c>?/)Rk,yݹV.mnWno:5o*+nE_VY׃rimmscKr+Wܖ]@kz^^ o%iör0-VWмMΙ*!g(q7ʾ;snoDP5Z!WUr<3<4LA1E?(& }$*ї8XRYl8[uͷmQFSom:[~~ s]#``l7'LEr':!Q뒾!$P+>Q,[j])Ux,HvinG |==?N@KtM97)fVi#^{XcKӗM0 %L$ Uy^ F8:p R\rv֗ φ0:oASƔ#0URoSj btRr@7rkY'f/B;Ji#:b2*L+z_ȮcFV VKmI &1PԈ58Z\GTC&OZPG@u/TH3}EDZ) 3ُJnUu ~fWʠ i4Z&8:jU.56!d]D\Evp*rDNN'6:9P̲5{~w=< (bunAj=.,3;9}BhM2';3}Q=$_t4wYr-i\-oH [Xʬ{TN^,8 |za=ЁQy&-b+w ㆈaL IZ*޴< A)xC;I1g>Ek̈́17@IBf3&tM(_dzЄ$g"` 8h:pc(4>N롌,ُyNB5Z0 >܇>ڡj^HPaV05 jω\!>#mfi4h+*̓gVS@M8:&,^i3Eħ4n[8.T37V:U51C&D2(Plj+j_6֎jf&aB0Sp)8ҬĄޙNe+[b aF&ePMغl3o&xaZd|t ǧ ѭ7XMaDd7Uo9uBT nc#j`פIOSw9Za$-Zғ:nxs58@x]-so7 xv$Q.. EZk8#.Gxm&nYy!IKA6{1`dfz3|e؁GDI@hlf4s㲿~ŗ#\HGtǀi5x(x^:^^XKjeH2: T4dϝ{D1.Vmr1/ ^*/KW94lQ*rP ^z|}TMShSe}Gr}=W~9czsawf~`yi.hPOZR5Fqhd- x@(mz%p99{f/^EGz>Ei~tT-b|ǛڕB5Ȉ*0lDj} P481#kC!`6`Q`2fQ\<ͦ#X'|ac&!00,-[bBMzl7UoY"V#7 zC-1} *?9Jt{ͻ=gP+;DN?}焬' iRg,gI=r1`W8 Q\\?†YT&yC 1q}G12uXc2|m8q84"!6jE:9E TTWE0]¹44`{~E9N_^U(fge7A%ўM#xY>{"1:h=6r YIE3dp",&EntG'Yycjǒp"_6K2YLR۩88Ljg9@^( 41:- JG >7x7xOWF(iUF;9>ME55 A^ӠvCŃA/'#a B wj>'Uٴ ׶;[$#3tcO{ 2~_ %Pf)r sŖL X2C'!]*2" h(/ 0y9^sW0vz:~:5$T$m7Zn;NLh^jsJc3.N:9m;"D9 ұ״(Iުn ޑQʾ.؃oxHRHO|9Z:) &FodӞYL$_2l푛b ~W4թ<ڢU 7@X0}0ҴḮCLdҥ<$ާPDU@ CpuHF] JU,꼖/~ܟz9]mGYpCkmF wRc*$$yuw/g3ٓ\JPI%&gVd\8LVԢf?asH󹥥rigOs zluRmq9[4r] 5xde^#kǴF˜||Ecs ՞4rϞ?q+N56hw'<:Sf ;˸5xy B˴7^,//=7XhYZi ڨq^ڤVB{=7$?ȍ  ͑@АAvN$zξ#j^J99HZ 7UdhC dx-Gqd턪\/9 Ok+bƬ$>&iHWnxXov/۲cGU1_C2 }jiqѡF/Z=HUOG9)CVgBzF}1Ŷp^,ި T%Ky-)$Ѵpl>d@bpUƙEêf뢉QewRUf9ݓ{Nwb֩rZʘ}\)[arK՞4PݺJH)#.f@JQg.i_!?g36sXJZs#_֪y֘ wAAWFl}:CGa1nt~D o#^{eS9 G}(["tc.#8*`kZr~l$tؽ*d/ OƯW|u y}Q^ T/ ]LFV':X>w ֪%X)e"〗h=Y:S([E:%ZX6JjU%#"ٻ `k&dwwNZ$ o:'M.ILBMބ+g^#ϲu Hus$I TUT_^hPŒ M9 3q0!L Jн:t{ 1 gguήRvo"/H!u5ɊafLG&C%Yp&^KD#?!Ҵ`BTWE3vj."+d1NxFLsL:ꅛ̨=)oc8<֘v{_i!F/*m6$[ |Ej _Gf*Y&ذ?"vEK) W8`fY&]J`]r4qh!VoG QGL@)GG\ B6ĺyҨoƾ36wl`^Nh77`xn1ݹc$BeL((b#*`!NgяO/23_\l&, 65OeWw!Ouɒ}M٣^NG_3V~9X_V8ٌ *y 3U39_m|3#3kӟNR@Qm08U,?)(NlRT1\ yE#_ %,3n%IqdYzL.d#}@14ٱrԨ'.Zc1hF收,o6v-=@JN^âS\%6%ދ;^]@h>K~U|I:Z|a'3oP$8!58R봚zEDc{ޯyjI>^4)ZSf!mDYn9k[rFLOvF}7E}X](V W=g(F&:)vNJiu`66 1-9V=u}{}R۸ZlұP %||BlDut6&Q~%6;$^w_s"CBwF>3K o r]=:"U/8jy3?ρoJ?-Z eo &a8dM…#kf%zГhbݾwta=Oi~ZYzT#|Ce}+]jv,Z޴{M[ > =3L^hV~W'#&wIU}4 L څ+"q9{X4 ؔx7ЃK@v8&bx^] +uA%Lh^[l wAg`I-C=,S Xe`41`ƍ`Wr弿YZedNbsen\{iVyZv%J$QUFZ|B4a/uQc2-{ tGy'`̐TaV%SciXW+' ~ŋ!m32znﱔxo )͎&k,OF\$'`7k[{Kin;);0Ir,LBi]1KtRKx$Ri@#`9L@SKI=M+5ַsM攊<$e1!P|wu&t2)&[2ʦׇus+dQׁhEbLMFvKЫV#EopM zogD[9&"/aDǺvjekMn7>oxV[eP$?VoCK?CL~:Ś {9G, tr1<>bGNw@]7IFN/7 &Yùգڰt7oρR(@by|S .Zyt;nB#! 4w4y!6QhdqWW/5DD(V'w FCsW8~bޏ=:l@i٫3ُnPZ_W?OJJk7ۇָ؇AX&k#X}Y|U+@٬*gb` V{&9ZwNgI2R$>MV+s\LUGzqM̩}r)dr27Q9MێI֮:m*`рLo7P&4C\[;.jNU-FUU贈7}.DM'j-낣-R:ֿWW0{Ǻ.s+bϹBxR Okx-ZVr#Ƙ:$)2OjzcհR[*]PMtU=3Cr. ׫v Nc' Al{-PEboF/stݪwN:ظ覍hz=UH'ݝ™n},@ ӷ7[1uȲ'DmC#! 8PNjGZRIv=+Ogt4r2SB` t oM+a8pu2 hKjqL\8< u?[p(U R|6V^Xf:fƒQIsé^KO foA,T #J߻c6ޗ#~0 C#p*b4S]I5 9-({ioOX(na |GG #Ea>g&ۖ<$𝈻fRw{jg~56%ŕ۽1G6FwGUwBk;;Ce6Pͯ^5GJHܕ r%pZĸm {Qv~@'>k {ziInrPT@>2K,)c*|ttHEFU38R)%ߤԋ)zQb 6'~~?e06n7m+sD&{^IJ-4{=?5*_!3$GO-6gAsswwI&낫Iw˞o fgo`O֧ҌQ;uRBoy5 q>H sQ7ª"4$4Z :&#ErOx-4 %!$Hdt` +h#CǨbMSH垦w &WJ' *eБOJiq7~Td-JT඘@ՏXn.pz~W6+)W.mñ.#`bYƲo7_X;>뷫&q#G~xmj,@bkKU^U]2B41VꕅU\ ?dn rIO'QG88<}_9^qRvR] nLZtuj#o,αJ 4^sm8ɿ]cun -"QD,(ۜHBǹ 7¬=^>pn[N_:ZYVyleO-DG I,ݞohF$+ `䑘,̓ zJh7渪"C 4:tгuwV[m>KN#قEؚxLӠ{s@͋mfv0xV[&*bpȆFw6XE[043e)>h?i׋ja^ݻ|v\4zNiTch7`2*q)VO=fc~u<(D\ҍL{ڄTw?C!N̴Ҵ.&t=~R? x3_޵r=jes]/1H14w6 +]V5_\ͯ[~4ak|ң p.e! #b(tuU7|vBL퇳[VUvsaX \HaYzSɣ/\ i:d"d4|t6M _!)5:{ZG*J\ہ=ZA JU*c>K؅Z;w;ӿ#$egikov'։(R*Me?2'`产N=Bvé$HP oE\7mbG| :; zseI0`n%-&* 59pWC4ŸsKaiFl}Z]\B\VǛʌwVWTJ>֒WU h`}2>޿5d&&3H;D` rؕOn'%gA#ϛQ;Ȋ3޿f(K#c쇇F;AV8JS[}Ϝ1H ޘA<&4xx_ohbyi1U-ךxFѼ2 9uv,A!ި?N~%'gxcs/icVMRd mn!wWi3vpvk1 n8f!H у-TLd&F=[3JXhE?+vՋ #̼h%5g)Gr[%PHׁa{H:y+>gZ8T`UD!T}>5vI1RcdD1\e(!&+Hq+:=s]Eutu5Z4v)޽ODv^\:|\*̹ q/Ds->*~4lc? Qn1kbĘîNƀ4`S_nyRzðD6 ZBKETR+Mf$c %Q }WzcOO R6WnƁ7}eTPk5篜+GB4gX? ":6wm- ӕwx69Gu M^m`qXMqI}!\LNn-AGn<眏U#GCIwdj 9\'lü-1|Ye1<$ZS}]W/:5˗NbT5].^X>w~b&|? ; ׮W_ϬtBNDx#_ ]8MB||;d 8M! zkJz&W] mqJN&F/CJEKg_ni[j붤[1<'?>aL5T) Z4%/# 7;%q\Zɯn/yə4}]B&*zU еX}TAO/$Il X"M}l? (R2MWDׁ<(i3VCgLM"j?/=SN!vnp"O.H:HV-pt4_ZGj校وu!zM>ULNݤXm.W|kKlGsv2Zf=U*$1eN+z-tv/im6˯ WL ͸h}*XdGMO[yI28 whEw]A갛9U U܉LN$|4B_CE]dBbfbg'sڊp4``a˷h}Pٌ/[&O'\5J!?k(9B>9\q>|:*=D0^to|{&:^I|'g̗F"ME&H,>I'vV;%eE|bg^L@g%z}~ Sa9r4~C~&`!psR#;2VI(66dYOQdbNwnX~_::3y/yRThVc6eFZ$8 gD}./S\m';08uԔH.oJ12cLGftDa=c?a~&Oa_p9L'I~GGgRNQ >}gh}3VYۀzzRQl% ıѴk5(kRYhw=Wέ0el3jq=ks^]/&} H/aL8 ŕRE>&Ռ</\UCqW+dCE({yr/h` 6Np%x,S\RlD=AzhxUTh] PijsB4=(Wq R'kt>40JUވ*;[ vSR P0I[ }a˲A )'H8]KlbFoBW߆w:C[댩X)tsԩ P U@!@o`mM}(2أ׉p)PͶx9J.ExFd|Wm6%6vr8t_%YC_3`gOQІEw)4 W0u-k6GLmTjzkȘE׊,49VԩOۢOXae\|{NhnÕa>R*Or,1W GJ094;4B N#R h` kT8>RTU{[BeqIB̢!,FLʔ.EA_Rp"UB R(I*D>"L8Y eۼA4"2rpt:F2cPD?D+n[Dnjs-lhQ(`#iKS[T'=hږ6)͇lDe EmZ$_P r4K4aw[&tg$SJj75ۄ<ڸ_/o'|Z~L5k/a|'sm9Zb='tGvV4`eK"5sz-U)GB3[7"`SfN :iN텬6] @@Bp5ӸT})FPeTy&Ŏ31%-UKj>݇GsPhکt]A1Oa%_R&~+.9gIg^H{ÇGn U'!튆0J̬ZhBM{/Գӆ٥9if1AZ`7eJ}v>pebr\VfԤαvqH~ɝB9#hPUӗƨp7PZTrLCBКmwnÁUT [5F/9r i-NFjJG`\M!xbs!n%1vQ3RU]}ƴnd. \E[N)q9HD+b- E ^H!:3F(<6OXLsP]Pc(No4k3EZ)qKQwrCŐ-Ɯ4tiЁ#Vn_PAI6ʩ>XPBo p%43uuq=Nsxx7hlgeƱ۵ir6PcFYh"m lL-L-8%`1˸.VUTj i@C?=W>IDaX& &+4cb4T@. C>FAO~%Ef_ض/Q8R }x9m%C$φ# ňIgZލB61)8Α4CJ7ӬVK#U*f201b>HkxK7?% ?~YDm~['_V`wlWc?+k+)$|?Q9A 6gˋA"9")c Ynu̠k!Qa(sE'񖹎Ro(d3F F~Z9O:0s*D}ߥ`1AbV_r¹ш x J_xb̡7gߕ۽b ԥft$L9<[f8X[sKcG œ4$3FL_ݢf7XJ7ҿX'ۚ(!>3iu fHH9}pC*.r{lxa29 O rS (!ʒ}qXV>y kE?^މ#/\\)F4uaG`QM_B!iw 'ʻjkQ 0L(^di;dD塳GW[mOrIYQq5T:teZMP̭BZ]U^ Si{fc.6mspA|2 CZ_O-64:httz1>,œH,̷xsUu*xުz<{iuY ^Ğ$2 2g ~Dbw{ \Rmʴٌ۶1/d[#w݀ d>2=W48WJ!ܷyiSmW jR},# $vCO [8!f•1ksNFڧ?INQHeyYP+İOj L;8^=9 9|Av=|PvZv~ . `"8g eu|rh^ m3V<,ׇX$P`Kو2luå}z)k,)Z~]o~밟`=ZM=?YޖNxSt(o>0ق=kemCB= Q_>rӉO[fsL{w҉ʽD1Y$dqQS >ħd4eHIn g@@^n E3h 8iиJVo(FB{_DJ!";jJkSsH.Y:?4SBz?ejZԈ_aʽ/ЭqLS@_ܽFܯTs/xz`Gk4YD/кA(őQT'Hq*.)s>}>tOg ߻\?h\I nۙ\ԝռ7̺v)FTFA"׸W'͢"<;f\1f0݆PX0 Gi1mh ,n:]Zu! 5*4e=٭2[6 c%aPD cl3 ~WxZ^1QZ{5{*8,ǞOdD'7'lj%I*w"@+'R Fv*dӗS07A"Ϝ *}3ID>O.kvFG!:NFC=f!6jl%PE֍L(L^s^kupG踥NĕPߘ5b!Gq, QưG=@ Ô.xGo@6dLbd\I'|v8bs8/p2ıy*ys0&98/ϟB?Co{ A4._zV "75>Vb[MX:O/zBz}X8(I.rg ϛB-.mlf|z{a!>.-,O~= okVJ3&NqWت [r+$LҷLvK;SӚKQ#LD5+ށyV1F)|.}s. M7647*5?}sFKw6+0mrXAФSG5U7ufԁ^V|{}[Z#x{og" Tz' SiLy1쵻8P> kJm1 l7q{5-Dt,r[ z踇y#YoT U ;`Fet@WD4vhAWhJ:k'OMÚAklhWttWӗ1#pNR!vf ]?kݑ퐄։U4e<;٥Lv~{A0:P&F ٲxXA> Qhsodh T5 U%v5վ5%{pڨ*ؗ:Mc tbl%άl !LnRX]}'497y HUf2iW,cNLг{~S.j^9N:cW]_lt4*G)8~&z!" [YqԨ=lFB gekaR5Z=hti $Ά;nmȴ΂iPiy+&S9(u$c'hKʫJxCk9|B|jV|CS&U>h2E B@Rư/ TB/K}3*}K};kKǻFPE!*gTDatf=MecK|يuLᨨ^M'v E@>0c8jj@n- KtLsnh~3P-V9Ѹ-m0˩L(:sGJy+f:jQH'C <.OTIq4CĒ ېiB5#; e[IaY00_}tޤ$莙X0i: w5ciZNꋴGkhG4hU4 5$}opzo8`2a>^G]2/-RpGnJq4za8Ţ>10ʂl u(+5!X:pd'鴶(4K  uf%Dkg ʘ0qE 8iq4*7R2 TLʜ$M)V 3dhYU ꍖlf4Jj*٥鳋ˋU39iri0:ShXl8 ޯ8#zټ ! v!*HdAEIdU]V1 .`[|ۉݢ^7JWV '72T: giJcfR W>1W&ݶA%|HLEi|Tc0&8~v+QzT LabcW|?HE NȪ퉵7$>H^sQԁ.4EuұƼ }hz@1%l͍NxNIFz(XI=7k3!!`8|~i]jagyP53vG#ިcT2'= N>(6a8>NK?n4~ߚoWoow4KkUqC35G},6=S45+eWHw&ƘѕI8`NioNpωDτ'BXk:n,'x( '2ӎQ2O /z{9odH8*\[4qRmS\ ߒTimVw!/]kyx @sQwMUJcb)f$ƙ !::NQlMpfك%zیDi(fU Q%5)hv>g,+%Ҏ`гbD-B Aȭ452ѵ&7r MY)dIl6('&ioծYm@PÅ>x|xa|wTwdllӦ`z[pig-e&:f.gB3}pśpӈV}uL6=&!Y )YhM7C:MAOAyqnH1Tu~R_5̨o5GP lIA*3{?&,t&GԉPtLK C-24^X| h꒻+ȶ:XC^xT7tПci"!TW #%G'R6XaM/Ժ:n on 9S I#"?_'8ٗv9e qZ >ڑ&s}x5'=fݓ>a:"IqAT;aih ?Dseѐ1k{d&d 3" 0^9y4: &fbMH(j&;fsXIÌĔ9|< ㉼K]n [fQɶXI_/ i4( me!;c?ޡqx@;DjYlV3SN>/eu1* Tx] jKqdy<g[O>J&=o*ɕ3| =8.Y 4SQޚ(3O&Ԉs1M ֲg 'A C"ϳ@:h%# #9Hɐo#\Ԝ4fylz(-FewLތ2θTSSljq͑-$+=yNhDRh<Gb.!DHaiKVqWjIP']a+6O ^W(˲R~r9etqdɻlCm@Z{0S|T^U쒨&|̐aɘHI: &ѣaho,aqu5}{,:1DW5&&•"vn|Ǹ#JQ|eHae\/T}M^;~NSn .Iڭs$ O_]DډpҌ% )}+*~lUT>cl[u6J},&rɘ ‡v ;ZnJW u0ޗD :KYEPl 'Զ?lƄzJXz5\Dz!m޷[5^ordt4=}z 3i_Gƹ873&NOP\RY+B hr\Cp/S*b"tU^ǨqCzٝ-X,Ǹ1qS !ŭÁZsjCW$`iu(y}e_tTKklp&5~w}Ql]cEUN:`G70RT^Z’.f2I0iG"+%ׂuϊFz+:c eM n߈ rF2'Fū-algrĉۚ7bV{<5cexF߷לDOUGhH 1,!c!\m !P\Jı aC N%&8H =;coJ\bs %LBHt.jT6I[sQ&)iRD#ȄCNюpBԦ 7YgQO@_]N>턄{kfhk 6tT2hh Bf mȍ [|Z{Mޥhle"ͩܨ -@S|HSn}Pt/K'Q"AHw1Ǿ M>OecK8f6iB8UDO /QՅ9tǚ hm8f|^:moG22,pb[ ;u3(@ZI^n)L۠~MlHOL *qJƍ 4{ \'"Rd"%ёҌ whco`2>ځl2|whWo;fO ҩcǪYS_A,f֍m?TI9a"4B*sd Yzfm$ -IoQnyLRHk4VCɒDK_AXp_|Yljhd+\eCF @X˩LB_͝,),8 "A uj,b7%lμme%$D9#"H;Tci(Gs>U 5*$C:{ '<~&ۇ@nuR&ؽtmdEB:%^E&k~,Z'> Ok['$j1N%G- 3:EґX\`ufjq\] HS~PE>w)bpW&k7pe|Og=rbQXA1Og<}$Oߤu%eq+0 2h݅[~;&nIϰҋ06ۮt1=X8_' PZMpv);כhm֖r~!WKu+-(ؙaQ \w(9_HW'ʰ >jHD #}WwA(mARȀ/^-B 'j^>l>v.a\O \RDq"u9FAi5_E bI$&^N )DG0P~w9twKp"ۺu$1>?EVjZIf}H?kO#V6QR5+z=6Q\wPrχ-瑇vGris#rs7 k"oAqq%s fªܮ6l.*qf =~s@AyGdO[du5>c$Y+Gf)Gڝ%,X嫎 BzP?v#Qbx`f|Dڳ1⇁>NmH "$3E[RwkGGjH>@W% tA hyrfԱeAb+2NFD0eS_vGe{VJZ> WAq%9Ycs\}Ն;ĐHLL;>fx&3=2y*R)Wo\1#HFBz-w+;#,cf6 ,e0x^7#xW "jA ~G@ HjTU#et&c4$,(@<8:V~Hqhik!gp/ vo v3!D} [6* #t p/f;T} :ˑNoI  n 4.lz!woXvF^ rRM5Hy:RH:',K: /$Й~!rfo0pF} _Y~tM#'ܶgfn:~v%;7KWj)c^ QfGBː4 =z"]4<,tVD^c4Va#FA~ Pש {hx8QMz{]\x {?+Q %XwGo p)jի Keu!m K$쏚֤s$,y¼tW[] 6׋[XkI?NZwʕ*^EeLKϛo[^V Pd[| TCgNY7r[+OՐ?:[XG'3&bͼh]ӭO@M^[ߪ>3\U VS"N^M tRsc?cR]mx>&[9MkFG=W9t S@N<ϞjӘU`$7(<;mnޭqB/B7euc]5_Es ZL}̠QTfJ QQέڤT{e%4*:rGt+Ý[r'crR5^ɕWyVsd%O @FB{E'o&F\-*a_=yӾȐq69 \lfl)^S㲐@+~liW6 k|k#W(? {d $S!ƼZrtj- 4ج@j9zYn՜]B&ci3?I C Ϟ57gCH3~uu,puXU~(D{D3.a&^Oʙp Bp]%i %=A;\_s,SY|%5ۻd{'cB@uS)D> k:~aHRlRM|lfǔli*覵ҵ6=~<}\)%/q^c!`^Wt`j :lzA1o2>q (ީGd(xø1 ˆ',f d*R[)Q+/DSao: zdoeFBҙt?"ҤRl^\:{1{VғrkDzzqj[}Gs$a&zU^ PI( YXyUJ+iU]xjy]qxqNhC[ ?~j2խW v3ZBeIS)mVFqrC WY0f I!'DyOyTn'#⧄?4q%xbj}Z;{Ymߨ],cgr?nRo}5"?{2ca7+8ip vzK E`ɻں>/k<N1I +d46:{8VYNkv۫;إ()hg 0}GHbqE /Gލ1-FL.Hjq9"<[;wcQ2 V EqV.ݬ`U]YsBmzҍO`f,g&HׄF ^W$qpI'QxKdtzP_.BuʇpDU%ݼ@8L瓉8[U&>4;E&Z4Aw[Ḛwf5+QwҭA9]N 2Av^jS[WV/ë҉EbT' } sw ̣V)\o*VXQs-gquZnPYϾ)pgB-TbWR_}z[/<hvʫ%uW &JjWX0pZŊ :|$_.]-6w K/W '}{{p٪kMl4wZJ=6jiS-pӥVUL~E%S;&ֱɊj,Lli0*E[0ͣ\Y[@ʡQw&~xU Bt >q~ {|'P:}#V*^3 8'e?<c^-$Ccg^އ֫f`Ӂ?n;rN V5nS6CAY+י9U BL,9w7)JEo!ƒiGo钃(xP!U`6R:]0|ߕ95։>\{b푅Z9;'|<iI})@&jMyt &_d|R>< UtQ?ayfhS;ys $b4Ez 39,9`kzb/*8otP.7vy34Gn 4D;hu&,x7X;/Ky{nYuVy z#3&<1tّA==GSJ.!D kKl` @0FD[xb%Yvʭ-n/dC>'ߙXA[;jf=1T&Zŋ7*}*Qu7/{sA^~c1gkCEXHސ!/o7zkww! v#F6N4mG1扵9r&r4FS2(VSiڔjڔ <<j6$@0OBs%C57 0&X;uP(*ʥDW*Tל0!jKxH(I.~tVDIOZGM3]*:_X"ZSGN96tDav ig''j է5թƎ/:a o Pč|<<@ו79[)~xh' !wc2Q{\/Uo#%OF{[GF9qj~WK7ET6j)/]εzcvjR!P3k_ͯ˪W# .v/PO'ZFI;\|Ȟ&lg`􆝖h7(ĿmՒnwiRiRB_·@`F.qWE$.＀[8{[:(#OhmGKg_vLdsZz?NLKV/ѩG06TIعEa9uuȑ}N BW-gUR;ZVoq3nV''tN ?znѼAp-ːC"ܦPGdO C E uˎ*i~W#&^!hM:,(ZUP#2T28B;w; Q\2ΨcWg`q?yC9Z]3Ш9(keH.st e6Т_aöGVm>\]e^!]#.W@0$zd4Lc*ed! Kf˥M1^nO\.dΞ_,dg]Up<2pZȯ}o 5 ~eK!BJUM r`N> ʁmRlP fnwihސEM+HBo8)rT{M^ɸV?~Kpl'-ht):QTcgM1vt٭Ԥ]qp[E(";GBcA؂&kUbb:hKP_uvGnwO, ,ܫCV%j%Z᠙dVtZ쫽CkHIΪm7CTZj-]L6YN.#/;/դϬlq{ϦE$pY8k o~R86E*_I"dLt^ǑbYi`;#ZS鳩dI DYw4鳙z~,O>pKCSdkGW2-mBq)LL1H'"/Yx/M%3&¾PE}Kȁ,&kjPV'!}@!"yk:os= Qj-5a_6 mmVXe ]ְL0 EphV~t}CQ ֏|OonHzyk!(h1WI+Q}NjttW^ztuÖq^VAK1X rX&7}HĔBQ!+ Pl8AD(D[a$}'kJ2ݎӧ/-; {{qOfA53V:q]σZ{1t>@?/ YSwEHdYJ7&[z&3%BzJ05'i ;V tq&fl9kZgh{wm=ZV[*f^Ad1S;2P3ƫ7'UߍMb%pz>Faq܋'"Гx=̤+!môg `㇁̔|&[)j`ʊ @`H{RqiQ:S  rryq[^R \HdqEfR~"fg^X\,3UrO$;KIC1y@!?*yQ}+ 3Jz-dt Ǘ\4mȒsOMvjWm#o('Uy3&Űzr}dWSU$jye}#F$Sz*_yjz^l&5ϡ׆2Cߋj/!:y]z4.GSrf Fo ђZ>m [ *uvz8[mfAj-|-qQv iu oIJ? ]xb΄b? ~7J73ޛZC7EC#Βdޘھ_6Rc2nKjxb"ރ3gO z g}RT+::F:3_ȤeΟ˜;wa)I$IEӜB~˛}-/ζ o0O^x9A)YFZr3*{ hh,>̹ݶOq⥢MNHw7(QIթǤ$_;zξ3]aۍ�/(v 6(./2|dQUiP45>k 38N<AD%`hCZ}9{{nS 8T_!4aljOoĉMYKFumVpp&.h֏Q Xu5+:;1>l.B cnG|>b8JCQcmo?օXۢ啷QJ^bD*GENMc? Ѐc8sm;yc}@yѐ(2 CM5̉ ?nFKd}#n(29$Pkir ]c8#6Qƥ۠kDXJmM?dvl׏HߗqSmΑJfA R12l'6VLtF1Af9r<@Ű3h! 6">p#ŕ9zDnbRv!$dt")EB0/QĒ]6*FÄ!޷V] ma+|}}ri\7GiZ}$et[晛 eT-V^^Zs 0>9P/?a&2\8v}~=67"4kٛZ-LMYcj)F{Ogѧc` ,ivvP"}jNU&DA cƚu! :EKH@*_No>d4B4"mlnV"fd[_qqmYl48bVo7M#c%Z Lq| :U;3qSS- My))(d"b3C({l}l(6V϶&$!Wdk:PԌo o&kRJYU o6xR1cRE9PrԮc#F'{ۀhXi5?)ـp{^魹O\/ 6ʥf9V{ 8=~>`oѻj6,3~H[+z͈߲^&@48נFs{Ȳ40-jD:J.'.wFH8 Yh5m*9A{ 5ɻ /)uy, )ȋ >) Xⷄ32ӡ?PzUȷs-Q7V$|jUnQI M*M/B=@ONkM~WgminӜ'砺^`YǪq(&+HfxZxXSfC-ЦըjU(JNL0W EG굜zgڈ}5<%58hxZa2%ñ/v}fJꖪkW-΁13-LY]\TuU./N)Y @qÌpZ51_@B0ꄹ :r0wloIEcÃCQ?Qs>$j,C¢ X~Qj9:`ˍZ/ %$C/T%g %ة$hCXh~Gb_:oTwϬxhFp3ݫ+ml˷.0w[2}C#mʞN,-~*ڈ=pbf\|zy…Ѩ~=<9f1.;dXf݊ܰф:1<|M =GyRku T"Qd Rgdrh>IYk=( d&&q>i];Gr=H1VR2|'XS^w橐n/6Y29x(p8`*j!}VXehh"'LKz b>gå_Nq xj3*"Vc16BX>;>I=PR!4A+t< /PʨW'ϼW/yTi*SpsUG[3$zK-}-"!̆pd'/%ۼ>s]V7xFzŕdTwfn%İ>aL^ᶕgcgg~]~cP2xe9= / B:f&Dcfjwz-I:_RO4/OLB?[e@ۑ(ӭF1&~yQg-h9^Y~"(6Q^- ̇3uۆڱ@lj|P6E6v5FCG޸45̍\p>ΦQW-xJ(43D ZʗW W k((ϛ9Dtpkazt7 O%na흸9!F WF?~WR,z#wjbϯspv=ZMTVKϪӡ:αd؉& 셥 ge/,O٭r-59ٙ=֪k<xHh]D$HȌ~R[NDeщS㯵V)~0&9LS6N[}E=avWb\tLIflr1X_ Vך To *cM;LN<)fMdg/eej>:uԚo6rn+Vx?n<5^o=$fkWo3A¸gͧ~Τ:NX|6"\HG}ϥ]8={Nӱ ~Eی9﫝Dn|·/2t,]c$!j\hN#Gf#VJ|e%{L5=/^D`>z[N3crs_C9> 5Fs X0c鸑/Wh[>A`czeW\nv(}iJ0–SE"LL7/'~߈pmoVH>^8_ϻϖ\F'3O?3{տ&?xzͨ~fC1/۾n}~R=}N3O'SY7F].=/_p9yW?~JJ`C!ZE\ q/Cm^W8-՟@)q5V>Žqu8W߆xW~~F_F-E\5|=u2&qWߋ9C=PZoph/S.G_V.B~+)(39 [տFY U\ܯ_pF_&pXzWy)qU5@ Пobq?WWqq{'F&>W7pYCn۸\P[wp{-\sT,-w W/ 厥0:`oj JM\}PͲGb߼0}F!O៬=_ڔ3ӰJ#z>LC5p 9Wsxgb峿nUP8/06sq%)ʙgc‹~3ŕ2fЙ3o׿o&7l=ãoL -4gfo9U޵}ɂ'N%աZj|8fq+y&A9e z~+^&Z>JUi5ƨ*JgJUBmƞ+^ ba >ᙧ+i=ST|"sϼˆj=fN(Yl6Y{srեgJ4DڭFJi}Tx!Q\Q _P#vvY |.1q+e0*s5rc(ɿ˅j-Jw9ZLU41;;JҺ3o*FS7>C;>+/eU5njpT gzZR0fO*Fڝ<};aݛekkS=f\~jF475oՇ3<+~ƞ_ߢ]dTyUYݠt_J ?p:u}ISZn5g7rT3r{̯|vCFSj5@ݷ]G#Po>},Yc8eUF]TT`qISS&u썏ZSb?7=Ԛ[#ZljakmzxrA711{=̿W[ߠVROm=ltY8lL: "%>|/o3çnw&5{vYz&d\Y;.]Su}-W ׸kb~}j!r3©doOʯl]^IR'$UC EbeXΨ&vO(;[*;Y5 <-B-0={"yGJIScmT`CKUnQbi~oc#ߎݚUKUo/u:gw?&XzZb$H?C]_C8yI\7tnV?W!N阂J^PpNPB.8du\mUahVc7NmjyTuW]oPX#IW7=Zobi-\>&~9| ~?cR\}9NW_?oy AW+[<vo,?@ c _{WmոA'n£mL k=p=V ~}'q1z\#~E~O^.W)4]ᬊTJpTGg=:xߌ_">~+Zp#AWFO}3qx;=c&_:W/ytQ We,jW<T=wն~5\u=oӱ']p~-\0Wi|<oE"]\1Q0~WepUňa\Dg>QGqupuG''9F㪇{?>_?vfZCOoWӟCGIWBz?XgH~ީX:W~ʷQPCrڣ-*=#E"ٺ|,(K] p\y>swWʘbGM) L(a$KkKX!l{*Pz8ӸS0mIj␘C\0e0V?C61:{s 𛔶F\F!Bukb p ;"SOF SVQv e MT.?mc zD`k$! LpmxY3S1,әٝY ( ѥe%q觠m:r_8Ry(vֹqLjl4̊071قf_G.ҚBp0% (0e"38T[Xd;TkZ8AWE`"ADcPc6wOPŅ|u+ډ;u sNli&;wQefer6rg}Z|4ctN NWC:8s,&%.lu]-c#4AӧL",d#=(ʸ7W SI, }`NyqdfRz0D@R Oh+/܆Keg$i'#e~>9KW߷Yl?GvMȖ"7Cg3m.rpa/QY9s*57oAbds砍{^d3+1Ddѽ4iIl1GJtn`t)[)u_l/2NTz4jMLg@h8mu3sS˓YMut݀L t [)j_&ӕt7 Kά&V`j\HcW^.~wXW/[$Zo-c*-as j cD1W N +#JLz "َa1k6Tz^`3iON60FfK*f_aR[h([fz0QW@CKJnfm{~n2eֆat͒~0i"KC WF(3c±xΩџp|Abw[(7n k].tC9ݸCFdObcq4PSQJD72oS%l~^o%dsˉEsq_屾 k Tf&6/+զZϐWgeJ^z@E9zӪ0:\,݅_+1Un42 s%)/Sq; e71T)%=M&G$q%BO/(r+;lpF~^JR8H&Ci#߀%J?2M^S!=Jf_S AQ(E4Gv5}#k Ƿ(̲*upd$qr X`8ww{JdUwq|Oɨ}Jﳌ>O UǑ|+ 2Oa<foGFC'Zy e`|B׫G3ѣ<@*=@4"{FGH @0A?3=@4"y Oy OQ/{f#y !< Hdxy zmTJR)ҧEG|y D= )CfS3lv <}h e캸AD^;%DOvKтy ɾn 0 'ՐїvWW /~VR6IY;=7LY k7EQQFIs`d1sϼHiTNYx 8eoYSU8VMa*2Jތc~<8rnMS6mA\NR,yln<Ԃ_Pb=U&廸2O<o]%8R)iOQmy+ҫzW Сq_֏%m[? [tpt*Wf`6°.:ZknỈ-Iءg G*P=^n/;?ݨRWK6uq>SP7TA7O3[zm-=`6WL II=Wj/6MBs':brj0脳'{֣3=Y+QD\M)w*_s6rl2o^][sWKǦ}gWxLk_y⑸uiz5ճg;魯5"m吖w?"@t^*yQ |Q8WQ(b4m8011=Ir M Q0ϟ3k *,Jġ0W+Xk:SK?꺹B`)oDi9f"dARVQ S2; *aW%'F5T 7oqh/jVu״!xm!M4Emc:n,w= [իm ;E. *;Qg XÐQ;[i+1! oH.^G\p}[_};%ޱMYg>$raAjPb@bd~CoG$fƁ8Z~L8p1ʀaq`3q`8w q6FqmʾƢ<Ɓa`@b8xc8xe;lofe8E}8p; Ɓ<ƁBb8#tOWّ[ !LU$0+.3"?~xJO$WXJN)O»l`&řDw/ŘLp?RJX b}в˧8`7Y+0Ȍp|2j۷_ݚolξa_u$d$ BxS]RJgmN&1`Ԩ2k"!\x7_j~TEKztsKRU u/-e.Y̨Z%!܊T\/ EA Jt*eG7Qj R*ԘTcmgT@tE (,+v]byg%l.W _O?ADosׇlz:zV3`Wo!*.V㊁e"3o}}TE;k8|p(pCgD!7/aāpKC &P}騮@G9uppppppppppppV9$Ʌ%ˊD2eH$JTQܰJe5* ]_(i}BN҄J.ا*՜/o\xŒ7ʯ~DEA"YW 7dRk5tMՏ+B}lրrO"bŞf{!'L>kx/fOq?C$8߉pk'%\Sǡ'7yU?s#:8{) Qxrf0Ѿ45Ѿ zC~u~v[fڊ˖g2[piǏQP&TZ*-󿿡jkNZU٧-')U3kAo+zY+zTJW\bS9= =*+C0~oϾ7^ |cEABɚi\Tb51j.sk-'ߨKMWכrA:گ傁'4/t ~>}\a.-.-^b#:{䉓k8p?|6S&Or />vߪ~?e;^ߵJ);5ěqp88?lǟ}>)g9?/x^wԐr#A5xÉ{?V\НԐ̒嗮Y&Cg eGOS>H;TZV{tK^״x]{1J,8{,hohY_pLS#XZyD`?ҫ?''Mp?>AqCp\ޡߓboK.I `?5{8_R*//Ev-hhuhOJsrQ?W R;;;IMHV#>j̐  3I&!!!h|4wW]uۭJ]%jEY][U۪vn={~̝I4D )?{syNT-{8ՉlQR]9Ď??KT!<%F1bGGM+KE$2#F1bĈ#F1b8c7IGGLq?.0ɐ,ԲABz.+vc^e2bĈ#F9"iG,!%RFgVa4v>J5w\)҅%^ܽ"+rEdźh$"=nj m]De׬lHS4"EƚVo9"+vRk"kYT䉴H{KYhDhX-_C#2.o "@eбfYQ(::[W7E;:X ZdMM+T]hxE[+NԔmmUvň4DۅEWPYT)*׏B'NԾӼ^* B{%nx D^ yYYZ8 >otqGx7}ns(83bVDor%yiJUbt5ZxK~^[Ř[Ҿ="(<fef֔K#c3 1nb[zSRI+r-k\Q ;M([Ꮞ˸Dە+4Y`6V K4PX٥(@&%c%ldm_' eQ. 6ê@C_.ɐ"T85;%O8Zs,5xdO9+pUo^_%5}KJ8.X#k!:þPx~uW/kj!4+Ա/ 6!Y SŮUe Xn`CMx^ 7Pü:_E}U0}eU]*PF VˠT헋C8W".S$a=*V̯WWUSKPZkh`xA/X)0UTe> ٙt>UIGNEK%jc}PPTUꃐVY*J.ʤ UNJh7 ڶN.Z0VUtÇU?6T_Uߠ۶WX,S%{ZR&_|T^3ujiҲQVam)p4;;4u-yHeb"vnOqB1bĈ#ǘxPHl[RzE@(tQގ1=ߪ\$O[4fiqr.}xW[qQǻKmxP~͘MxWm uwV3FR Vo.;>>/C4Բn ٭6ӫWi?/I}\)嶮(H`yܭXN1{aOGx/T}NCL@@]X+pg9)l@zW-:6+VG q@]x7׊ь.vx{Ia@~bGb@3z7z#NHJwbB2V]r%0bȱ&  ObPO%ӦMO4uz4, ϮÁ*uOx'#F1bĈ#F1bOy#C% ON{RJoW*a=B1P ]Nu _ #F1bĈ#F[ӆ))ZYu,}y GW.kz(IQO%?_4dOi ;"MT6~";in+ڕ9 k[0SdW<ڑ}-2+;;9"kAgSN]Y錴t٪-|m𔣏8'蚵aկmNcD~uNJlPw"wFۺBZݱޥ+ X뢭NDo!;;i"K#FUXԴ ۟#RJ+..Q))[ä?yϥȀ~"\,(ٙJm"'K)t)rG+6"1R.E8xȥs) xإ(Sc bK1@)v9JӥHC9q)F!.Beeڥԥeeey˸m˸aZ&LghrKXmR RQ >ֶ[~4|}O-f G _2@O#Ep/D^z6''!~Z9KNv8a³TVT30C+Pp:5̧33اNfL.15} BGbJsI˼;]L5<`p1;  ѐAi 6T `Ci8C'8&P]-clW2co%KIC0v<})5蝥5c.!bPe5:ПKJR IT0[L~ˁr@M¹d4`hfcЍv~ T. ;ɈiDNYNYfYOڭG@1Zbfp0gk2y6nMҀ1ȵT1jW_kt^gOr<w^e>j }s=5Aj AQv0vk/`6QfE̤f5y2 vUюzX]ϳ4`ԺAJn,= w kzNYi bD)=:F(S ܿ e9țy \717`MY0 D Kt #?zZ4,)؃fOVoX0 ۄK6\R][V7 |'O0v^~#50&AU`_Ed NLip}t4F#<`5E@â&YC>j.%s%Ph4*@MT wzϭlj SA ~V"ϦϚicl ZNͳ>}^bhL`/WHDU,k,_/bbh_:oo0w hpF wzgOj~0:d-s:)ٹi$2. =}E|5}6sװqP3Ơy;M2j.!/v3LCzW۳%ǠX~3#d338|qUK5e$/#Hk)n=@=OI s0XNC0Ǖ)p;8n'LdPaa/Sƥw4B@-x]FXFЀ_1Sģb&`Cz&dC g%٧ Lp3Bp]{ Ҡ6q!ůo4fS/At"u.qܻ]!wXi'JV"K$;vnֻkkծM - mZPRJ_۔R~_S"oc޽{Wk+zXs~3gfΜ;W=WW34x.`u021PPD.`  a:l .t.zdI^(4N\eƱ)(buRE)ǁ,iJ<9n+OFEƋO*CpF: ^  >x.`F2^.kTT2uȏ1uq^ŻŸElCCJ4êA{^4pU2B){;\JY?;JbHDnpzpxPW.` -h%zpxq^I%C>"2/b^]-e8*)UO,xCgT<v.>@* fN?M'".vyuj os2T(OG[g)i<+Fٌ2<<[xNknvcwkMy?'r) F W`>,H+QӤiio|Q0NXaߢgQ-!8d:!%dQ=D> P^O_ESM8ύÈ9mh DGn%қ* FCt!.rw!{$)DW3Ԟ2:FG"?sGL11?^D71-FʈN n`4Ay`ԌFF2z  ͌ލh'/"sV" P{ Dm܊[ڬ۔VQ?]EˈvS{sy/ja=zQ }(f/0uqF~AF7YR|%iu'# 2B4Ȉx;D~ aD`q.p;,4Έz ÝVۃ+-V.0tUBtџ!z%!U(ݽHہZUxfLGȸ8a=mId1`ncDy3i FX`D&1D!3VPZ ܮWP{$*4nDS>(T4# #*z,C4͈ʛaD۪,!)0:AF}!z !pJceVmQaDZaB!F$K2UV71y5AШqPzFo#%AU#p<@O@[ABߒ#M)fa<x)5lO@;Èz\͹?7}744 sW#< ZFgDj=s1 9A3D_$ ^W#z0]`YՈ.D_f4,O0Ognj>+h,C:@#9O"OpFyպ:0ۣ_2nGT,a%/9 I*#Z}ÞgDs&pY|~C aIrMB,wзaFIo1'.p0/3zѷR'##*{eDCO` ԾL vʚ{ 9 ]qS?dDkeND<"h]96~^ D?&GoaN׈,3aˣD槌pkx3g$2g#d"F(i`*E1&PQUa]A]EHG\Oal D}΀IwmcQ3D[v5361̈Z3QW▸X@P&ka\Cd¸+hnGZM`رjMZh:aܗk% j?Xpʈf`izĪ=؏PKZhCAhբRD~K;#oxoQzـ/&Ҍ݌n`t3BKVYw1‘GD3oZ 8> ap0^AZ(So>Ak:  Β^kﰞ@|z`Lj*3Bt}?#FTFڻ{a 2t0[{ `^Ht{!>hAFB4ˎ0F8qaƀz>t*ᕌ(&C`V -'`Q=!Іi'p(OX7؄43 N" gDyψʃqf8@-ϱKl]ƀqCLV[@/)F4Ҥ f60#ˈx 3fiF |j{n0'\`D:e[ )=!FԆs#ĺAFh5r aF$?0\@y>o?n߲Z⭌ɿ&F8c4@>Dog g #~NczüFBd270j@Q팈-WW3ـy #50pjº=XY)"XWR`6G9$41eˁ&. |q6a J _83{>A6~F} #!npueXbb61^mnfDZLA??O?#fFR1gDJ[LiuFW1̈́Xv2;Qˀ-Z%0K݁ a-GV.`}ckmړI\Ǜw1چ#"#{ļ/d3"d~(#\󚽌1" <묧 {7,$[zj+ayG? ;$)I_IcR;e=6Y\K&؅ ;Гߎ>Av"ؕmzȈz#z3B˶fDsH<I*|'!`DzY"Xa{R.MャЂjTGk5?H ~F$ɿaL͋_!ia>.pDGWh6v `^#j_goc0!ڵ4?ʈZ#,+$Io`mxQmȽ%Ө F$`B[2 7sYڏORMg-f D⺃ݬfG4#TaL@xTkhdҧ'ĵkXU#jIy@0v,г48'h8"Is4h{l_b96=ZAjt*ۜOJ5&G(&1G{֑0 +S+@ܫ5?1:Y4ˈg1F7a;6a fz'!~*?bD+#%1xV0Ky# O+#^V#m#\s{Qmp>"<0a聱=8t>V8/#s[ģ!D;\R29no?#>F9@C#*Wxk*,>*W9@+QVO=Jo;yQQmVfsaU"ڧ=0* vQF4fD;h!3)=pnaܤ{/#-{^IK|5#ū,yhLQՀ4滬sqFT^y[z"3#>z@׺wz/7{)FT{0yIB,`_=)Fh q$h|iauK4ÈYBaDraD v,! 2z+,x@<.ؿzF8/<ˆzCpt1BaBeD1AGPc'a`mB$MJꁾ+3/~m2"ͯ[A{jwb)>=\z@aHLO4_`JD"Fh1FT1nF)w}ܒ2 #0gK86R}a%Vm`"+#H ۈm#b~7$ĹzS8#y`'ߐx6p 8{JS ?Ee)F$g{XH9OH/=1B ׋z {@v<Ȉ(|#9G4TC+/X`%/'T(TO]]]_롣J[9oy * NglIqI&M4iҤI&M4iZt"8W'ĕj1gEndtP% %![։Cc=M4iҤI&M4iҴ3F5Go?IeSS_yxdS6?>V =uB5b#:`QQ+1[j6|+pBmu\A9Gid㓱dC'Ar~U,?Pn%!+_~_)^W5E~˨8MB}@oSј(4Հ\K_3&M+bp.u[fդI&M4iҤI&M. 'й+5iҤI&M4iҤIӲWO$rB/|N%KnW4t8 %bP&I%Q&m`F0 yDXnhqL3yt*访H :jfkwYjnewgh,?KiE<oK Et RHMgCn* O٢(T(?;Nd{JЙx"[y!*G )Aaa+.>OfBt4{%2 gBKD|2ULB[E_oSUNdm<(گZॊW8+`T>'Zb먧Id0SȠ9HlH^L/9oD<+/R]0#t, TXS#0Owz 9=NfٝE܊X sϭj>5$q$Y΀[|I~yx8NFbU]^aYrv;`"ŕAys`r[ dj`0'/RݸwŸ& efbxf"heVZkeZ'/gM@`-d,N+jD. M*N$BT$rdd-PeTb[ڤ_Ulp6Mج6~lVTYm:`٬6btf煳YmRfͮ gڤjfIզ6w]8&UlV}lVTYm:T߫Mk)j"3]Pt!cQ"Z^p2".bH: JNkqYv('tDN-ʴ@sH:#=}{Fwuvcsx( g%PQpY#¶QG PH4Zb6UFƑ#ۙP6|?,.g"t&Vqr_}ͦaG{*FcD8G<NFD*ϜNu6O ٟ(#,ґe 2϶VO35L~6y6c]VfʦRߡ!GaO`\>sA]: ÛVrt-@riZ=GfP:RT[gk)㪽lv/Zv (ƂT>Mr-ʴ֢lp`M93x88hEK&x*wʼtKfI= b%wXb+Ȱ:<8R\oq.wZRYF[tk~|S"Tqصqۡ:+[0@ɗO  ,-?&$RT)7/S ymמgUe^ty9(|y יoKA^D;@pUzx]v? A% %f.рMDж*%-i h ]H@/BmRZKkʖSȬIm_^Im@R;Tڡ%u(Ԏӎ ĴJ1u.ZLWB\^I,?ORP j*TM^W^Z^i*/UJ[|wi^Һ\juE i iwy!Uw]@HճYUIj"P]x(*ûdp/(*/ʻe[ɖRw/`%;Z[{ Jws 4.ՌԖBkZW[^+|kyuEM~Uou-*j\Es! 5E, g+EVl߫U |&(!adR)l㫔W n}&(!ץXRZX-0%u5l-f]Yu6. yj]͵v5׮.]ͫhWsj]͵jWsjb\x Df%Hv5ג v5b:v5_قv5׮+_Zv5_B݊ծiWsjҭ]͵Z5ծ+|KkWsjl R7v5/-+K.۾Io9*l'|盆C~P8,Dj/UCсxhP.Z*aϧ|tt&=HkK\% g#B>Jy:j %(rt8z#E)6L69%霌RyAC2gea#88,`V0ܾ᎕pg w$*`k%1]+]0k%1w$w!|i xA˭Epev͑Ŵŕ,k;M|?XDŌ NZ'9?CY7m/M0/c6 pNW!(mQn)Z~lO*T >/;/z'z'z'+a= ԓ@= r(D_}k~ϝ͆s=vu2Op6-*khe- ijP43F1d"Ѷyx8Nmfp 'v:;}#nrޱo,J./:{Qx;C`[ U`{IX.`{ W`GIXoS.;*ణR;Ka,HH,vVbg,vfkAK$$*`RKؽ %])JkAK$$wUJY] X"!wW"}>n]XoHʊZ1 /eƗFtz >eJ%e>+h4|.<ؔJ|V2*p%Fx^I>t:N`ၧTR泒'{|y9]βSXu}]D֟!%BeRt&eJm]R:/‘i 2ZђJ<nM-Uq"S!gNkCd4e7NWSHqӨp[(a{"ykx?8=Wˠ>">j%uګQԶ*rx2 &ݾr;oJhR "o&)8qNV u[1n7D7[M8cB)mk8 9r{/ʨdGM|b?UKSS?b 8F&BȩZd"J7ER_K*BEW ?,;K䖈հ%%2ٞ5isI[QM*IKMږ&n5i/I{e5iwIդí&E5騬&.5Xtդ&դӥ&KT.tդtԤkjVtWVnt/QMvdWQMvUV].5ٵD5VE5]YMvdcckcغTq]Jz~Fp(?TC~u G۰Xq?:G@Cm,p?6ȿnDL 'ͥ7Tӛj)7˴\oM57JSJoM5ƫ0fӫzS͵&zSMo{kMT+XMRћjYpu"Vgenp}TuF/uSItάQBͽJ~oډ'1a2o5:d)Hwfl:ˆlґ>. VOYw~\eT&ZIeqeDTF F@ptl(,i6+@TVrm)VrܞH%c G&;ήfPŤbImprqD'wKn#P:[]k6 ɋ&@/ ʦ -afeҗImi(>g(JY4vӲ~ )NV.%JSŦj٦Z ˊS}{[okɯ'g@d:ծh(>jyV(bTU0ފQ[+FubT+F-nEx?=8똷9`"PEBGemYۼ ,Ew λ%Έ%¥"Ν6XhM/nqWۺ~rA-`ik&79#JJq|0- 7 f`d#MwrGbs-EjctjXR<*j"@ >?L+oy~] ܑ<`X(h,\{-29*+}.1t哏Au$;T쿹%_Â{pgKYD>[v,}Ey-nNݦȎ)  nww9nLop`pȿn,) K΋>@˓"yV- ͜N1NEn?tl`h Nm tt௼D fkW.>+$'/³D,Y:^,Y Ο_gA<)k#!M;ZW~9nԶYw# bU\^ g> Sx*?NfezaәcM2Mk qNn43'E49H0ЪOzۏ!~|=W/Yud,Rlԣ 2dll_x eNZmF% !_#w ,/}hp7td;OGұp6M;JVfᅢ%-2JjbM >ACX6I!| O%T2I%\=n4k40Ξ%j<ZDŽxf*fFa XxǨzF0!]S|M2Fj amb?BzqM[+P/652dZjy7n#rqRf (`wkzf Rx'm/O\]#9l{[[@O2ZqţX.z<rbNa!9 / ^bE.S–Sc1˹XMrvYr#}ãA8voxhg`h,phF=?ܱ-/  _C=c=㹑RBYP=Cc  Cc#=}AxX?7<4x,`P1Lw`(;;>CՑP,u<?0J Hܡ1mC=G{!]c d]Cdmtъ5h=LJCxBhx(toptB{{; ȺGCiX#+!2)du9,@6r@?\pttx4_\6"d!wϠ"5 h!hѠ6|(8t@> M2YYOu'o$':gWx_Ӱq#8[.]+x} ';<lo=B&p_FVA*\/| bh, G n&L6 Lpό@h8'͢q4v"%et "YB[ECO>ـh06 E.Ʀ7Tv۹ɩP6|\X+SSlՉ ȴjuT"IT7a}YBrq&QM ki[9V:5'VB/`eoN3h `<?yRmL&AzH|jBflgP[ zci%➻z# uۏ pzPXf<7dvuIDMWgNm# u^BoFͤ'M7U+=LbX4#XǢQ:uRXfy`XgZts{>y)u0w'ET<5{vor$)Dp=yyv ^'Sð^+O[/=F,GfOlpT9=WT!`XM:GF3sN!#w!4*d\E!M# rjr'Pmk:)@}]/6 7w6&mIlDw+i 2q^N*?$$5%{KQ{7 o4]2[sƆ] \l(xegu{a5͙u?W- ~{C%`M—yN xɜgs xkחߎ9Ð극6׃?F؟\=glu; o6cz9;svSkTt ;XGk#369pސs+8W`3a sBs=_'[1N.spXǮ{kLcLDzrXxXuZo) _~ )U?# )M4iҤI&M4iҤIJ%w O$ܼϔs ȍ=K'cښV V`XϡgM4iҤI&M^D/OIN"'gU!jD\i 4BGvvsΊ5&ZQ V2NǒقDt:!jjkZ_dfwC\8/sc]]sƗm1r΢ZS\hNHFO6> =JBC˼ZGWkwY Z/ @e, ?g?=JߵFU# 𝇺ZCΏ[= =W%34-'\˩m Ɨ7nh_M4iҤI&M4iҤiՑa! }Kr/RUCIjҤI&M4iҤI_W'?8\0A'\Z#|جE8~K@x0_7VV*xirmUmg_LdSa 5YW}\j"S(t٦ߦ-}&M4iҤI&M4itIaBi+sN} "gjҤI&M4iҤITuy'Uaڬмw\x.uuX}W_|7¿A>cЏ&M+vt6l)+mlTդI&M4iҤI&MV!֣35DvմHGkҤI&M4iҤI"ҝf5bxP̀ >(UIۭ pM"z:}8#ET4eWGc\/MSSQݺ2$p[9pɠ3e@]^4! [m> i )s|9C_;vL0 {aa#ȝI\Qpercona-xtradb-cluster-galera/scripts/mysql/mysql_var_5.5.tgz0000644000000000000000000016306212247075736024650 0ustar rootroot00000000000000)EN]yu3vaIV[c))cKrWwI޶ %GpֺiJݪE#4E\6ETvhia5pqp1v3ÙY.CX~s|K9(l3! >~lvFQ9epjRZYQu%RW :RCCc#GƆw*TVyΙA+O*{U3am@.AitEt]ƹ2[_qW|ev;mZ2?tys&.{!(|OH>gxKMyrNu]SU"s0J\խ |XnRQ_UVŢU*쎑@ 79qG nk$);*-cߑᑡc߱q\  O`+I顧G Lx_!8z`p' F @ i>"4¯`NMВL(5mp;ShĻmfc @ x\هgӧ'+$/$!jV1(*I*t~%WJ t7`Mn+GŞ/6EuM7%&0!~*]-Ave}jk¤%,k]-2juQis v_nq؆_c]S.3I6dEea@ @ >D/_h$/M*=i$oR҅&xgE-**6Drr46j; Cl;6`){R.l@ /75T*yPWA("}xrEABe 7<, E*uU.KY=˼V@Y tA0#+Us _h[Ixh 34lFz&% s0>~JU`7-`4]A|%EwO^!^/ꔹ dhMÙX"%Sp42a*2kp1i[ЊZլ"s\)oi_Cǎ95>64_;$3k!/@ @ }"ӐJTlxt@0z"X ]ѵ; @  f鷜>Vr~Ј>r127iXex "wo~r-Ox-ͦCs(ʵVz9\d X?3:::?;yktl5<=`V-+,I2&b@ @ ?w#q #@ ćoo*CE_wI ,Mi+My]"d_Xs}!\4ݬBqZZ?\UOy93۪<6Y5>zVV| ж*bl~DnN]@|navlCG04*kx8 ܍cs  [9귰?:.w|]bOA29gwO~9i;ayBh?MF@<,O0Gb?tE~@}@U"x |yD@!&yF-azI.4isp6 - rp~gx)oﴼA8Ϊ\=Q>+5y$ױM4ww‚iS:Rum{vZZiڊql)mk3GNjXn5[&ndp&o35p"wSR-VK+i,eRR,% FS 8ij%.\)UsX\QIΨWE%¥t31Y|5R 37[XyB+u%/Q_ m$"~Hl(ݛY14M;w5`_KPBB_l@+9˟-LG2 h{0q~ܦr'rs|X~vVb+R^eN ϒ#957Y9)R %ʒJ\\Gه7g*Dz79CN2Gsϥ|b{Mu!I'ryomy:ݐyGR'' aUnOƸzT*%za3|{-bd]4czLni\1 7~jgRf*Y%RoKy=vW75$YaE"dҖRhNIq"yfw]:vͼwO!;z^kg+ػwow#) J5+]ުVYko:yF5uwᄃG[ iRUw# CxZ,mbP-2[p:| AWX6S5*5Ԩ4A. 7/N޲:+Q߰⤘e2\{M"Gj狃 ϫo[=_E/ qG'іpi>fe}Ŷz,=sG^"6-]_!'i5vNuZ,1A\1WXܯ%BX|}yrktTۦkuZ?ԚeIiu\x?vN_v.ڧQ d.]ǧҾVn AGraH=j\eq)}pudxj-=aƪ" {*L]eH~fI3ܶL o&u4skm 9i ~i4lZ0[eGO&bNQWzQMc5=]JbcşZͲ9Xqʲԭk'y~9w=^S+%IGuNVfOU39o@ pO^-XʋY{ݼf5W>FX0Un }tv턷V̖cx V<89/fhH --6xtd<ԎT k"XtnYxU,}X`FjŚb֨ek~̴+a~hg7tc;/)R9iBatK?cIV8 (tBn1:6<0#X?.~.g @ @ ĵEazBCij\G#B큰|T(nv |# n@ ݾџV+TL*T+uO.'aIB {70}/x>U|Vγ5bP-HWBNes~-H\S)#7: .pQA0\ং}` v/W]zDϟ.}TEg %ppcaZao4Cfk5G#Ԝ-P5?4i5iz{ӌƦ-Mӛ֮ifm]T4&gL,גD8ME0}zN3X<;d#LHIDbE\4M$/qo4E89{:Jg'%ɝd&סx:==x"ͦphdgшOe'gSl2;1G%Mţlzv" qw<Kecl䚉jD:3R'4f"21̈́L[eḮ&Vu:b f戂ёQ~26!⻖ΤbLMJefBI]Bn*/q.H!cv֢T"$}47dQCӦhh٦SSYZT4h|6"G6efyS EST`1MXS7v+SZ"o[@7<>~y|?w+Ϭy`'@ @ >6  vjnbVA]6vۖ¦Z |JKg3mvc#ԇ@ ;l*A"}Ч"'?igHL_+]y)_WlN˪,%fmKrVjnv[b7{Y][zGcYHT.HRZA?R(5#?* sjE}T咔}RH \ "8HB]ΠЗJA~?51}4-{aI3 "tuE8L.1xDz`i|Tlov'VfO/\([{#P7 jŸ[KA]pnɓPrArOD%ꦂK,w-m$鹚$>dhv\kF-Ȟ&icFH(ٖzMd/ IrN.r3L2$rL6XA"R~T7/KdztWu*6w~n.NG}[w[qk=@]b)vhè"o7^Y[.d6^Z$f6oRq&X[MRbcͼY,[, 1[3%.orfr/.©U~I $&ʤ-prcmo2)pq}\\OGmZLN}mNO#pyfg@ '*  $qCm.wp;Nh*;U:IpP-/ЈMK(hϻkzvPPUQi>,fSD̀WGy9bF/c,EN?~(3z3 @DžGO7穃qrTmqWDMsS,orESբ[jx/@a?bu-uHyE^"9]?(kԃz3JQQIc#wR FQo$1ԛ`۩`/&rPR, <؏3UP>(qcv:U"3BkZdV P(R"9iҥrFP,^Yԗ-lye+mFce䶭:vr.'F})s#:5bn_ݑ1W р6Ed{1)aP D(aH D)@Q°@ˆ@) ~B8'0H@™ABxQ a^gS!/ 9B5D@e"zh=^YH&^Y8@o,!7E7E2΋,U-/" #h\rM#@!R;i% zh=ݤPB123s #3ܗ EBOa|Ior٠";<=|);jOE5ŢC`*I>ѝi#3#ӿ}EӟɅyulww/cX?!-gUzjtM9 0aPw(b\,^g/PA7Ba@{;[6X$ݯby0<Va!L=LR'Ƹ<>o G,?tt3@>@O=h Jdnۑ 3\,< u^7BݺMs>Gbԡ>](vߥGtGUEϣzPvا0j}KWJ~PԒR@Q,lzs= (QDe(  ݩG/ CwtԭcԱ(R-u48FRD aX e(%Pף=lE .I$e.Sw;LRnba%&bfvkع-f=`׮RlU#-OY#py '*P5j ӏy@tSz:d|U)2Ԃ)cb'P⓶N"$TdABt}AL8_Q0ОנH÷ꢋ&(4CW#ځN?}:OU<3LHSZgz6e ?yhb' Nl#+E_XwsEEߪh1olϙ< QM;c,o~P!@0o =DћC|/Iz}SWF׃;r~M˕?9lโݩܿF: 8~2Ў`:GD-*;.0KL0h!Z :H=%g &4kSW4ԃbs[x/ B]C@Dl$Ē!b6&!&uƒoJ``Ix&~ !@ 6'1mvZeNcpF)*yNxR N"$4r7;Nr{8^4ȣyBn@ާwYM!u"uv Urӳӓs~0Q.>hRߕ8AI |ɥ޶WҫYys5J$Y//S[)W~7KL*+W;rtpW$חWV?d7oܼu{gqb|Eףt=iYtˤyQF^NY y_kQk(m/Yo0-QvdO*rVtEO^URj))GNGZukc'YS9Dp'PYF3+ӘXh{vLOSC-q׮]|pؕSfk#LOW{mi`F.{l `;7ہPX':|1WK3`af5R+1dγst( -~1Lx_T`Mm`|h|r. Pn>bv~>cZ}2透hw<#6t`cѠͯseM-W lLJƐ1eDG\nnG3lt]gۏZfji>ܫybSIS7TrnO9݊xԓq.`y,'aך25,G-h\5[`j8ޞ<5(+ˆ:73_L3{IL@(*BYk^.ot4/t߁EEɚP+IN>9h;r¡ԙ?}9S{PgDGfJ${ܦ%AA/XbΦk(DZ!ZD>Q.*wRoTc+ׄgY׾&mƛԷ'_7OWwE`_q!|fTTUos[r_߼xO =SR?5BXae379&,@=ayK%6S]zc[<Oߺ)__Zs>wX-yGqfH~3aJ]Me~/NS:\ys+ΧrN7=D 3% xf_6im[6VtV:+/-6krԭDj=^p͛O_9/Tb*XVa^;UwS'ڛAe#!" Qc|sQVm_Չ=@#u4n80R2mp%F̯3Q.1P g(JQ7QC)P+&i%(hOq2W Lڵ MZWk~XnUZ~m46+}^]֖ڍ&ܐ2nNQ2媖W=UK&_1O`q;xBM#"p5;#}ΚUJ 2'z_-{IR:9~@&>bҺ^U ~Y6ۤUď٠#6hC+jŨiEo;Y7tz?x(tQO1;|l^3':a+i}"pZ5% $m#'eZ2 &',Qb(m{"l-xVk<ڜFyOѮT ;AKLI#*9oo4Qt\ u^jkVPvj!S zzUھW'FX91:!tB脏 zIޏ81R=0j zmPDk劢oUeDmlޡ'tjIWwJJ EIJCO&Q߉YlNOʺd{S^q~6ٴ!q+Ӳ: jejvxi+^,mڟ]@Jj^[I>8yNN2p[36XQoĜ`6 VD~lGKJ t\u* nV$-n"llnc..ɲ (&eabY֊ydyb8r<{xL2K33ch'K49{={Ͻu-/?>3Q DOiL0=qLfؼr+[DVe,(S!ȂTNy߅Lci@dXBtt@o?Ť1 |J| j'.Æа]90/ez.Y |"}CgPװ`M-#7z{OvK@=7k-W=Q6E X(ݰ`DG$H4Zj5azI}d<y)sP\ffZq[X0 :r'W_ekSX$-Їx}KO, sZv1wMCllsrl{&z\VKF{a}lCLg $ 69;%jw~/Ztc>c=us{#HWg#WQ}(}(?"Bv-02\es:bK$ 8YbӒwiKvst5nGgֆ1&=)K Aby*S?MGpGpsoq~fc=I5zH>_AKHZBׁ6"K6!rIVQrd)}U4TJ&yqY\vg}8g)G=r ٙ-̿#ixz[f;Mxi >p[*@[em؆GMaWc~}iyqsյ76WM =7) mU)gt%)\%)\|R|SYkh}MERC7LT'C?y#t}P]rtC0+3 ^sRO9 Slb9 GQ:m8(\-@VsX. @ _ ⠻L~L4˜0O-i.s]Uc)pC̘X2A|vIAஓW!F-Zz7}??٢fBCTĿ%I8SY [ڒ恵C[W[R`HaA}襇].v+{Wxi7H!P{W}n7TڻD>Rh#8zFOyX;Cő#nØlͦWmc=[RqfTL<ը:h{D#U}(ܘ`!Y&66`c 6>ll+{L;dK$/ N~}m,9TIqR?Uq9W1ZğD];{޽tΜVGIFjR(D8zKyK;{IR3(7=l9#R&L\3 @  t q=4;7{J4K[?5chLJz?2:[߳܍# d"ƻct,H7-c4J{[!"m#hqK9>~J+{m;ђi$my{?䀀 PrNK[e^2vi9f߂ ^GJf^}tҏ&&p %<{4H50SJ3Nfa Nzt۾۹u"otU»sk'$2skU])̴{[jZkdB7ɷW~^ݿU;)D?QzXVZO/n,) 'G_x;A_^_\`d|ۭFmmIt+ەgIKFsIАsDiPunjmF\۩:3mҜ͹7K+oMa=QO<$L&KV6U!˻_ 4^%dF]G#{ ){Tq ɮ|g-A\Jw+JsL %5t˚EIa{XIO N++dRtR*n%ӑVx2}Pi{Z+0_fXj)ezJgP^+}rb*^- xݬ3O6 7huzfii>wmqscS+ ss+;FDܐI/UhCK0l^ok\\[[^!WIB0*K ,8Pl_'^L?dgt5xCgCTVIe(Z*)pHdc#D֋O<3`13FbRR|Lvm,6nZ JR P|eg S#SA QFYkb&Jk;SZI"ZH[l=Y< gAǚR'Dkr&1)H~C %3qS P4S[lXϰa=zFhdWoxk Q]xnbC $=#9Zm;)k$}˯H Y\p$"0ixEDUl 0ޮMg="%oT$A,d1\CFޓy6"3yd}y=L(zx엡laٶ{wENna$) ɷ ȣ%sF9L78SS:e$6j8ɞ!FAM/O!=ROY#1Vʨ`\Q%RK@8@8gZ9d߳=~TGA=[T=_yP1 *2ij(\W*Oq ++<\rQ r J,CȆ6vuls#Ǔl6Fn{̫iL<~Oqqq6ЁSVV)+!Xy#CMWt7+;[|e$\[,7&@u@Gu.+VJϡL12߫{nߟKz40?w:^Sk+FSzJ;{z^f""z/㤸096)%#VetK$.v9x1lnafDPz;6#m?44>])Z0*AR8\ 4e F% %`56q5L!k:#C V{Db?ڹhDkJBq KāY׹o1/[)l/YyE =?iٝb~K1nM2*Ma9XZhMa]VX5% kiYDrhizH  R4PS m7i&aV]k=0ZIX'źxe ʇ+V>BO}ˮ̘crծ MP X`65J}Img<!Ņks[PLNݸ *'KqiП0ؒqNIA@JbŮ6[{UJ2x3H wYDRq&rA*0=\EWbY3\V?Z5+K7Mz(lZӗPHLI|z©δB Bp%5&k rYgWi,{ PxXK%AM\ot!TsT~Ml)ʿ+K[Fcc++4puqcpF3g))f/νZůsOެk[5H r翤kʖ4ѐ]mnp,qvQ 6P9xD#rq4Ty:dL^ېFPbGe?g^* R4Fj1>?1M07CLl8py5X` X]W 2^i,`8tmHfE@AC Z;x\ V;7>pBܔϾ.KWؽ"=HC{T?r)BXZK7k#SE, VGߜ[}nm촒]Y7$ OBج$$S ~iWm;@ w/3e!Pe+1J0>P+21Y'@@~F ``+\U&,k:*XP"6/C27U%&;>o~XiòwDǖD.M!OČj:U WWŞNI]2sԹC͓͢2gF(D;`@ 9sDaB ^:Hbzt e")SpUaP!wm~fGEܦ$وMTWޤT*QUbŏptC.Ųy3h-oxJyyY–1IVj;=>Ɣ i^6۴H#=A֑JI4ih$ MO4|~p2:m"@zgLWl5v<*ERFq+4*X4fAxC:RU=+X[_KӇ) h1{ObqpX$ՕHAG)h֕p@xl&is3fMYo4v?Vx ͮ~UiD y< %i3S~}/~C5E[-N>x/VdFq`zψg'Lm JME #(35g`Y}ziz\_5+uQAΥWG {}=' N@8eLU7b}azlz7f[rTqTNh@12_ 9+#vJaqur!xS'dLIy䉟ۨR3^ jW ב Ck=FMjQ: DNc`F(M5K2;^; w@!Tz8 >@I.$'rFG?Ea Ey<<@QC>.P* 3/;+ ~41|RgyTb>:^Bx qSȬ)0F%1& "gȘZABX6F@5&S\ƾ>KKNՓ8(TvH{H2JUkR2=;5I%N7 +|qK~4~1f<$  9(Ӛx_^ f@S'y\&sJ)oAhO1:U cCKxJf"9=0|M 7?V ϑmWάmO 4T68bcJ- M[驥SjmB}5"dvq{سR_3wV <95eRi[2c oB/r#k 2F,~[gƫqL>ֲpH&IOZ_ E?St qn:-0E>evuB\z|_6у,44BtG6q:&M!(>=:4J;v856#yV[p+  Te2~ѲԢ KjMɝnBx˸bB}Bq~ojm VU]#$3K']E/fw//]cm7NnʉO9&°~g~ ~¾K5ۣ'Hg7?ܡ?&Нs`#ϻGxb;;tGN[&ama4h,X@cD'ҧXw=gHB3VJ5 |Jb]=5`?x#p+#24kx܋ 6JKb%u4N Ĺ2eopPӑS ^jWuYI;z!(/Zf 19YD`&ŏ^4~b?V>XSb(|-cj RR*8n[{5Nt2tLt23giZ^Z>68-9i*)L37` xlu358s  cL+34:!b$f.QAU~U#ithEcD~!I>H 9̟ fM#mjѯI_ |kA˟6maj`ҨL"bЪ\~?<,: aXԸB=ڃtŗNAv'YpkEA=%Ȓe)?A!߶宇),XTmz ayN:KTF'dC]&3p>1"~vi~vI ۉX0[QҮ,8K,5g %pYj3}@9K~5ɝvv_|~4\Z|RG'23yV+;V¹lD;NXCI0ZJ?Dx6yәc(,K5dEa4pʚJ\Bz֙C8mÔt5WmRSzĊ;Pi{tsg@L 715X3DVQ~ĝd+`rDn\jjj#?`{͗ݣTĹ6=VךkINMżlߧgӕ-Dρ@ZVR,6 ڙJzҜ,c}N0nwjeV7PN}kZc6Jyo gMkBܫچag)Jʎ(ʴxΓs7[:6`f((-D[U3@洒ʴxBh 728|ǟT*/Ĩ3!n.dϘ1$0N!%x5f'@l;r r?ZNt2 SHjɥ݃QFȦS3tͦ7d5?B%4)vN3߬y|s\ `B)4F$RLv<.]"m\b# PI!!ex/.**+|VJ9G*g78Bį5C@7m#Q0Lh&4TZvB.u,r x&f:-l|B|lxJg*[p>z=x9a ˙.YnÄ_Do]2嫤H ZTJ%'ҖSɒ4n" zYP:'a ^PbkGQBLd➠.eUՀ*haP*TAny@ P U@t U::e_?3-,Z6ԚZ(boz*mme O+\jC'4d!c]fm*-ת^O+P Z%v4xO0ظ) Zƶ$H$3ܥ e$!|1ӬB'Ucf;Q~CC ɵf$ԓ:2W@cX99pmFxt0z7݄v{9rr]66J%9$i]#5fWJmȖjHG,Mt7ML0p([$BLq; 26,(L+d( '%pRWI J/\I I NJpR}NJNJ@I}pRHw$RЉ;O0~^^in5v<_tF'7je} ݢc.CPs?ܸBzcYOy´wm(~۠8Ŷm{ΦPʡfU rJ&K6 ,l6SYfa5O!?;fy* k)?(c(dF2c,%3 Ñ`C10?iB (U]Lqz ' D3Dn 9bqH"#$,b@#ZO6({]p|,0qtrbݓ񊔼PiW&0f$f Q||׽#@: ,?F 00 00 00A_tޅp7}6#'E4ܧrAOmSQgsֈ0"(|NwF=M`RdMhD,/VQ 3ـf+%PKoT;dUkIO)$$jdQj,饐R : h`p;][ڹ?fw0nQ b$8C80V ${dn[ѥYx6"ZRz56mԪqJj[8 S>uWLme`]%ĴbHmtS=f%1]"&!Cd1լq̜FhSaQkdLR.MH3V!Uꗵ&\Ƥ,kK~|ʾU#-Jp@n b 5 րXk@k@/~f~6|ӟmsVvia(r>nd]+u<^SSOY=2}pI X41h#L7CKHwI/}KɀTƽ0*_ͯ 8=D lT/)qj; !K&i ,b w!" gbLELbΣqޤΆN*L<ZPV-PmᎬL_g-7QnE $"켯5:g~rTlHt}}QA<@8R͙;}Z.)qG1p|qH1&a*pN%OfFlLfԔezy8ekPrB/}qaݤV2P5FDC6#FXd'm_:b&ɾ}̬:&nɊ,QVmT97ϪnﳘB9ARvdp p &p˅p I.GL>Ntj NtA78]qYƾ_x_`^o[mv G` =v N (\s0ԥ# ['lu [gLu"q3}@}ڬRl-q?{8Akt~?_-/X*-?\St8:11*rPb,F?MsϞ¶؆vmԺRiyo3/ xSw^^e a"֣q7Zn46wWt< 5]A:FH EjI| 7thQf 3wnt'6.TokI*9R;2tH\ KHd9tM2M[ U\[/%Y=@E[g:7RQ%d!wy3 <ڋsHI, c0u.?A jpPh jpP C@A jG:P{#A ~*kξuo7#ہ-8#YZ ϲPu%U3֙PG[I[7CNjՅ)mRmClUUIPe(J5̀ S#k[veH@TP2([s$ALDČ>lqy5}GI\}W159)Pk>Zdiu۬g[[(yA2@u;H%GɓZT1I^WWfc MnUMnt @ ͨ<&h7A9M^nr&h7A $evMbzro4JۛSi' |vkH18vDi;'-JRWUa·o*ԂdYo5]i->9WRbPzݟ2?,Vlf?K63?~#(E Qx6BfW]XMK0'RVuRW]n׽6)zJy #ZˆZZZ^g yUAgYvQ>w"LU։qgv+V.ޮ\y]~~GGnݫN;23ÖpolnFٸY6pxc-myv>1sv\I/!~@Ci|cT宷哴֙pLPYb%j9$2>J1LYL<-W J\Q( =/ etE&%F:$n tm`K)msw^k{W*+P~vc#zp=TIdQ'Wq_WxL"R1jn/-_Cbls!يm/^3.[ 幚U.1b}qЫX{׸xsaHSMe?d¸ ;Z:!*3JeCIJi#J N0%,'R)4[>P>JydL7T@Jr@D44>P-?mF.v+=rd f5x.ޱG|Kr|az|fO})8:zf#";=7D"(1ʈALm,3 gY.w$ X}ơGÝz̓x_tPuG7[e%'ܒ%, %B_rT\F0ܒ )~}{vc[|+TOqS6>Dj*|)u͡`qKb [800d ` FkfW%IPP\7G'8ZR~NEr^ i~piiⷹ\Ǿ~\nu Es~.zܜG1Z ^o9k'"ь 6 Ds&B':EZoA !\m[UʈiQ;Gcfku'8Ӓ]`u;N7$^:r`cFrH׋g.Sh#E oϓt@#js-BAom8˦'R_ :U  Qx^rCr.E Us%n on麪!&w/Ր |_va吪k-IժnHB/@tg2A>+2'EWs p%\p %\s9}]ZvKp V}6W85z{5[ܛZUkfsP7] b8otmS7;{>!?;CF_¤fYcSMߊK[,x>vQgy<-a"娹e:z>v(RR⋟!o/.X[wCd"]ܫ7pֽh88F 1? 3j6Mf5q_i@a치e((5#cM!> }N/$⋓!s}qM6-!P<_ꓚ7=GVxahc)ęlRE( jiKL#K|ȬX";/gkuUp6b#MPq\*qMH_N棔U`Kϴ!IxiU:ܦu8U@$Fa9ʡ3fu ?X\RR(lP&f.qB:EtRY)oe0}"Sě$,S /֢86D֕kE(UQ0Ċh!E!f(Q?4B77~N)I( @gَ[f@Rffv-󞛄 bmOʎfwBXBOy^[O46@ ~tqx1AA u"t!Y#!/(TwnґC(y$o@ mhF4ڠ6hA mh@ mhF4ڠ(*A mh>h#4v*{m?5Yṑ|o9[oZ.̲]Icn'ioz͕[a+͛ZDN&<\"KaRɆ:swo3Vbpsw!XH Zۍg.:R"Bh:_m3S.8.vqa]<](Is[â*U<+#EmɏЀ lqVX1׏>D:c}cŵ+7aRJnjչ7|Z̆ز, ח\\{{Mʬ^Wn{m2K7p[e,6ZX Z~sei>rEāk# U:DWܙB\[Rq\c@ 1A0,VgZ8R5vf£26Մ,j_9T6OEX5!/LBH}2- f%L+drf厚2D_a")]1#!U Cڎ @iDGA1ǰ.*A,9 \@p!޳_z6}k3#dY ?SLgWUչGj;wIMfYEg0-%Ec3Sq~Woll7^]QzOep?AvO!!Jä4eK|Q ˆ 9P`8j3%9#l-/u{m%hZpQ98V[;6lJ$n~~oN>u+Wݤ+)\-9Yq#Vt b80b6c6&qf0\:\QCe6$Jwb#59Њ@!NZujYmYֈd9=6/E|ve ?d&A$ouS,Y_R" |l  .GV_()XqD g )D|_0 }X} آt%9D0 XŒQ49,L]ݪIpCA}Y<ոqX^Av8tT kLY^Q_ c^(zð[-b5 L*L⒨c,S0CTu@LLD '}=n0>u~L\bw+bu\䪦d#Aq9S6L[3¶ȆQhGX>i¦mr PʔFF٨e+6YeA"QaV5ŨUVyɸ C( tt3d;[R1*GVs !l չ^hT6@$s0kB#ٶ!4XrDMQڶ4Z YwRVҩ'sj+L ?)=j"Zuh^loYˉ-fuznaSJ 2 i|浈7釀&uFbAIJO*DNIQ\;f2sz䎣Sj0pdw@UM(m'0j60j6 ڌ %Φ[`&QQ[,XS^8i& 0j+9`Fm`ڀQQuk| ˨8{F R_SmAq#x5 0z(ŗݸA7,-5KY"0ɞf<+ֵj7敽^: V9ټP$|DQK]°P>hO(iRfcOw>t˛YLna4Tٖ\ѡfP5+A*xAtB?us%e1m_|.jg2EtA2ۋ c`fyyyRQ q{̸ 3~OBDu׌HDL38 2$bj73IIM?Zʹ'kY{^~\Aq\ƓB4/!1|P5U8%dNY+ҬkRF2ut淮&Z{vq r[K &t!rѦ2*gNk s,f SR[6,$*_2².zbuǬ@&o]#3ں2G *KR(&ܱ}z]H޺,l:1<քPg{RC5E4 zX?f:b9o.EiBk eT=ǀd+XMy5ȅՅ C@vbal73ة/̸CDY'͒,L1--\lK#^Aa4\1͇p-Y֓ kʖ?ٺK1-C"2TbKbXc`24 R2c2*Xr L G<1m_5 ކtr䈟;EoƼد謩)PzM֗ "`h(>aQ( #f$G#gQkp8YλdyV${C?ޖ#$ o"{$}h#cl)fJ,)A\cRAeEdP(&;/pp pp pp pp pp pp]k:8>sk8pp Ar8Vpp=zz./!-yε{=bR|Pnk^3]&ҖWmW3gah Us% NsΟ Wol4ڕ:YEJma]'T>A[jER(9Fͻ5L)'SW3"ђCLlaC# v %T9vL1,<f] E3p#\CY1qN V|6 c( f c&Xmh{~u,,Vy0 b`Q-1i_"qEDG q4 E6¼B’Np,I}*1֛U1Q@~0B6y).6ԳPaHa3Iӵ}τ`=93[l5"(ŀ6qir 3=1}IGb'W&qy$+ (3$__Mfⱹf2;s̐ʄIRCb%8@oL>Gۄ&bua`=}2wҌΟ,QB1&Sµw1McGwL"X|Us b#1ݤDHdB\d34=rFE;̭----8h5m K- ---D[ -A'>q9'./--m`cfMMlylyF:g\jm7m7vm\^Y~(a$<( ßI xJjśB В\lu}3(U%[{;f 8#j"Ǔ%ߊOj'˦dnil*}Il؍#!emI]kll>RY֙`z鹞<%(,< 3KX6Ͻ2ҳv- ^h s}'M.80)]cjfh٘KYf2>wb-P2';<,xXC c!zjfp Łs2':vMS!Fke[^I(IZXE$ժvRFt*&.̴`=40ަgfzޠj2"ņ֛b;͒)}q7!o5D;9>+P SQ(1Ew}S^0'h A7OŮ cRy; \LB q4zƑHHqL 1X+rm=ZDnV)|rrDc_<63~8:lz$㉞LF;RfYj"44K4J+ 4J+ 4҇Fsw˶M/.W/E`{7|oM"3)y JQ\4+2D tҁK?\BT=Jv5ĕ%k7LĈKSF\nQ 0p pʒ8`l&'v[矼f>y8o༁>wԥx;6,SkaQoG4Yok4\4pEl.:89CB$lY!5G8E(q^SN;SN;SNNfGjo4wNk?f&TK½V?zῧaJ#$(^AH~cwo.m_/S"r^ -BO.:<#EiMפ[!G_:v00L$?qj]F:i;ʱ#739#GXYG!?#eߟ!Mu9q5Dmn,A}7G]+ 3#)FMSd#`$'d yf⬹4I#2KFXCc.Ȉgd/0:w$W#eDH%/r秧gfɴH.)#vHV4Ζe*i#=8d+cu+$zw L7 (o!T>qnU– %to *~*²Hm5!)zߏG(&O[F햫씋k^qIԆȫ2jy.N0z\xpU'WS؄k|k/4{NEkPh|D|Y\7nz6Zi$j['(w!@ƕxqPC6͛ HxGiyͻlhY8:!0!*o5⳱؝C*H]5p( Djg84&-]FNʏEN GJwBq2; X6`ـe X6`ـe,B_@+Y'D'!DU GԪSּ[{;ܡ;M;Uk'fN]5*G-k!4ZQYf]%wK֤6g[p}oVj.YfV%'.=Ri&$% yX="wPvuۀnÜ,l^J\t.P^5' b>[:{IpbT6JIߘ))5\͓CÿöeN}ʼB[R)Lsedq2k^իgr;w:1^@2!/?eLKF2mRvaj#g *ۆklz6/n"Rf0hGFK87QP݂UR43,D}}-Z*Bs_0pɇo_!4-m0mDXWE@~sM͔3 . e D0.as%S]Xu}FňC!^+#"Be<5Wg+b{+vK &.bkI1N[rdڴ(>v٦ ;9XcA&ȱ ǂ r,ȱ ǂ r,ȱ vME/7YZ=;*yV3l=&o RL 0&Ƿyl[ĹXD5 gΆ40pIO:NI͚T3T<&C(lj:i<,VC]8 H-!RxOp>S\LS6#`+2&HەkH**aYhq)&f&n caȒ́PGǁ Շ1-&-Zd--4vZtui,5}xTXYr\>xoSWc)PQ"~\!Z+1#C2*nKi~,z|&#_.C7w}A.uP!'z|2B.T$chE$oTKF(E78ݚwLxΜ-³r<uTARtaoU.ju!qnQL1؅{c<&qZ80Wi¤fYcKt9}inYSiNLOOzv#ieKj:i1:iT%2 ѵc6%ߩ62C^UZoixxا0^4 HT,$+iP{!&121b>#b~zn:Z_?_\y\O!ѹ !4IRNMiU–8 Kj2=D*E*Rn T_>L񼑥dxS"dM\e\78k^qIԆȫ2jia1,a 뚐O8'i-`'\0sܤk^{Qݣg GFhd|Yl )VZC+mhDmR(aJ(Q~Dj;ʤ(v|4,H:0%S*o5Q=[Rs̆[lCЕ&?=x7_61/b2b1hb Db_Lv@m1/ŀ/ƀ0};<6i6i6i(a@&NR6NNN0X %8m8m8m8m-}-)]xFG~~ve_.~G1dp:Kq~#ql04ƱJG/p|8l?x1a.Je/1**U-8oI ځs8g8g,prYHFQ(-njcYZ2tY蒽EuY\Vbl%8۸q*wz"=HQp(Bφtb;@@.8#(:#@m -75p Hu{\inyMҟ~'Rwq%Xt Dkv|DiCs67ge\+7'L rX+'ӡ%b)ﯝ}"A7KjAG\\pRݾ8 W,w6\n WÕ0 sesc2N"9Dm e+benCYv0~æ)V8PnrY˜/!_7Zβw, E,ƭ1`YfE`=YfQݠK=KۆwAhIh 1ÎY'GllXQQ t_ظk{2J{Ƙ7\\Hu"zv mN-/EgUj#RD^αUSaVŋ:} *~'Lpf٪hӬcFZd.UbɥerXȨV!VY XInTTS)iD9: lf&TY4]3h>>| cz\Wuuy&;)V)N%ERI&ibM'Kh_++reܣ׾-+UHû2Qb]Y$[J@` &@` L 0"N}?dߛp-K*d~f6bC_덽fz//gZŃ΀3 rLΜ Sq|1#VQ 6WL̕F V`Z*0z-mԭFO`Hp>$Ge2ILJjLJjVCe>Ź3aLYq=bօdYxϤJ9O)o$9ЧH2{O|4 }sc^iZ}Z)A"ӿi|S'/ט4BA(j\ ៀICO|t1ӟ 9݊s*jcԸ05/3~>v ;6>ے'zc,bܬ**qWw\pBik Dŭ @_*01bH ohe}׈PۈqJb'CT|0NN(i{H$~'lJ(/sVBH|r5Uc.9 hp@48)E\'Ԁ48 hp@ |ߡdߟ?l)'4lݧ"ȚmmmmJm/3+umWqkIzg՘ira62>^'pJ>ƫʌ̖j3@k-rowPD{GJfJ>R 酹K *"5(y75(<龪HzA؞ݮ4  a?ػCrQOr2pAZi04/XrrnW~+ Q3n9?m x'BAw]Ĉ_%g, 9SHjEb/ilL5Ț\1HQ(ϲj(%^ ˌLL?FO>f}{\i AKuH Z{!H D!H D!HFp$Bvւ3Q8r!govaT z׼;dJK烦"6}+KfGY ^:2ˣ*ErLq/ `Oli;Eeb_'2id0bG_bMJ5X|EW2TBwVi~T1JRhdSsϫu0$Ҥ9m&L V!5i ,Y drFd3.Gd(USKGE8?x~^rm;Pp4 .6W[Y_2&-]%vtaV9]^wk[Vnڄ][-߶5${L;߹w3A!&+M墬鞬) eY{q]e{v%SFcD"'f}igf}f'gYӥRNn YS׬deرv֛U1M?T.~&iLp6p˷.:\ʷ/R[И)P+x-Lx6bm] LH"b3骭y%S ]R5i073=/@} P_/@} P_EĠԯqtOaYmV4'&v.BN,ZݭLi ޑ_l O6wi,5:8]g-[2ޢkEd*udYNYb _*}0,VlFC!dC!l>)fiq7@["MVCB;Jb)+k ̽v=5F)w%`bʉG*zv;rwFߑJerxb2TǛ*cIq6VJ\`-[<3O1|}k+d xMLn-O$cnn^ir\py n\^fC \pyCw?Ni>`nn^\i>ۛs}'co37깖74_Lʝ^ً5C6Q89[o%cK}'fjFdP(e[G9)7yӈPNPhk"7g?|o08)S(IT)Qhu'%%C L̅? ap.e45.v[i}'/껛xۍ3oY=pn|g.ff./Ε2*MgւMY*ۈH?{Gs(ڷMPwxrF&)(C#$fHMqӠdX8Mt7?}ʧ1{pL Ti0zW:?7}"^j?J;,/#|Hcp"r|_$+T5ߣٯڦfSK#cc9*\x8?sϭ &1s~KHx0FqY9ecUI$.XY]f>fy^!Ei;Vl`zON?GʿS.]}o D`HH?1[:1)F"5սv DKA*z7ݪ#Sr(zL2٬m#њ`JiKG^F;rQ 5BG|!#ေqGGA7jL[WkdJL]@t|zͺmf) 4^x{ځ?Ab'3h |OSw2c][֘T^2,n/GAkx6GϾ<=0svq ff̟Z>{ ?Ssz'H>ߓٛdDtllzUFO:>N;v`%#R~;{uLhlܫ Y9rT_y_`0P}#G-}@~1Fo,d_αVhK~?֬M!'0݀C[0 @z|-Y""KNVn9t}F>Ə&9z&>Xa6UjXIlڂ"Gުl&T@Qqڕ]4z2['LO_.߄Te?ɱFIqaVHQ,>8Ǿ؉}aPpP :_;[77ovۛ-^vcu_/\^˽CЂg#&-;O=i2b>ߏ1}RSZ.ĀV6+Nd<59 F_A#_G##Nm;p 2 (:QY6w*G8ҷđ16\(Kໂc _ ?DTgGFB;GPㆿ 7o}݂}սj{imݪ5v^ E_ۂ^dž/^ߞEQo0Rp#?b̿ ~lQ?w DgH3ee/L_O\QC7xB/Grq0;+'< z?^BCIGaljtjaAvmTkUG{e*-?ت+-:E2W.vWlG>OL2 8p S $i!@E0)Ӗۼ7x?h-(Bc7sjWxzJ0i <\އkh70o^*ֽf˫6v:~@ 7tYvsJ#?z)'T/=x?evBx/W|,q0*?]?vU4&DtJ'q3)צ^B?/\3XǼN{S#w6ؾ*[QTwq16+;[;*By{oF=81.{ @Hq|Xep @#e仁/Ī-p?ȕԤ. e쇍Z4w?h}C?:@Unv7fLgoos~ЕZg֫IFڀOhB.Y{G>*vԼ D#/pԉ$r .ɡ_B04_PCܛw!LKϨPBS1/Q?gOπ^ 4z(?G:?}?})R܃<3n9~7CN}b4?%䍖,myj;VXZk^!F=zMvNmzVEG轾#xU.;C_Cm  BG}7 ϝD?"7H>DWnnz+mYoCOż`ub, Xׅ?wy^j5F _wn,/"NbgHUcl_KdUܨm{%b$26_"LAoҌ"'zd#0m#-ɤF)+w~ZxyeQ̈́?ƽ+e7jN/h6ܞ@ xiy־W-VÄfk  ~-3%%QN< ?~9yp7G_n LO c9TS/9JrJJVn!p=_=D ?h4puhGQt|ݫ{6E[~<7 8*.4#صfe/Qtrͻ5oNjNcK;[=$:6Wo{M1_p{{wkݯ}Y_1?ݚ ;Ni#*>~E8B1W} 7RP}lP}_C W{?X220y_G}'Cq/3Wحe t̴j '|LOn(`!'Og.B 4}~m!A`հjxW %ObwȐqaf˿?%3R{44a@كc1Qۍ}wZfmތѸw[P5hbQ =J{X`=vG#FlA)C0^(~$/ixԶ [k5e_3ϝzw4-os(J} S9q/>~sDg;6&(3C(HFܺwy rJ!s{4_  >\FޣУ.Z~ޒ.LQ ա+zI yExR׼mީu<:Z\BNOһOGww OEݧcOǥw}zTzIۻO}zݧǵwN}zӓ;NQ>/=G їQ_zt_}3KOa_z"#yO/=?ŗNė;NF}ϊ/=~\|wNnSV 5 )\ހ#G|XHi||2??4GÓ{w_OK>;!FcQx,zg~h^z%/eN#Qϸ>']>}C[sq_νp8 `$N_DIa (Ģh On>CϣV%{odsXhr_=2^V]٩zoRu ShϢL[a7ߩ[bݑc37^I3qbJ>HN 0C CkWWޘ[_\}9H0,p!ȁׁEC+'3:2zr'767~e2Cϼy}e{6:q+77߉:S< c;~*}F'EzOڵolԾE怎k{+ks׹ssO@ n_<knu &v;?!r3Ahhudlud`uJ22ro6v2paZ)Ϝ^jNȃ@X0tCoGD_!r BO|?ЇY\FϏ >Qv=bvڭw*5^)OOf],O/@yDEO3pLf6 @ns="BaJsLfS A77cEGWn/qt|cŵ TTD؏6pWqo﯏~-۫$zlyq}cifPqsK+x3?|++W7GcW][GcB^Fё©S_ -<ȝ@ѣ?D?C0~gYˣ+,">!x ~{vaGc/f a bq p(ꛏ݅;?vZm X\pz ^`0_vm9ͣJ*KhuubxT~ r<1BcE\ƒȘbG/:Q(!t މ8Gq?hsEt=ę7|e2cˋK6f>+xRT̵hk؈wpWh36F }@ ю}!BqbGWY E}B@خ~{vaGc/f a'qyKO4C!4æ_vwZ5Y}JO2 LmǙ33` iGx8j7)`'b]TL;n?N8Pr̎qC 5ܟ=autk /Ly8\ll |6g F?}U:|Cřl3syx'|$H  #'`B)r 0CHN/__Z1 =+/z:p?` H)Pf#ڵmٹPrOo@Zesp=X'<~5>kA|:?}'"ϱ#0>ڟ{"_^Y__Y^ci~-%VH`@?3'|h(pz}Ahhvr?W/nr#77د1tleuqys~-$hAnd, ;V,8!%~P~X~Xf-gn 'ݪ/VTjkڳ{[/F^i;4x5+jt dV.yV }|!_7E8percona-xtradb-cluster-galera/scripts/mysql/mysql_var_5.6.tgz0000644000000000000000000022443012247075736024646 0ustar rootroot000000000000003LQ[lWofwcΥEFIKN$qj;Gݝ:]P*/H}B(TH}ꍾ!T@BTH (9gfgf{/73gΜ7gY*ie|6+9>ɋw岙Xn2cNװ좩TmZ/UYofY6uم$cL׿`d.Ͱ?>Vr Y4,kƣf/pn2gS\d7ǯ) I2pH>I "OmtwhNzCczwLbi2{ؙ(13 r\|ڳN't뒁1ܜ={ٝK7=Yͯ}^THD Xۗ'Qt:Φi܏^CޔNш$:*Ek~xܹhG{ni/{RZrgױEZ{:ҹ,_ :#)6i(]-[uVWb`iQ뺟sYω^'׺_#|zuS>11;)KJjX/ms-ϭ'9ɈH>?1ۊ?nz$?!'"}DI^~OPdyIb.&0B l@252NlQ*=;J$?Ysx N(ݎh'bYlg`΍zhk5m^35Y^bWkI3-iۮcch,}k)?o]ŪHcU%;Ъ+EYWA_ۆ_XxD&;_>/IY=֪Mz G(> ^NuHdb7i8|IL=xl`))@y/"*'mVhe}}=Ksoe;C$=#ysqQi?|!BhٚYЍږuA|Id)|')v_ps+|R);(:y"9MOM_3q3b4csiw6K.!1_3E>D%/d:L?*-H4݌FGzaFuN3 BE74f[R[p|P^(s3Ġ-f,S+5ӦV fMywP.XVQ|֪JEw^i?%Ŝ1-닚IÔs/jM:@f͂-.jEӞӊ6rN7tFf-ʍ^&hoϨP5 ލHVɬI_#8MqTJ&f|]lr,{?IJ)(G9M,W?%J%N4R"Gly`vP/iڟdx~ K xfG9.rFR4 DGɧM.:8S42 R+'v_#O4 !? {iGﴕ{z;% *NP|; lkguvxvBʣ*NB鐛jm^j/j^&IMIlg}3FY/mvn.j,nZu8KJZ_07Pb)Vxy;l^TO=E^6ًln3kU m۵%2T?upL+_j{s(|^ӺU<'";$bISuKժJڽ4|wW/y;o@3_wͽOTj7>$KvX m `ݷ7`P(͒h[ڋǩVAZЗxjM\kR$~#R/:>u m8>yg(s-B=m6gu?[:~?c\g?yߔH>Q$m"CFm`7];:6[v[9Mߧב7W9`|7s0f嶅G>0)W}=t.[o^P% E%[_ӿ3l{E=Ɵ7ǿP|,B2,s7j\b/tc^6$ըT>lw:)yoyp$d˛ݠEbhF]Y6bײi34D 9K ڦzk !(zȡRgO=޸-Cr5b @$Fg3dYH"$_eq>/];s>htڮ^{`v0s,(q%kyi:_ZRyŅR|:Oji,{?AikwiwOLDvHJ19׋ݣ'D7o F &&4Y]u{gUӴnsWζ3pLi[_[چe^hO}7K/S_a34V딡vԚ:|*,r49mݎTD<]{}G|effoRC t=969I3imVUsu*ˇl֢tJQ{OcJ&eYܞ%t(;5;˷@+_Fisr__PXux7b-&gxbzދ/vzx]t{Ed`wĢ j%þ'kMecGT./S\],"i][Lz/?o0O"F?LJ"Oomou֚-e6JrhHqNKbo4IsϥHd@Dw)%7yHS3D&=G5tUsMˬ;TRsD_M=mpɽ3G3QO6koe1UT,-TچaTBìǗvBr4?X\ZOSJ,\$U(ɽXz,T7heY&;ǼBp+~aLgci#~LDF~Lecx2K!jeC9 C2ɝV]kț\THq6wfD e5®NT"&)iP:?;+^[Y&Y&+27H<ރENVaQ3Yy (}>(y`.(dr;38-wnpBlp7:.?|?PNݿTkR$A QLO=#O\k:Hxn_ʼQk`D?~s  WGO뿭5Xc_`%)3Q4=IrX++D?|&tԽ5R›~ڰOOIbĈҭ0}r nkmS۵i)[׼)Q]/&7fS3^K<5{ۡte*Ezڌ(ȣȲx,e+WE\Yy 'juL^+^ʆy|owmQݰn]7*|~8 J_I881~{)7"=UU#݀?ao_xv0L ÂQdclFm7vĈŰk^h% [dv`b?CCwj-Mc\=tcD .{^G@/'/Dͼ#G$u?%Ju?##lW"4* m W[κ m}^1 9)=GU)<ouHF9},\z=LGrn["7Cq?'k~p.y$SF{-("ԝlQ:#ԱuQ:Tb#$M# _2h.})*BQ._O!Rsb^q(87~nܨxtE_"eMϒ[VJK[^/ĽYo/eÌ+*悯#XCs~ p?7]f mO5<Ηc VIsW̯ p' e0wei4+v_ֶjnYvSkyz;}dwTT?eyy47tfm;0 >KSQe95i62c!(OZ]I ]m Z̳]3F٩Y1ъh!->k\آByPW]&˗cE )(2'? n)p?x7/*T*W%%!lO-~{I60?̈5e9f3a5~9T@~|u7Vo"Xb?H!> &S||H|@IJYuH{KOiW9E4Fi?b(+C,.g"{gYs= [N2HޓΠ>IݏT7J^ LpC HxDWD׋0*dSumhP,ѫQ_r4!1:-(ney*x3ZBl8Ƈ:h[XםMhyEXS1yb}8Ȋr/}߫!y-YRR: D\n(M\KE}FFG ٍ؍;xHmm:51獵ǹ-P]D Y;࣏H?>?ou1I83exT? *t `GI]1Az?& G+<gkF#WaQ!cQά I+MO`ytbC۱E33E߲(;`Q>|1_'q;36HSWRe {\GWX>=S"+ =&aDեhzDR#Tw=^vt$>4J{̶e$}gp*Kk{9dxaOlYߛOZJ+@CqBV6[%M^;O䀀JN ¡pȩ\({3c{4nGg{3ox )¿Ui/|,_{:?J,|LKݞKO*wJu7&Rony!}%;A7ݕڥo/iVV/ީ~ -Mxt֛ԛK'%Jf$)x<Ԭ7VjxoW~Lm[dB$RR9]dMJL$jf ^Dη2.]ДK2M}t]Z^X]~tnA7yO!YP]?R'pM~fݩל;7#RR?9%YY"9N)$/Xg^>=-ZYYw$*7lZY%ڸH$CzX躕EƼ ڦ p94NxR5ջnwZ]q[])kNqߦSt*Nm|\-^{e]?t~Rns7VnZzyqOOqְR+Yh?K )S|_/gr?_z]n^k)Ab^+KF*m}jAcaaWeϼY%/DD#ծ>R{OĶԣyڶq:F{~|fv>l+rb8`O[vsՕލ:\">O?{hH^+&kYJNJL}Pn5Z՚DdO=+)o|5`?'.+Hh edӟ(˯qn xuެ|]e\%:\ _&B֝[ߪV[J^2%L8w|߬;#>UWҟK$|^L2˥sT>9˦s\>gEyqtzNJ"OegsSWffDr&3I|>̤ffS3J\/zsoR(Fol2Fk$k\+x[lyo"bI^giMAwқ״HRLRNȱX%)KNsjyhIZ 5PCK@_z/+ii"Q@W(U ŕֶrnj;=M<״7oanSoM-m+M+=i..- /_l(zMT׻G_׿<$3/:t???uz?ygg-,__xb!׿K@7O[b1tO"o*棁ngiV\ ZjwUV֫JU(5Uw!$+}]xW KneeiroqDwrokrKVh8]o s6Dl|ak6B ~6JdC6f)(h,RjlJ#M$NcnZKmsctpkk[ч}5V~MqKh\vU5b4ZxUuE&i\usS4a-zb=c$KݝXQܴ^?{j2t|t[d=$@H\q""W ?r߾x EƝ1C?D[ C}G˅`:PkPI4Lߣ7DN(6]rjT$QNi E֣ E_Qw(RL" m4z4Ϩ{j]P| hl)U# ZGqfbᓷהעQ^mxy_gEyH($r<%'"Q0&O%"<0 "Ѩ8?ω#<+*Q::ʗ%6~<gHoLMR%B"G9>9>>̲k?̙&N%㓓U6N3 Ns_?[c6[>~ulԙn#4۠޽ߋdcff1)]>g $Q_Z^wre{s&۫F<TModvH iƫ[ZcԪj8 y:$:}O\c8'w;Vlͪ8+VptYk4Բlr^2XhTt"ճOdžZӯgO{a~›vr[[ ѳF"@"PN rws}ظ-rzwDO&Tؕq4YU/7޿BFO`kl}1NvC+*z3]Sm3> !# ?Cߡ L4B >LfL\o?߬-3}ə<1_ Ih_IxYG`F$:-O^t)&*dUhO{y?{39s'93ppiQ~z?}ѯ"Ek㷼?rOƣ՗Fc'zBs?O^՟#yFn_2/ x؅r0(:[("Ͻk$}9u ^z}BC{~1zv=HV_3$zE2do~OwonQׇRZԵݺ@8?m:! C W'4=_Os3//z߿= F 7?vQ^KVn?J}/ܒ]K6>C!'r=辽~<u?-?;-LyD 3ʴ/>_.}_jPW;i 81c铮Z$= gFB1:#yrrzwGꦀ_0%Dǒ<ݕ~7Jߣoɢ~4}XA K[*h*whBV6(DQuO-TԢ0V-qTS׎wDnUZ*7RK0J+ZR̭UhD. <P&"i8hummTb9 7_)|k*Mh[[$EFuZ/MQD @1*ZjRʵXE%34UjFX~D90rb ~8wN'.2FqA(Y.PޒI_D4ZyLrV E'&L=<ezN|<]\7e 1F/F![2]gja+db#b,TeO+m,V@+Ж+S~)d &1-9͗]pjdY[{eFce26ʬɜv:5J֩1wbvbnyv}=/l5-^cMv-gb}*zR˹l9sZث(*b\MLK>moM9c]gZN\2BRĕLoiO2B:Rd>B*#DFI\k\l!\_S^)b.\]d|n}Mr?VRXR'\Vē鼒*| ~(3,ʤjA$̲'U^NTrkb1[/Nev`&11?XbSn$2|T>'W/T YO/~ق3;/ҌU&kIWb3,O:X*啫+.8dZw"cqjĹͳkk8z-~Tv%MS[(]K$keMW#×M?'RZf4=~!iO矍.wn?+X=_}?|w%sKz貾JĊ+Q2Yvra89oY TkWU@zX  3!~մr&  i'w^gvS XF{hNߙSRF}۪Ujjbuғ}V: mWPTq~_rۓ{|y?㈌&bUBg}wN2SɃC/zY.W. ^ğMOr?31WG0%s5NQz2^9=k,{$B/ܰz\awJá|ʓ|CY^[fE!1rXDwQ~[>UI=Ϗ{q4f!+GV->)/f3+r[*+Q٨vr* r;< rxM7Χ^lo\ 7=o'hҵ׊]+d+*ckӓXҽg0ZB)SOZu&o{ѪSU!ϳ"BjT{Y p&0x15389x{cIӃB;Rg:$oE/cl1_*3Ϟ&| O_'??~,?1J_-B(a)VNgA3gr뀄NۢXjvqGO!m|qS3 9zրZ7373N U [WO{(a!krZ#?z w};fzPSL!^X ӳBg/sѨQ4n"wLrfDV¦mU׷#47ù"azf 9CQ3{=ARQ@m0 pfaFl>bf1aEGmnxf82 la0" FE)O c"ŧm7 ػ0x8=kTم=}NxrÜ0 a.(tL^nXܐ|e˿C/+ܴr\RRVTUg- jΟF7Eh[0R&C=xo;u;3v?kD'5Ex#t8;rTnvz fu'Ҋo ml!! C(}?{Onm-GmQ=C>K5Ꮌ9#T}t0xtJ'ubխ:r)zf&f緵loK79b癩I'sǯGMzrP_`O,P+?r|/~8~zqXV}g3?xSvX9} /H9s,+vǾO~rªN TҊ!EV4VJBِ%wEk)߉̴S;OwT#J}zokE%009/7' g-u l;_QDU[k"(w~ײC<ҍ8{)5}.xXJU%=Z+zZzhe_uTn׽?䟈'(yB̴'A>c_ֳe#+wWf5S˯\2٬^.S7 e-WҖQ0#K=<=[-REԳ)exy/oe᧊,D[% N,ǣ2bdr5-G4bb Z,Otm*OS%"Ig!GcJ/= {BaqW_gq׎R߈Rv?oX'#$ejc#Mqľ!\3u{e9ey}P'ۭ-D"!K$b_;^uOjL59C3F_cqQy=b>7Zju\..ґ4gRbLK^J?:-{!*+!YP\rvI#rkF>C%}xیB(\TK–3ei{cWX;y{ËG z)ײƐP&5=gpK{qm/,BGkOucc}ʅf?p 7 ?)}c?oOa{Z5&ƙ28SLVq̈33ά8̉37μ8qYga8Ʃ8Β8q,쉆?;>fO2/)]i_S.4{?S.4{?S.4{ѧ\h4CO>B >Bgs>Bg[|ʅf2)=пڧ\hCrs >B ͮ6)=пާ\hCK셆>Bk ^dݧ\hv ru7}ʅf/6S.4{OzC>B {}ʅf/5S.4{]r ^aߧ\hvO쬡Crٍ}>B  ^iا\hvg3S.4\h*CAr٫ |ʅf1S.4O쵆}ʅf3)O6Cr _ hʧ\h.J0S.4JC7>B ;̧\hՆ>B1S.4{{r)}O '}ʅf`ѧ\hfCO>Bo4)}gr7O ;O[ g|ʅffէ\h>B^9dփJChxo}yi /{pTslޛQyD!<$HNg, 3(VqN3c/|GTWVԶJoݻ 6{{νws.y®|wGs67ĕImQ>J}kt,)+ BQ5YZDl}UW_P" CGөΠ:jYTΦ:\6Pm:yTϧBP]DucTP}TzSTzꯩ3T穾@TG0DT_z+T_?P#?Q}Tߤշ_;TRտS}{Tߧz꿨:Ϳ!Տ~LRT%U/%=t IQ^y,鬗ATS-ZJj9 T3VQAu$jgR=TGQMu ձTkCus:DRLu թTo-e{?#u9Z%_@Γͣrs/! ! w҇=>Ǥﰾ,y+;S}П^OL/SK8H8ks`U|hya_¼@I0wM7z{Ea@M!R}ɨsjTWXѡ*Rp-Vf^Kr K?FXNxɪ,p `iGUDMQú/oh^bgɫJ3dyS&;KY[VtIЀ2KjyyY'ʸw{jkX<NG6Dét$ʄW[ s[+xBs6V9xwrjFsCC;{>xCe/Y0{椚h2KTOd[4D{XX.+"\:д`NݴG Vel౽T:fJ+s >Q%UĮ74mKYitb@ _Yhy9j%l#l^2%[x_u3xUroXqI`SzijnuO_3٨'l$_1cUދk6)c!ʗl,ѻ̚v3|MW lcmCe̘2q{p_.^Z^N}Fۃ*S6X N|셭˛=wOlVsg2>լ Ι#]mYϫ^{2Ow6̕uݑtdC$ #Azl}!;Hbb9u֩gz廂%wP.kT,==Ed*rX!Y%D[ִ:F9p.'6"/lC,E~w|Co*(}=Ew X5 B*.ovqnIOinET`yT4qż:/ tL;Pd''D7g#"jDú=?]GnWN0:fe=:%/. Թ3ռxyK׼3촜3;FeSNցK[ۗ7&elN uMXݾy:LhSC|&cEe."jPxR!!pQzC!VɳuX.WlkC^̢&'C6SIW7dz`x|kN} sE? Gƹz[VS;|\u3g>u*+ϋms#d";N }|cwv_K?{aoIi_d0{_޼诟@7\|G߽om~ݰvוy~|oviTS {_CvyTE?J`aF JWh:Mn Mmm.$]y ?` .~ouar~Lb_y.aT^ b#v"@wOoYI_?Skܬrjg[ar4^c9-myXGK۽JPa\I#C] ڢͩUlh5 . AAhs($n "pj y??KaVҫrb1t\Kޟ<L;yU9SZsbja2eovB7 %>W@+-+;ߌ!dO +& wNjq|XY櫿#{!dO +}}XߎTeGCȺ@rJ/tA;e~^?Ϧ ^+{8|Ge6V4"NۭK^qa8Eӧ8XR,riP E"ZAf2\QLCVo?@_`O<9q>grw}-tEorlR9PI'C_3xyJ|r~I=lǸ(DsTCA79zF?2O@!r+W{͡o7s(Nm@!r+-BckGrV}(DsThwJΑԳ9ǥ@!r+6zC/9gm2cTj|"xOMQ{}3f\IxRlbdf )Q )& ,$Y)Qk+ 6׼g179v[^߳2?Q1sg_q`N4楽/9/bvgf- s0/yѽ4nٳ'3W׷4|̉&W4~wߕ2itkT>a`N4 o9=׋邨Xxk֮jEkw6΋hھ4Wk9=iruq%#W9{FTM:CEVWK7P/eٗ&}{_bж1M˜_$ b$VBXvFi4W0|OYSKY:} ]MA$˓P=qvTڣ'O;8|CCaiCZ6BWejK8ba 4#y%^@J wx-SϿ&%Suޙ5nD/4S:x1r_yl\𙷇^k!`Cb8$Urج Yb:u Jr(4iU!B P ZJU!VrC̾󮉘i#%;5 7!`XE ޣ9AUrWVz{ܙaӽoY1ۛ 7k(n V.3֕S}wJS0k^e\+\sO:wПRs?O-ҵ4抮}AigNsE>Ǡ{v+7 okk;]<iNsE׎NsE^k;];aNqMеv+ANsENi 4Wt 4Wt v+vʠ抮}A抮Ѡ抮=hпNsE2\ѵk4WtA:;]{ؠ抮ɠ抮=b?fkg 7i9Mv+Aako6抮}Af;]b4Wt-\ѵ/1bkӠ!v+\ѵ/3抮-iڗlko5抮}A#tm[4Wt\ѵwbk4抮}Av+UiWnk_c抮mmv+w ϴ\ѵ5抮}AYv+gi7ckh抮}ou]nk?7;]kk? 抮}Ayv+msok?wv+\ѵdпNsEmпNsEcx;pr.Mlܒ[6u0 yYvx_R ϔ۾ow#%?2Y4F T _'VghS!Vfcᑤ|g;ڦ4ѱfg/=mUO?%|@Y Hʳ̙9m$w˖f>b5 s õ?m Aɰ5'[wqWȻyeNqLrphkfɬ%kz._QTek^Ciԩ`bs2>Llf.p_ CPˋmX2ֳp~쩓flYOc\r)ore6 ̕,G3/ʌl+卧LU]B̐TSX2v/ruO_;"Ea)|(_Dn=%[܇ZBlL?T_xkoY[}/YxvT`Cr_UZ?ݮ'7+x?rY=JSEr!ӛ ֥+^au?yb3jJ?soSlYGqN[jf}a4"l+c|?z~zбsfN;JM&Nv/=mUS&o|)w)o| |YB|9d~nz}2IۙqO@"2JwY,[n{AL8W YUʘ{)G+S:o;;!Od5|p> Ü/'^+]d֡/]{ (ܙj=,֏]N"h6w`Hl2N %'e5[ɭUl.iHEj$84D6vȈ {}0v%IG2e1r7f[t{]"$A:c਼Frp%IV SYEh Ѭ[Cx&!ǍBܸn9 EVQ`wEei$$ =,Z β<SԱxV !Y{NB4|K%Q5$iа׋wyT4OK $FRùT&vռ[vQG Qix穣C5^lw GKU-Q!4Uf GVFnF'"=$š)S3uoܧHHl:EΦRo<""%1As%)Sƻz)4%wT@;'cZD$ L,uqg[EM$jx6Ql.Nw%BK9pdϋIƜ=y)QhJzY&+_J0_— ~F}.29_)3H@ 'YC%HHܿ23 R`MfS3tr].>̍l_p А` / .3ID5xsuv0.?ůbQLאiDn?M6*j# Q0) jQO2_KgS?AVFQ7$ф1?~WO5K]Br|sr$~Kܷܽ Sr:ݢ1@.\L:QuJ Eݻ$ F.?}ܓYd޼tF~#5'ό2D9c45E񕙏12+gD|]f4]x4ȟh ^ 'f:/wYUhD $beɔ t@Jwy 5IN'-[CRr~ONB4$߯=HLl$y~ʼnN)oH8Is&}jkm$0L/XbeE嵈?*y̜V4$FhT,~봉!XM$t?U(ɩ!9'9dFya= LuYDM֟ %Đwb5o@nP3 ^,w 9:W)Yr#JG=soU:Gn ggD _huӎ։tifmmkHl#;䟱p鎴pmǚ$d!8o" vS ԍ7aFٍAI7etz%s5Ƚ}^'uExիߟ"qv!l`vk 0'O5m|4NJ:Tf6mZj"%KZ-.f6-Т9hLjj4eS?\DsJlܵ -ro@`͙΍Av<9h.KfEaWg更kRGJ?p醯{z[:{[[Y-S J\u~ ÍbiJZT>+09[7┴؞r_f%~ Ú7(i+ݵoaVx8*ʊ=//E ^ʥ3cXu7uǵf]g|"o:ҨI79#En?x9B0Hp&2X Z 7JjSe[sE5[9/?;~a%Iw3o2}RJk__NxW)•>*~{DC)QnW3^`BddUHT;ci|%^0s?vY~z>ެ Dm"уk=826'eRfTDpܡU|PʌN|PN9.O`Buy|LtE|\Wte|BWuUMp+^Tl_UUZ;QqQ'oz]SUUWݺ6>TکO<ܣ*^*OM_uwb@P.Ps^+?h]Us}/F[(qAG;XJ88B)3>[zkx?{=co/>=pgs|m(NC&Ozzfץ6Y>m`WXxe>5B|Vl- `uZ폗Wj~ Xׯ C䆶IPO6x-WO+f#oƕcy+i(Xݷ ?Y_Z0Пt:w潷t˾]{ҝs?{#XO\[]:曛nxxu]W>;a=5ޞ 1!OώAUv]}:x정E CDalW@00o]Kv ĒE yIKeݝyv05ig]Wm^?}fz[E<0&_V?;C>y4tka k#' T7.Gg0ϞtQէ{gZ}O|↢X|;"-SxaT~]k-UW3'0pG6ks+Fߘxuډ};_۽}tāGShq[Ne82Lq[/Is(?;FW|iokw x }cݒV?}asb}h:ֵZw8C3~œNz]Ϲ>wxgb3ˎ͵o;}}fgO;2gdh!0qp tîw7_oU8LbQO//}3??o(ֿ=0w@w5<3FS{ N̍[\£cgO:(}oMheyJۀB%EZ=yP(*)miT(E\~{уK/ mmoETCיd7WDyev2Y睧=W;YHWVtBI?f/vfhf]KZ;OȲ}@[$˳gaei/;ډsͣc'T8}4αWyL! 6c8ci|^Mclf5zڬf>6roKf -܊eοf=a拞Y^?׫3L(c Y,[Z>ٜ++u'WJ/d^NVm=vC I2+kɰWj>oWH|e. ?3{ F+`$Uδ~,s(I/o.Hs` ]l?۽+Eayɽ"dEe"Yɪ,wcdM)Y$f]-eY 񌧜sy?swgaf:^3i|"ri|'׶yy:cL  K{հoY·ȳpdHx\|k ;wp>FxR ȥͦx ΧȳX:eHx\j ;|<#1j ȥ_h Ηȳ8dHx\꫽;ۃ|<L  ?+E#e,JןM11Ģdݲ[ JQbQf%I^ݒ3sN:_}gim+?W!,W\ )PH?=Q_o سT?IHyB:щ|=dQt (汨?ݙl 3]H+@&R+j7SQv3XIsL2\Ը~T س|\5>2\ژ:|=dW%DsD_mk@&R+JkQ_h Ϸ3]IwHyB.DdxǞPg9Ds~ݹUDQQqGE X(% FbLXY *y&7yjwߜ}gggtZxFggs36?gv|]S߭ܝn'?vlNڻo;K >iݗ9[tlbhl->b+`TZcSvlhhsW}10*Mz?lY|ۡ_;taQiڍwf[ggsC5`FaQi5sWX>o;)+;A޿|{' {jʛ^[SD}5MdߐkʛȾ)Ԕ7}KϚ&oMUSDD&oKׄ^ǔ$?쥉5MdߞWSD#jʛȾ3?e#kʛ^?jʛȾ;tMy+Ԕ72?{jʛȾ7?XSD}5Mdߟ?Ujʛ^?'Ԕ7`Ě&JTSDÉkʛ~$JMyُ&O)o"D&$O)o"D5Md??'Skʛ^VSDD5Md??$W@)/]tnp;_h?]߸Xh|"sdg:-VMB +7gBP߉2: \d|RMοor??趖o>+4>rJ饽+tsXV:F-@.2>W@)5ο_e6o\t3r:xN,+S)B +? ܽ+Eq񐇫)LF%d UA AQLʪA20AEw?r'3y8O:zm>ӌkI7JǍleP U1OΦۿlFP ٧y8mlTP 󑋃_ !J \{*Z8ZI:RQG/g/KBW@ [/I>Bԙ:[FP ۤ>]!JN`(D{T[+w{[s!Dݩc-5> ^=KVq_H|:7.Qm֦ডbkht ^C. Dܓ8xsr>z|znEN,FBٳrQP h˼92 cv\?h|Ԣh} cWl4>jQ_m`*-aaKFUF?^;_/_EG1}\9j4>jQ_md]/̏GDž3\46i7?l?H3}^L66>wKݻi0v@-+1KqI%`\" NuIfB6ԥ:(8 uӡOP')ֶxQrϳ$ս!Dq-[*h;wvCQ9(5> _UǤ?~] !Ưq+5> _U4~I:nѣ/M3RyPE/z.l(DT7ILQZ(DTQa~ !jJBW@-lοtBJ퓌(D?KUa%s4A%Z%p)pEM88bwi^B[[A !H(gGw!ĉ˜ QP4ߋӼ{BlrwDr=w'/cq0fްDY/~i߆;1;[*_HW@.lW!na̾-Z hwћ!^al{@%RD+ӫyb0ޖ kJ+6?gyg|GqP[;K׵@%R!Kq7ANʚAQi-ރV*(" E̥Yjlŋ ccr9wzANt|k|dbPDI- ˩1lW6rL }I:Oa%5F[ՙ\ j\οta55Oms{$[Y|ަR.kTem/>[UY8?:l'~|ޥW?dmqt+OFۿ.Cq9qj(% IfAH Da?IlF -:08IHz&zR HkۻAѼMQ?W =+EŹu,;ܫX(,2ȢWg@&R+V_οZ 3KwHyB::TxŞd{k|d" i1uնrxc`#ak|d" ipmh!/Wy{\ )PHmǍny χ3xNHyB n}GQo&<cR\k|d" `o'/), $rLr\tp:;:M<+õt_EsRc%hEsRbg6>l} \dEYh|"=+Eˢ`<.&EEydPdSvL &ѳBn8g<)t> ?7Q?^= س0Ywg{d# \y:UxŞAJ)Dsqw7T> fg#@&R+&wοagޝHyBڝnEhx>ƞg,DskfO-L|S<_'rLǞdH (ƽo?iY ϗس4Hy/۽+Ea1+%.R620$bIY JQw2YMY0PF\/MIx9'~o}x=[fƂ!,LK@*+ /O~!8#A{i|"riim'?/),\ʙ@*+ ~zc;8kg,fi|"r鰳v7#y@*]+ {G|w58_"b\4>R\}7v|Ğ/\ )PH ջ߼m3h:JwHyBj[=:湑|=dW9Ds^V9 '@&R+vjQ1u/gۑHyBZ.E~%2\۲6uu?{/=\ )PH'Quog5>2\_M˔eҜUK \ $>JM2KK pm[h< %hSR-VEIqlko>.t|Lń+`&}`ә-7wzck?0_:>b0~th\S:>b0|h_w{ +S1 I'ϼhpCrxGZ(S1 I;n4]p;/S1 I{|hWvs?t|Lń+`&Ϳ֗l?.W"idkCfohPS25!76RS2{SCُ7ZS2{sCo5!4^S2{kC5!5_)&sMyo)rCٯǟMUiRIJdK,;0Z .\J pҴ,˛e B\ -q:ZlNҾ z_.{.[͙0p=ќ"ߩ)/"{E"T?S^D^EW/)/"e9Ed"Csʋ>\dNyV)/"9Ed\ѿ*#sʋMNyٟV)/"hES9Ed:}]Ny'?>U1gsʋly)/")/"1W?S^DWcsbsʋzM_)/"\E9Ed[RNy+_)/"BE+9?}Orssܩywﮯ;S_H!?WD꿶yՋVrހ7SsL/]7۝|Đ+`"Vjv.{[?;u!?WDm˱mmu.{{ `$\ƭ;=~tssaÀ7SsLMov9ӹi9RsL,jwz9۹|̓ݝ|Đ+?+Eqs;[dbJe]%E1P0U<;L2sxƓ9^ۯ3t~Ϣpz6cpb:Y9k|d" y*wֶ[ϯ1\K7rL2\4uO APf5Y3k|d" b F@el)a5>2^E4{Xۍ: (Ŏj-Y{k|d"~uAPj+z ~۱Kq74'.N.$F f: . nBSPKࢋPNBM84 r㍇ry?yl9"=,rdP hx1"uVEgy?rb."jF@-+ w'3Qa>56[;70fᅯ&ZTW@t=;:"lwdh|Ԣy6""ϷrgEKy?{14"=.}?ZP n!Kq u0swSGĤ"AL6fSAx,4Ȓ0QA"a5=`p8'/rw}>~^_!̌Y?4>JQ_u4uqv!D3ctoU(:j~vǾ ueM?94>JQ_utvk{#!D2c|qԬ4>JQ_ut0=+wC!Dݙ1~W(:j9GOO!D3c2|oU(:M;B̘={+RW@}_J;Ͷ7!DIfL~5+RW/ ۡJ%abG. bSA, 6f܄aEQAM˲ "ĉ23JQ_u}5v'=w!vf[wv+RW/K]q LEP?f2再Yd"cE&q`+1nU dd T'xJQY5e<{Kb'36fwrו@)+~>I;W?Bx1_W(:ڟ{+>2cc _?Yi|1Ja?.c j]n"z-bJARDO SI"Kjba&`aT!p;a)03y/iJQ_uxiO3cs*RW@=YL;'C̘+RW@=H;BfL~~+RW@}{yvgBqzWۯ4>JQ_O]U陙>X+I:&@Rˀ$@!M 41g捵 %O. ucXѐFcP\aCRc+c4;1Ebo9s}{Oy{)lmq澿^_s#q^G}T}hݔb_mq郩E"+~_c;.W]fV_e>TNZX|?X4Pb|x4`ġ8zGTnԃeyG8/l\;ΞbeO/ĥ:٪/nJq؞\]5uR'~_z?N)]wu=?Zx_U}򇯥w׭G9pMb>8h?`pJ@ ֶ<_ev~V^NtN{ᩅ'/>2͸7G1>șj3\ԃr."sݱ/hx98>FѾ̗b#.ĥ<1pЌE]5[\__+SI)2[_q.HcWm{pݺkx\{sbtbYlTIY=[F3VneWޫ;WwKwPo6VZlVN/bwUmrSΗcV*o^i_v0Nwge-n/;ume_heۻǝyvl΄"a *S\576G%D^hvD/2HHSb "6"Hh<={( }{ioW>Zs'WFnrZ;oOk&=g.woQ{Ek@f@ E T.f(^1O`.{kxn?mմ7ɑ?/v{Ž<O*0[VكR]^Z+ -;ՉўkP6iQ/nk0WdVPdLh*)Gt;eBNFw GdF/srBVgnΙy?!+P46ܛ*<LJxlN1[wJ=wM{d e 2d eHZ"`RNY"ਜ6Df)lmsr Ny/7d;J[!y }~['UZa3@HF~KBu"ړvρJъc+MQƭ?69l &?,g?6qh5+MQ65%rOuj[njթ6Vw\um:[5aNmStԨ:#wCꌎa :q:t;LuWwv5Nwء^;#KK{_ʙs?W{ʡ_oO{K'A~j/uߚza_M?.`8tpy+sg;0vz+ &\ۭ>F[?/_ZƈUq߸mrG&ԪJ]/ěModtC2besL]kYrwDN&d&edsԍΧN\wۼex d׮J[Ax&` 7Ϙw퍼p5Z-*EG ZAL؝G09w]9ج=)紩ΜƄ)lTs!WcR/bFշ~e'D@٨4igPڱIv(`Hrj "p4>PB-E] X%3\$!B -!UcS|C! *T0C>QU>% m- zJUn>_ƃtޗB^G30.*() I~ \\TWIynbP־&!3UڇLu.֞&?W,xD1qbgIsXAcâ ~`XiaGy #O4WӲP!qowosoݽxsQYNoV4[m Ap<]O3彙cefG_z'ԺU q>VwFr֖MIyO~8%#[VQ'a)$,2 Ni Km6Yu깹پ2j:lORݲE̓ݨ5>9zq%/ʇR&M0Vk7|.( U>ϯ0uW. 3=Gc<_ڣ \,@S4%LJP>$MFT+Q:<)Dvq?vVi~/!cqeIP$d" Y.* Z & pPgcIȉIȋs\R v%(f.BIt]0!I}$$DSQO msI. yh1Yֵ2ydZ % Dz,{ede&2ydݫ!Bֵ`du?ZXw"?O9?= iO`4@'??O"s496έ[n\ټ^z歭G3; c9?؎xDZu_^kvTyҩ/IZZoMW;뫮dKS~֐˲8Ӹ@@apdsIH KW%Av7wgᾳp-;ݭ;՝{ ;5=s>0 ש;Mikmׇ_p`?fzCv`$GgNtS_>=CDmULǩלV\h6m"x¨eWk YNOK5M\{C!a)S!乐]~KH!7VY.pX,8^"'$i/`4@p/'_Brlǽ[ǖ =7m< ׹<S 2b[ 0`;nz_qz/1w5}R\wnڲ4)Em%;Ô.gXv9ô.gN9qUɔ%Mm[C9ʊ' Qk,;ض'XZ2+M\GtF ^*ңBئrknuӡ6Z4!Z/.E֠ X6I*H^49=˧rҸTIOˇf8ڟ*AsTT w#eU͟4| պH/nZֿe$tEߍDo0NGG$AŠ$hBX=Dž IȊ\\ %!_Ӓ0.I(=%(>+ 3\$'IW$aKBB㛒·e$!]]B!HBq/?P\gj"F/e!T+I`JŃIk ,X.VV ֵD#և6I ]b,%/8WkzYf]3;]f:o;;~s7^ݹ_qs?Ox(? <Ko_jaSkZ':jPkQ3&Ӧ$%W,)>g"9=-}SM^|f%!%JAbg GLGL)ĝ3qzv<^޽skɳkz,_Ajt_p;2 orl_qW)ϷƄAZxVb6DUɋ*תd2h/SQ?#鰆u*_0؎/S#t%^Nݏ+Tj?ܧd()}Jw>)]O'+IVC>jê}>Y8ggRj#}NRsgo}&{ULuULG}*jê}=}&DXW(Ku}XJu=ԣMZwEӻ6.Qr\Gf#`b$,D#`QsPe0 %Q+P<. 7%aRے<$!)*x& )qC씇P?P!wP!w'b[.τ3 < ǒp 28#s& oF'$Kk(M34n*Sk;_8{(Tth&yvR5|du4*JZ >سf(kNhC;Kf8%mnRӴWy-2-sBl6&&ohquRW=2?}Kb<2?AJG%i\AQh=Ag/!lDЏqw#Rf2 $usɢɢS?<5s,:m,:gLLgw׶v7vvnsol_{Kq" s Q׳'0_yz[jJ ;3/G'z6~o%=Է`sfvӡ@ҎnWLʐG1aRM~0iI:Ӭ5f<ߙՊ^ TIH"{^9Z:?/!ʖ$h"Q$x^;-MM ˉ\Ϫ}OL69m0& ԢĺGU^pZX6w=k՝;{oOq/O$kaffX&)eCm/`8=}GәxmcY=3m&T;[wf|=]PNdn43K͇HhA~P $PDE P)B!DH<ھϽמLv&v9{}mQt!~SŢ\.rueg6MӨMӢv J4r >F]M(]mj~U`#8]Wۛ[vxs{{c:777Vf-̟u$_{_68(-7^y+A_r~#B|w^qTk_V:W+uS&)W_IZ\I]l=F%׷Bn/{ ?AAAB\)q)A))D;_;\u{s㐅fΟ_s_ UCBZʟFP~C~.4a?K94(9e_1Jfeaʏqj]ßzV,JBt?u-Xud/jXS2ʗ6CO]!>S=@s37< '?0 ?qpG EͅO\Pt0G}y>uO@ݧTSՖQQFnNrv2&j'9U;}smeQGͥնa9m:{ܪ 8;*RA Ŏʺ{,v4AiJݪg83bI\;I3c'Q.񶸬DI8z_g:[_2Deqr|Gt𽐻>d!E/"?A7EPmLA$e%v{Dy`%8Na)|X;w)R٣SG 7JL17]o(Dŝ"{+ŮW^xsϝM+Is{c) NݬW:k>ig:\n6k%.yǨGsʥ9/zX4WLs~4WLiѯŴ~_1+~tگ;i?+b:b)IEcyDimUZ~TVVrNV٬SY2m"@N_埧7 )Oy&<vjw3O]޹lƭOu?[=(fM??` /gw`D+W3DfϢ4EW'ΫfIJX͵Vՠe1>SbPFY[u\scPٿqrq'Yؾʵ<'OEcnea;2^]oYU)2Ԩ,[c%>Y7,N7H9ML\K" R\a%i˧Dat~)L9嘫ܥ_eXA>SK?rBz~$q #+vw[*MOߤ) n= vg3TdGSwݐJJג!_Es )q(QQ/fTQE.e_'dXveP8`wCH{gYGA`di7oN^_`o[O;0 "az \LXE{KU(e\6)GZ}ٰڔi(Pj^KʀEIXaeWVlki::m.ʱò*ˆ3%E-LP[2Iie/bS Xf4xe+hjHy V4Xe8E''KvMOOP[drJ LA/3=](Koly@)ux$e@s6d(i,󔌻/- A,[ q ^WEI)"H+"ȔtlƸ? o忈 7 ƹ\8)"(sPiLnAIXK0gEmW9[%n{n۵&wX%o[)_%[!ԭ :*蠊jC*z*+n~OoU>#oo--a#/0A , /_\퍝Owvonlm9SG}m}N=v2;6x-4o´DBE,:,؛٦lϢw|CZ$+=;͖ܗ(RիՆsiզYS+j}hԻw [;kɡ퟽/{gg~~nb/ ?8! pǏw`pLP=Z/Ca"vAE؅զ8gJ^Iҩ6Mu %tf;vE.+ez)id/n.5;VŴ\%?AOKNU?^YZ7>xjfZ8O*F@gmv*k r5}=ۊ4fŬY*^KN9-敫\k*'p&zͼ?}Xj]ow9INe_2ʤv]KYiW+V$zWUݬ\i[^kNf^2M,[M RQ=Ҹdwu1*D4& JÁEϧRJu\U%v:R{"Hs2e|_YqrE~ #tPA MA&8${DtPAa\A:D3 "H9&Ԅ>-tT(>i<#~EwXA*{S[zS{pS{ö)rj-NrSnnjM Дn5w_Rޔ; ގԠ{roDR[:g;Ol+K&Fi.;U4ְ,bn2EC4$ڸlI$`\ n^=bh 0\'TWH{va|O5AM?}k 6& &g/uәkC/82)@a۩r DQ &IT>!A9U8j MlG3grfK_0__w_@k뉽{tQ^m!`h߻\K(c,RekKr;ӻv9~Yj]MkM TT]zwa;혡ϝ0y]݌hgX6rF$z_G5 t+=s\w7 ՝[vap϶:Ht_4+cAo nbA~+8 b5>DV "+Cdi},!\) !eW)w.M-;Z,*pldQE-\q;W["6r }'NT8veꤊ7ek빫Z946g3pҿI_d1 FFkFx:bR*Oc?'5]} ,={1sJW-U@߆t'>2:oC>[ &]ی!77<23kڵoz \bu,>< וQ 5_{l(]o\l{G Z(K]ʽ/_WpޗԼ&JKK^ui.z˥k^0n_Kkgvhc]K+_q91>MA=x]R] te}rN"}us)}aG5QJr|U55WrƹdתGisa{mp=\N7t$w}rco\cdXXrSpI/PWe@@9?19kN iHD:]QrGB]/SM*:^W wzҥ~Đ?Z*X`A"-_Z\0Sb>M솣P6'54IkdbH|d W3Q:T:T PUȞ @/۸(n,oO]}_|1[1W1_1/P1/Xܿp}jY%,kGVơk5kMVw>_yVn]j,̹gٰvPJӰuPаtP: 9(hX\gK)Oe*hDC"Sk\r>tC/55?7ufaMPQ.YtPwhiIuh? Ι6jpKuy8å;1i6;7 򪎨gUQʫ:WuD=OCo޼Y=W^H{tJx3^)-5`*rNQ5K)6(Us%y^^J:y !cm!ÞF\2p*^8#< ++B(rqF\2,B w^WވWގWn +|es? |e+,|,p>Cx!dS9,|.TB !SQ&2 #BE!X,<.#La,4Pc,BR|~!ª2,l!+BȱpC}|~kB(BޖC9CrY@! !BrYD! wBL%P{քs!BaB(0! Oa|JB=?`YX{ a?\p;Î|#7g ۃީAG ݕz<\؝,tnS]&G N >x Oz7պ>7v׻}7=Eߎ7n~Gߢ۝ύ̍.?6oȍnVH7vw# Utm6(m\[;=WGǑ-܍BR-O9y}}}‹BHr#!?,)CZߑCȉ!drYB*ccc)!FctR!B]mOHE#,g"`_ n v @/E+_[}.nI,GE ܒ,X!raVZ߹xmJ W>\:9ۍ;lիݶUߡL:rhtAl}|/Hpercona-xtradb-cluster-galera/scripts/mysql/rpm.sh0000755000000000000000000001526112247075736022650 0ustar rootroot00000000000000#!/bin/bash -e if test -z "$MYSQL_SRC" then echo "MYSQL_SRC variable pointing at MySQL/wsrep sources is not set. Can't continue." exit -1 fi usage() { echo -e "Usage: $0 [patch file] [spec file]" } # Parse command line if test $# -lt 1 then usage exit 1 fi set -e # Absolute path of this script folder SCRIPT_ROOT=$(cd $(dirname $0); pwd -P) THIS_DIR=$(pwd -P) set -x MYSQL_DIST_TARBALL=$(cd $(dirname "$1"); pwd -P)/$(basename "$1") ###################################### ## ## ## Prepare patch ## ## ## ###################################### # Source paths are either absolute or relative to script, get absolute MYSQL_SRC=$(cd $MYSQL_SRC; pwd -P; cd $THIS_DIR) pushd $MYSQL_SRC export WSREP_REV=$(bzr revno) export WSPATCH_REVNO=$WSREP_REV if [ -r "VERSION" ] then . "VERSION" WSREP_API=$(grep WSREP_INTERFACE_VERSION wsrep/wsrep_api.h | cut -d '"' -f 2) WSREP_PATCH=$(grep SET\(WSREP_PATCH_VERSION cmake/wsrep.cmake | cut -d '"' -f 2) export MYSQL_VER=$MYSQL_VERSION_MAJOR.$MYSQL_VERSION_MINOR.$MYSQL_VERSION_PATCH else MYSQL_VERSION_MINOR=1 WSREP_API=$(grep WSREP_API= config/ac-macros/wsrep.m4 | cut -d '=' -f 2) WSREP_PATCH=$(grep WSREP_PATCH= config/ac-macros/wsrep.m4 | cut -d '=' -f 2) export MYSQL_VER=`grep AC_INIT configure.in | awk -F '[' '{ print $3 }'| awk -F ']' '{ print $1 }'` fi if test -z "$MYSQL_VER" then echo "Could not determine mysql version." exit -1 fi MYSQL_VERSION_EXTRA="_wsrep_$WSREP_API.$WSREP_PATCH" MYSQL_VERSION_FINAL=${MYSQL_VER}${MYSQL_VERSION_EXTRA} popd #MYSQL_SRC RPM_BUILD_ROOT=$(pwd)/redhat rm -rf $RPM_BUILD_ROOT mkdir -p $RPM_BUILD_ROOT pushd $RPM_BUILD_ROOT mkdir -p BUILD RPMS SOURCES SPECS SRPMS pushd RPMS mkdir -p athlon i386 i486 i586 i686 noarch x86_64 popd; popd ###################################### ## ## ## Prepare patched source ## ## ## ###################################### #FIXME: fix spec file to make rpmbuild do it MYSQL_DIST=$(tar -tzf $MYSQL_DIST_TARBALL | head -n1 | sed 's/\/$//') rm -rf $MYSQL_DIST; tar -xzf $MYSQL_DIST_TARBALL # rename according to MYSQL_VERSION_FINAL test "$MYSQL_DIST" != "mysql-$MYSQL_VERSION_FINAL" && \ rm -rf "mysql-$MYSQL_VERSION_FINAL" && \ mv "$MYSQL_DIST" "mysql-$MYSQL_VERSION_FINAL" && \ MYSQL_DIST="mysql-$MYSQL_VERSION_FINAL" pushd $MYSQL_DIST if test -r "$2" # check if patch name was supplied then # patch as a file WSREP_PATCH=$(cd $(dirname "$2"); pwd -P)/$(basename "$2") else # generate patch for this particular MySQL version from LP WSREP_PATCH=$($SCRIPT_ROOT/get_patch.sh mysql-$MYSQL_VER $MYSQL_SRC) fi # patch freaks out on .bzrignore which doesn't exist in source dist and # returns error code patch -p1 -f < $WSREP_PATCH || : # need to fix permissions on 5.1 [ $MYSQL_VERSION_MINOR -eq 1 ] && chmod a+x ./BUILD/*wsrep # update configure script for 5.1 test $MYSQL_VERSION_MINOR -le 5 && ./BUILD/autorun.sh time tar -C .. -czf $RPM_BUILD_ROOT/SOURCES/"$MYSQL_DIST.tar.gz" \ "$MYSQL_DIST" ###################################### ## ## ## Create spec file ## ## ## ###################################### export MAKE="make -j $(cat /proc/cpuinfo | grep -c ^processor)" [ "$MYSQL_VERSION_MAJOR$MYSQL_VERSION_MINOR" -ge "56" ] \ && MEMCACHED_OPT="-DWITH_LIBEVENT=system -DWITH_INNODB_MEMCACHED=ON" \ || MEMCACHED_OPT="" export MEMCACHED_OPT # for RPM build if [ $MYSQL_VERSION_MINOR -eq 1 ] then ./configure --with-wsrep > /dev/null pushd support-files && rm -rf *.spec && make > /dev/null && popd else cmake \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DBUILD_CONFIG=mysql_release \ -DWITH_WSREP=1 \ -DWITH_EXTRA_CHARSETS=all \ -DWITH_SSL=system \ -DWITH_ZLIB=system \ $MEMCACHED_OPT $MYSQL_SRC \ && make -S fi ###################################### ## ## ## Build binary tar.gz ## ## ## ###################################### [ $MYSQL_VERSION_MINOR -eq 1 ] && make bin-dist || make package # Fix the name of the binary package to contain wsrep suffix OLD_BIN_NAME=$(ls mysql-$MYSQL_VER-linux-*.tar.gz | sed s/\.tar\.gz//) NEW_BIN_NAME=$(echo $OLD_BIN_NAME | sed s/-linux/$MYSQL_VERSION_EXTRA-linux/) echo "Repacking $OLD_BIN_NAME -> $NEW_BIN_NAME" tar -xzf $OLD_BIN_NAME.tar.gz && rm $OLD_BIN_NAME.tar.gz mv $OLD_BIN_NAME $NEW_BIN_NAME tar -czf $NEW_BIN_NAME.tar.gz $NEW_BIN_NAME && rm -rf $NEW_BIN_NAME popd # MYSQL_DIST WSREP_SPEC=${WSREP_SPEC:-"$MYSQL_DIST/support-files/mysql.spec"} mv $WSREP_SPEC $RPM_BUILD_ROOT/SPECS/$MYSQL_DIST.spec WSREP_SPEC=$RPM_BUILD_ROOT/SPECS/$MYSQL_DIST.spec mv $WSREP_PATCH ./$MYSQL_DIST.patch mv $MYSQL_DIST/$MYSQL_DIST-linux-*.tar.gz ./ #cleaning intermedieate sources: rm -rf $MYSQL_DIST ###################################### ## ## ## Build RPM ## ## ## ###################################### if [ $MYSQL_VERSION_MINOR == 1 ] then # cflags vars might be obsolete with 5.5 wsrep_cflags="-DWSREP_PROC_INFO -DMYSQL_MAX_VARIABLE_VALUE_LEN=2048" fast_cflags="-O3 -fno-omit-frame-pointer" uname -m | grep -q i686 && \ cpu_cflags="-mtune=i686" || cpu_cflags="-mtune=core2" RPM_OPT_FLAGS="$fast_cflags $cpu_cflags $wsrep_cflags" fi RPMBUILD() { if [ $MYSQL_VERSION_MINOR -lt 5 ] then WSREP_RPM_OPTIONS=(--with wsrep --with yassl \ --define "optflags $RPM_OPT_FLAGS") else WSREP_RPM_OPTIONS=(--define='with_wsrep 1' \ --define='distro_specific 1' \ --define='runselftest 0' \ --define='with_ssl system' \ --define='mysql_packager Codership Oy ') fi $(which rpmbuild) --rmsource --define "_topdir $RPM_BUILD_ROOT" \ "${WSREP_RPM_OPTIONS[@]}" -ba $WSREP_SPEC } pushd "$RPM_BUILD_ROOT" if [ "$(whoami)" == "root" ] then chown -R mysql $RPM_BUILD_ROOT su mysql -c RPMBUILD else RPMBUILD fi popd ###################################### ## ## ## Copy required files here ## ## ## ###################################### mv $WSREP_SPEC ./ uname -m | grep -q i686 && ARCH=i386 || ARCH=x86_64 mv $RPM_BUILD_ROOT/RPMS/$ARCH/MySQL-server-*.rpm ./ # remove the patch file if is was automatically generated if test ! -r "$2"; then rm -rf $WSREP_PATCH; fi exit 0 percona-xtradb-cluster-galera/scripts/mysql/rpm_wc.sh0000755000000000000000000000701512247075736023337 0ustar rootroot00000000000000#!/bin/bash # This script tries to build RPMs from MySQL/wsrep working copy # probably will never work due to lack of essential files (manpages, etc.) if test -z "$MYSQL_SRC" then echo "MYSQL_SRC variable pointing at MySQL/wsrep sources is not set. Can't continue." exit -1 fi usage() { echo -e "Usage: build.sh [OPTIONS] \n" \ "Options: \n" \ " -r|--release configure build with debug disabled (implies -c)\n"\ " -d|--debug configure build with debug enabled (implies -c)\n"\ " --no-strip prevent stripping of release binaries\n"\ "\n -s and -b options affect only Galera build.\n" } # Parse command line while test $# -gt 0 do case $1 in -r|--release) RELEASE=yes # Compile without debug ;; -d|--debug) DEBUG=yes # Compile with debug NO_STRIP=yes # Don't strip the binaries ;; --no-strip) NO_STRIP=yes # Don't strip the binaries ;; --help) usage exit 0 ;; *) usage exit 1 ;; esac shift done set -x set -e # Absolute path of this script folder BUILD_ROOT=$(cd $(dirname $0); pwd -P) #GALERA_SRC=${GALERA_SRC:-$BUILD_ROOT/../../} # Source paths are either absolute or relative to script, get absolute MYSQL_SRC=$(cd $MYSQL_SRC; pwd -P; cd $BUILD_ROOT) ###################################### ## ## ## Build MySQL ## ## ## ###################################### # Obtain MySQL version and revision of Galera patch pushd $MYSQL_SRC # make dist if test -f Makefile then time make maintainer-clean > /dev/null # make distclean fi #if ! test -f configure #then time BUILD/autorun.sh #fi WSREP_REV=$(bzr revno); export WSREP_REV time ./configure --with-wsrep > /dev/null # MySQL has a mindblowing make system that requires extra/comp_err to be built # for 'make dist'. comp_err requires prebuilt mysys and dbug but they are not # built automatically by make dist. pushd include; make > /dev/null; popd pushd strings; make > /dev/null; popd pushd mysys; make > /dev/null; popd pushd dbug; make > /dev/null; popd pushd support-files; rm -rf *.spec; make > /dev/null; popd pushd libmysql; make link_sources > /dev/null; popd pushd libmysql_r; make link_sources > /dev/null; popd pushd libmysqld; make link_sources > /dev/null; popd pushd client; make link_sources > /dev/null; popd time make dist > /dev/null MYSQL_VER=$(grep 'MYSQL_NO_DASH_VERSION' $MYSQL_SRC/Makefile | cut -d ' ' -f 3) #if test -d /usr/src/redhat #then #export RPM_BUILD_ROOT=/usr/src/redhat #else RPM_BUILD_ROOT=/tmp/redhat #fi mkdir -p $RPM_BUILD_ROOT pushd $RPM_BUILD_ROOT mkdir -p BUILD RPMS SOURCES SPECS SRPMS pushd RPMS mkdir -p athlon i386 i486 i586 i686 noarch popd; popd mv mysql-$MYSQL_VER.tar.gz $RPM_BUILD_ROOT/SOURCES/ MYSQL_SPEC=$MYSQL_SRC/support-files/mysql-$MYSQL_VER.spec mv $MYSQL_SPEC $RPM_BUILD_ROOT/SPECS MYSQL_SPEC=$RPM_BUILD_ROOT/SPECS/mysql-$MYSQL_VER.spec i686_cflags="-march=i686 -mtune=i686" amd64_cflags="-m64 -mtune=opteron" fast_cflags="-O3 -fno-omit-frame-pointer" uname -m | grep -q i686 && \ export RPM_OPT_FLAGS="$i686_cflags $fast_cflags" || \ export RPM_OPT_FLAGS="$amd64_cflags $fast_cflags" RPMBUILD="rpmbuild --clean --rmsource \ --define \"_topdir $RPM_BUILD_ROOT\" \ --define \"optflags $RPM_OPT_FLAGS\" \ --with wsrep -ba $MYSQL_SPEC \ 2>&1 > $RPM_BUILD_ROOT/rpmbuild.log" chown -R mysql $RPM_BUILD_ROOT su mysql -c "$RPMBUILD" exit 0 percona-xtradb-cluster-galera/scripts/mysql/centos/init.centos0000755000000000000000000001030712247075736025165 0ustar rootroot00000000000000#!/bin/bash # # mysqld This shell script takes care of starting and stopping # the MySQL subsystem (mysqld). # # chkconfig: - 64 36 # description: MySQL database server. # processname: mysqld # config: /etc/my.cnf # pidfile: /var/run/mysqld/mysqld.pid # Source function library. . /etc/rc.d/init.d/functions # Source networking configuration. . /etc/sysconfig/network prog="MySQL" # extract value of a MySQL option from config files # Usage: get_mysql_option SECTION VARNAME DEFAULT # result is returned in $result # We use my_print_defaults which prints all options from multiple files, # with the more specific ones later; hence take the last match. get_mysql_option(){ result=`/usr/bin/my_print_defaults "$1" | sed -n "s/^--$2=//p" | tail -n 1` if [ -z "$result" ]; then # not found, use default result="$3" fi } get_mysql_option mysqld datadir "/var/lib/mysql" datadir="$result" get_mysql_option mysqld socket "$datadir/mysql.sock" socketfile="$result" get_mysql_option mysqld_safe log-error "/var/log/mysqld.log" errlogfile="$result" get_mysql_option mysqld_safe pid-file "/var/run/mysqld/mysqld.pid" mypidfile="$result" start(){ touch "$errlogfile" chown mysql:mysql "$errlogfile" chmod 0640 "$errlogfile" [ -x /sbin/restorecon ] && /sbin/restorecon "$errlogfile" if [ ! -d "$datadir/mysql" ] ; then action $"Initializing MySQL database: " /usr/bin/mysql_install_db --wsrep-on=0 --datadir="$datadir" --user=mysql ret=$? chown -R mysql:mysql "$datadir" if [ $ret -ne 0 ] ; then return $ret fi fi chown mysql:mysql "$datadir" chmod 0755 "$datadir" # Pass all the options determined above, to ensure consistent behavior. # In many cases mysqld_safe would arrive at the same conclusions anyway # but we need to be sure. /usr/bin/mysqld_safe --datadir="$datadir" --socket="$socketfile" \ --log-error="$errlogfile" --pid-file="$mypidfile" \ --user=mysql >/dev/null 2>&1 & ret=$? # Spin for a maximum of N seconds waiting for the server to come up. # Rather than assuming we know a valid username, accept an "access # denied" response as meaning the server is functioning. if [ $ret -eq 0 ]; then STARTTIMEOUT=30 while [ $STARTTIMEOUT -gt 0 ]; do RESPONSE=`/usr/bin/mysqladmin --socket="$socketfile" --user=UNKNOWN_MYSQL_USER ping 2>&1` && break echo "$RESPONSE" | grep -q "Access denied for user" && break sleep 1 let STARTTIMEOUT=${STARTTIMEOUT}-1 done if [ $STARTTIMEOUT -eq 0 ]; then echo "Timeout error occurred trying to start MySQL Daemon." action $"Starting $prog: " /bin/false ret=1 else action $"Starting $prog: " /bin/true fi else action $"Starting $prog: " /bin/false fi [ $ret -eq 0 ] && touch /var/lock/subsys/mysqld return $ret } stop(){ MYSQLPID=`cat "$mypidfile" 2>/dev/null ` if [ -n "$MYSQLPID" ]; then /bin/kill "$MYSQLPID" >/dev/null 2>&1 ret=$? if [ $ret -eq 0 ]; then STOPTIMEOUT=60 while [ $STOPTIMEOUT -gt 0 ]; do /bin/kill -0 "$MYSQLPID" >/dev/null 2>&1 || break sleep 1 let STOPTIMEOUT=${STOPTIMEOUT}-1 done if [ $STOPTIMEOUT -eq 0 ]; then echo "Timeout error occurred trying to stop MySQL Daemon." ret=1 action $"Stopping $prog: " /bin/false else rm -f /var/lock/subsys/mysqld rm -f "$socketfile" action $"Stopping $prog: " /bin/true fi else action $"Stopping $prog: " /bin/false fi else ret=1 action $"Stopping $prog: " /bin/false fi return $ret } restart(){ stop start } condrestart(){ [ -e /var/lock/subsys/mysqld ] && restart || : } # See how we were called. case "$1" in start) start ;; stop) stop ;; status) status mysqld ;; restart) restart ;; condrestart) condrestart ;; *) echo $"Usage: $0 {start|stop|status|condrestart|restart}" exit 1 esac exit $? percona-xtradb-cluster-galera/scripts/mysql/centos/init.oracle0000755000000000000000000002770312247075736025147 0ustar rootroot00000000000000#!/bin/sh # Copyright Abandoned 1996 TCX DataKonsult AB & Monty Program KB & Detron HB # This file is public domain and comes with NO WARRANTY of any kind # MySQL daemon start/stop script. # Usually this is put in /etc/init.d (at least on machines SYSV R4 based # systems) and linked to /etc/rc3.d/S99mysql and /etc/rc0.d/K01mysql. # When this is done the mysql server will be started when the machine is # started and shut down when the systems goes down. # Comments to support chkconfig on RedHat Linux # chkconfig: 2345 64 36 # description: A very fast and reliable SQL database engine. # Comments to support LSB init script conventions ### BEGIN INIT INFO # Provides: mysql # Required-Start: $local_fs $network $remote_fs # Should-Start: ypbind nscd ldap ntpd xntpd # Required-Stop: $local_fs $network $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: start and stop MySQL # Description: MySQL is a very fast and reliable SQL database engine. ### END INIT INFO # If you install MySQL on some other places than /, then you # have to do one of the following things for this script to work: # # - Run this script from within the MySQL installation directory # - Create a /etc/my.cnf file with the following information: # [mysqld] # basedir= # - Add the above to any other configuration file (for example ~/.my.ini) # and copy my_print_defaults to /usr/bin # - Add the path to the mysql-installation-directory to the basedir variable # below. # # If you want to affect other MySQL variables, you should make your changes # in the /etc/my.cnf, ~/.my.cnf or other MySQL configuration files. # If you change base dir, you must also change datadir. These may get # overwritten by settings in the MySQL configuration files. basedir= datadir= # Default value, in seconds, afterwhich the script should timeout waiting # for server start. # Value here is overriden by value in my.cnf. # 0 means don't wait at all # Negative numbers mean to wait indefinitely service_startup_timeout=900 # The following variables are only set for letting mysql.server find things. # Set some defaults pid_file= server_pid_file= use_mysqld_safe=1 user=mysql if test -z "$basedir" then basedir=/ bindir=/usr/bin if test -z "$datadir" then datadir=/var/lib/mysql fi sbindir=/usr/sbin libexecdir=/usr/sbin else bindir="$basedir/bin" if test -z "$datadir" then datadir="$basedir/data" fi sbindir="$basedir/sbin" libexecdir="$basedir/libexec" fi # datadir_set is used to determine if datadir was set (and so should be # *not* set inside of the --basedir= handler.) datadir_set= # # Use LSB init script functions for printing messages, if possible # lsb_functions="/lib/lsb/init-functions" if test -f $lsb_functions ; then . $lsb_functions else log_success_msg() { echo " SUCCESS! $@" } log_failure_msg() { echo " ERROR! $@" } fi PATH=/sbin:/usr/sbin:/bin:/usr/bin:$basedir/bin export PATH mode=$1 # start or stop shift other_args="$*" # uncommon, but needed when called from an RPM upgrade action # Expected: "--skip-networking --skip-grant-tables" # They are not checked here, intentionally, as it is the resposibility # of the "spec" file author to give correct arguments only. case `echo "testing\c"`,`echo -n testing` in *c*,-n*) echo_n= echo_c= ;; *c*,*) echo_n=-n echo_c= ;; *) echo_n= echo_c='\c' ;; esac parse_server_arguments() { for arg do case "$arg" in --basedir=*) basedir=`echo "$arg" | sed -e 's/^[^=]*=//'` bindir="$basedir/bin" if test -z "$datadir_set"; then datadir="$basedir/data" fi sbindir="$basedir/sbin" libexecdir="$basedir/libexec" ;; --datadir=*) datadir=`echo "$arg" | sed -e 's/^[^=]*=//'` datadir_set=1 ;; --user=*) user=`echo "$arg" | sed -e 's/^[^=]*=//'` ;; --pid-file=*) server_pid_file=`echo "$arg" | sed -e 's/^[^=]*=//'` ;; --service-startup-timeout=*) service_startup_timeout=`echo "$arg" | sed -e 's/^[^=]*=//'` ;; --use-mysqld_safe) use_mysqld_safe=1;; --use-manager) use_mysqld_safe=0;; esac done } parse_manager_arguments() { for arg do case "$arg" in --pid-file=*) pid_file=`echo "$arg" | sed -e 's/^[^=]*=//'` ;; --user=*) user=`echo "$arg" | sed -e 's/^[^=]*=//'` ;; esac done } wait_for_pid () { verb="$1" manager_pid="$2" # process ID of the program operating on the pid-file i=0 avoid_race_condition="by checking again" while test $i -ne $service_startup_timeout ; do case "$verb" in 'created') # wait for a PID-file to pop into existence. test -s $pid_file && i='' && break ;; 'removed') # wait for this PID-file to disappear test ! -s $pid_file && i='' && break ;; *) echo "wait_for_pid () usage: wait_for_pid created|removed manager_pid" exit 1 ;; esac # if manager isn't running, then pid-file will never be updated if test -n "$manager_pid"; then if kill -0 "$manager_pid" 2>/dev/null; then : # the manager still runs else # The manager may have exited between the last pid-file check and now. if test -n "$avoid_race_condition"; then avoid_race_condition="" continue # Check again. fi # there's nothing that will affect the file. log_failure_msg "Manager of pid-file quit without updating file." return 1 # not waiting any more. fi fi echo $echo_n ".$echo_c" i=`expr $i + 1` sleep 1 done if test -z "$i" ; then log_success_msg return 0 else log_failure_msg return 1 fi } # Get arguments from the my.cnf file, # the only group, which is read from now on is [mysqld] if test -x ./bin/my_print_defaults then print_defaults="./bin/my_print_defaults" elif test -x $bindir/my_print_defaults then print_defaults="$bindir/my_print_defaults" elif test -x $bindir/mysql_print_defaults then print_defaults="$bindir/mysql_print_defaults" else # Try to find basedir in /etc/my.cnf conf=/etc/my.cnf print_defaults= if test -r $conf then subpat='^[^=]*basedir[^=]*=\(.*\)$' dirs=`sed -e "/$subpat/!d" -e 's//\1/' $conf` for d in $dirs do d=`echo $d | sed -e 's/[ ]//g'` if test -x "$d/bin/my_print_defaults" then print_defaults="$d/bin/my_print_defaults" break fi if test -x "$d/bin/mysql_print_defaults" then print_defaults="$d/bin/mysql_print_defaults" break fi done fi # Hope it's in the PATH ... but I doubt it test -z "$print_defaults" && print_defaults="my_print_defaults" fi # # Read defaults file from 'basedir'. If there is no defaults file there # check if it's in the old (depricated) place (datadir) and read it from there # extra_args="" if test -r "$basedir/my.cnf" then extra_args="-e $basedir/my.cnf" else if test -r "$datadir/my.cnf" then extra_args="-e $datadir/my.cnf" fi fi parse_server_arguments `$print_defaults $extra_args mysqld server mysql_server mysql.server` # Look for the pidfile parse_manager_arguments `$print_defaults $extra_args manager` # # Set pid file if not given # if test -z "$pid_file" then pid_file=$datadir/mysqlmanager-`/bin/hostname`.pid else case "$pid_file" in /* ) ;; * ) pid_file="$datadir/$pid_file" ;; esac fi if test -z "$server_pid_file" then server_pid_file=$datadir/`/bin/hostname`.pid else case "$server_pid_file" in /* ) ;; * ) server_pid_file="$datadir/$server_pid_file" ;; esac fi case "$mode" in 'start') # Start daemon # Safeguard (relative paths, core dumps..) cd $basedir manager=$bindir/mysqlmanager if test -x $libexecdir/mysqlmanager then manager=$libexecdir/mysqlmanager elif test -x $sbindir/mysqlmanager then manager=$sbindir/mysqlmanager fi echo $echo_n "Starting MySQL" if test -x $manager -a "$use_mysqld_safe" = "0" then if test -n "$other_args" then log_failure_msg "MySQL manager does not support options '$other_args'" exit 1 fi # Give extra arguments to mysqld with the my.cnf file. This script may # be overwritten at next upgrade. "$manager" \ --mysqld-safe-compatible \ --user="$user" \ --pid-file="$pid_file" >/dev/null 2>&1 & wait_for_pid created $!; return_value=$? # Make lock for RedHat / SuSE if test -w /var/lock/subsys then touch /var/lock/subsys/mysqlmanager fi exit $return_value elif test -x $bindir/mysqld_safe then # Give extra arguments to mysqld with the my.cnf file. This script # may be overwritten at next upgrade. pid_file=$server_pid_file $bindir/mysqld_safe --datadir=$datadir --pid-file=$server_pid_file $other_args >/dev/null 2>&1 & wait_for_pid created $!; return_value=$? # Make lock for RedHat / SuSE if test -w /var/lock/subsys then touch /var/lock/subsys/mysql fi exit $return_value else log_failure_msg "Couldn't find MySQL manager ($manager) or server ($bindir/mysqld_safe)" fi ;; 'stop') # Stop daemon. We use a signal here to avoid having to know the # root password. # The RedHat / SuSE lock directory to remove lock_dir=/var/lock/subsys/mysqlmanager # If the manager pid_file doesn't exist, try the server's if test ! -s "$pid_file" then pid_file=$server_pid_file lock_dir=/var/lock/subsys/mysql fi if test -s "$pid_file" then mysqlmanager_pid=`cat $pid_file` if (kill -0 $mysqlmanager_pid 2>/dev/null) then echo $echo_n "Shutting down MySQL" kill $mysqlmanager_pid # mysqlmanager should remove the pid_file when it exits, so wait for it. wait_for_pid removed "$mysqlmanager_pid"; return_value=$? else log_failure_msg "MySQL manager or server process #$mysqlmanager_pid is not running!" rm $pid_file fi # delete lock for RedHat / SuSE if test -f $lock_dir then rm -f $lock_dir fi exit $return_value else log_failure_msg "MySQL manager or server PID file could not be found!" fi ;; 'restart') # Stop the service and regardless of whether it was # running or not, start it again. if $0 stop $other_args; then $0 start $other_args else log_failure_msg "Failed to stop running server, so refusing to try to start." exit 1 fi ;; 'reload'|'force-reload') if test -s "$server_pid_file" ; then read mysqld_pid < $server_pid_file kill -HUP $mysqld_pid && log_success_msg "Reloading service MySQL" touch $server_pid_file else log_failure_msg "MySQL PID file could not be found!" exit 1 fi ;; 'status') # First, check to see if pid file exists if test -s "$server_pid_file" ; then read mysqld_pid < $server_pid_file if kill -0 $mysqld_pid 2>/dev/null ; then log_success_msg "MySQL running ($mysqld_pid)" exit 0 else log_failure_msg "MySQL is not running, but PID file exists" exit 1 fi else # Try to find appropriate mysqld process mysqld_pid=`pidof $libexecdir/mysqld` if test -z $mysqld_pid ; then if test "$use_mysqld_safe" = "0" ; then lockfile=/var/lock/subsys/mysqlmanager else lockfile=/var/lock/subsys/mysql fi if test -f $lockfile ; then log_failure_msg "MySQL is not running, but lock exists" exit 2 fi log_failure_msg "MySQL is not running" exit 3 else log_failure_msg "MySQL is running but PID file could not be found" exit 4 fi fi ;; *) # usage echo "Usage: $0 {start|stop|restart|reload|force-reload|status} [ MySQL server options ]" exit 1 ;; esac exit 0 percona-xtradb-cluster-galera/scripts/mysql/centos/my.cnf0000644000000000000000000000104412247075736024115 0ustar rootroot00000000000000[mysqld] datadir=/var/lib/mysql socket=/var/lib/mysql/mysql.sock user=mysql # Default to using old password format for compatibility with mysql 3.x # clients (those using the mysqlclient10 compatibility package). old_passwords=1 [mysqld_safe] log-error=/var/log/mysqld.log pid-file=/var/run/mysqld/mysqld.pid # # * IMPORTANT: Additional settings that can override those from this file! # The files must end with '.cnf', otherwise they'll be ignored. # WSREP NOTE: additional wsrep configuration is in wsrep.cnf # !includedir /etc/mysql/conf.d/ percona-xtradb-cluster-galera/scripts/mysql/centos/mysql-wsrep.list0000644000000000000000000000637312247075736026212 0ustar rootroot00000000000000# This is a MySQ L-wsrep package description for ESP package manager # CentOS specific part %requires /bin/sh %requires /sbin/install-info %requires /sbin/ldconfig %requires /usr/sbin/useradd %requires bash %requires chkconfig %requires coreutils %requires grep %requires libc.so.6 %requires libcrypt.so.1 %requires libcrypto.so.6 %requires libdl.so.2 %requires libgcc_s.so.1 %requires libm.so.6 %requires libncurses.so.5 %requires libnsl.so.1 %requires libpthread.so.0 %requires libssl.so.6 %requires libstdc++.so.6 %requires libz.so.1 %requires procps %requires rtld(GNU_HASH) %requires shadow-utils # Required MySQL packages # for PHP-mysql on RHEL5: # %requires MySQL-shared-compat $MYSQL_VER # for mysqldump SST: # %requires MySQL-client-community $MYSQL_VER %provides MySQL %provides MySQL-server %provides mysql %provides mysql-server # Conflicting mysql packages #%incompat mysql-server $prefix=/usr $CONF_DST=/etc/mysql d 755 root root $CONF_DST - c 644 root root /etc/my.cnf $GALERA_SRC/scripts/mysql/centos/my.cnf d 755 root root $CONF_DST/conf.d d 755 root root /etc/rc.d/init.d - f 755 root root /etc/rc.d/init.d/mysql $GALERA_SRC/scripts/mysql/centos/init.oracle d 755 root root /etc/rc.d/logrotate.d - f 755 root root /etc/rc.d/logrotate.d/mysql $MYSQL_SRC/support-files/mysql-log-rotate %if x86_64 # CentOS (read Red Hat) never fails to screw up things $LIBS_DST=${prefix}/lib64/mysql %else $LIBS_DST=${prefix}/lib/mysql %endif $SHAR_DST=${prefix}/share/mysql $SBIN_DST=${prefix}/sbin $BINS_DST=${prefix}/bin $DOCS_DST=${prefix}/share/doc/MySQL-server-$MYSQL_VER $MAN_DST=${prefix}/share/man # Commented out CentOS pieces of code which don't seem to make sense here $mysql_datadir=/var/lib/mysql #d 755 root root ${mysql_data} - %preinstall < /dev/null 2>&1 echo "Giving mysqld 5 seconds to exit nicely" sleep 5 fi EOF_PREINSTALL # Postinstall script is a combination of those from CentOS and MySQL RPMs %postinstall < /dev/null || true useradd -M -r -d $mysql_datadir -s /bin/bash -c "MySQL Server" \ -g mysql mysql 2> /dev/null || true # in case user existed usermod -g mysql mysql 2> /dev/null || true /bin/chown -R mysql:mysql ${CONF_DST} /bin/chmod 0755 ${mysql_datadir} /bin/touch /var/log/mysqld.log /bin/chown -R mysql:mysql ${mysql_datadir} /usr/bin/mysql_install_db --rpm --user=mysql --wsrep-on=0 /bin/chown -R mysql:mysql ${mysql_datadir} /bin/chmod -R og-rw ${mysql_datadir}/mysql if [ -x /etc/init.d/mysql ] ; then /etc/init.d/mysql start echo "Giving mysqld 2 seconds to start" sleep 2 fi sleep 2 EOF_POSTINSTALL %preremove </dev/null 2>&1 || : /sbin/chkconfig --del mysql fi EOF_PREREMOVE #%postremove </dev/null 2>&1 || : #fi #EOF_POSTREMOVE # percona-xtradb-cluster-galera/scripts/mysql/centos/mysqld_safe0000755000000000000000000004451212247075736025244 0ustar rootroot00000000000000#!/bin/sh # Copyright Abandoned 1996 TCX DataKonsult AB & Monty Program KB & Detron HB # This file is public domain and comes with NO WARRANTY of any kind # # Script to start the MySQL daemon and restart it if it dies unexpectedly # # This should be executed in the MySQL base directory if you are using a # binary installation that is not installed in its compile-time default # location # # mysql.server works by first doing a cd to the base directory and from there # executing mysqld_safe KILL_MYSQLD=1; MYSQLD= niceness=0 # Initial logging status: error log is not open, and not using syslog logging=init want_syslog=0 syslog_tag= user='mysql' pid_file= err_log= syslog_tag_mysqld=mysqld syslog_tag_mysqld_safe=mysqld_safe trap '' 1 2 3 15 # we shouldn't let anyone kill us umask 007 defaults= case "$1" in --no-defaults|--defaults-file=*|--defaults-extra-file=*) defaults="$1"; shift ;; esac usage () { cat <> "$err_log" ;; syslog) logger -t "$syslog_tag_mysqld_safe" -p "$priority" "$*" ;; *) echo "Internal program error (non-fatal):" \ " unknown logging method '$logging'" >&2 ;; esac } log_error () { log_generic daemon.error "$@" >&2 } log_notice () { log_generic daemon.notice "$@" } eval_log_error () { cmd="$1" case $logging in file) cmd="$cmd >> "`shell_quote_string "$err_log"`" 2>&1" ;; syslog) # mysqld often prefixes its messages with a timestamp, which is # redundant when logging to syslog (which adds its own timestamp) # However, we don't strip the timestamp with sed here, because # sed buffers output (only GNU sed supports a -u (unbuffered) option) # which means that messages may not get sent to syslog until the # mysqld process quits. cmd="$cmd 2>&1 | logger -t '$syslog_tag_mysqld' -p daemon.error" ;; *) echo "Internal program error (non-fatal):" \ " unknown logging method '$logging'" >&2 ;; esac #echo "Running mysqld: [$cmd]" eval "$cmd" } shell_quote_string() { # This sed command makes sure that any special chars are quoted, # so the arg gets passed exactly to the server. echo "$1" | sed -e 's,\([^a-zA-Z0-9/_.=-]\),\\\1,g' } wsrep_pick_url() { [ $# -eq 0 ] return 0 if ! which nc >/dev/null; then log_error "ERROR: nc tool not found in PATH! Make sure you have it installed." return 1 fi local url # Assuming URL in the form scheme://host:port # If host and port are not NULL, the liveness of URL is assumed to be tested # If port part is absent, the url is returned literally and unconditionally # If every URL has port but none is reachable, nothing is returned for url in `echo $@ | sed s/,/\ /g` 0; do local host=`echo $url | cut -d \: -f 2 | sed s/^\\\/\\\///` local port=`echo $url | cut -d \: -f 3` [ -z "$port" ] && break nc -z "$host" $port >/dev/null && break done if [ "$url" == "0" ]; then log_error "ERROR: none of the URLs in '$@' is reachable." return 1 fi echo $url } # Run mysqld with --wsrep-recover and parse recovered position from log. # Position will be stored in wsrep_start_position_opt global. wsrep_recovery() { cmd="$@" wr_logfile=$(mktemp) log_notice "WSREP: Running position recovery" $cmd --log_error=$wr_logfile --wsrep-recover rp=$(grep "WSREP: Recovered position:" $wr_logfile) if [ -z "$rp" ]; then skipped=$(grep WSREP $wr_logfile | grep "skipping position recovery") if [ -z "$skipped" ]; then log_error "WSREP: Failed to recover position: " \ `cat $wr_logfile`; else log_notice "WSREP: Position recovery skipped" fi else start_pos=$(echo $rp | sed 's/.*WSREP\:\ Recovered\ position://' \ | sed 's/^[ \t]*//') wsrep_start_position_opt="--wsrep_start_position=$start_pos" log_notice "WSREP: Recovered position $start_pos" fi rm $wr_logfile } parse_arguments() { # We only need to pass arguments through to the server if we don't # handle them here. So, we collect unrecognized options (passed on # the command line) into the args variable. pick_args= if test "$1" = PICK-ARGS-FROM-ARGV then pick_args=1 shift fi for arg do val=`echo "$arg" | sed -e "s;--[^=]*=;;"` case "$arg" in # these get passed explicitly to mysqld --basedir=*) MY_BASEDIR_VERSION="$val" ;; --datadir=*) DATADIR="$val" ;; --pid-file=*) pid_file="$val" ;; --user=*) user="$val"; SET_USER=1 ;; # these might have been set in a [mysqld_safe] section of my.cnf # they are added to mysqld command line to override settings from my.cnf --log-error=*) err_log="$val" ;; --port=*) mysql_tcp_port="$val" ;; --socket=*) mysql_unix_port="$val" ;; # mysqld_safe-specific options - must be set in my.cnf ([mysqld_safe])! --core-file-size=*) core_file_size="$val" ;; --ledir=*) ledir="$val" ;; --mysqld=*) MYSQLD="$val" ;; --mysqld-version=*) if test -n "$val" then MYSQLD="mysqld-$val" else MYSQLD="mysqld" fi ;; --nice=*) niceness="$val" ;; --open-files-limit=*) open_files="$val" ;; --open_files_limit=*) open_files="$val" ;; --skip-kill-mysqld*) KILL_MYSQLD=0 ;; --syslog) want_syslog=1 ;; --skip-syslog) want_syslog=0 ;; --syslog-tag=*) syslog_tag="$val" ;; --timezone=*) TZ="$val"; export TZ; ;; --wsrep[-_]urls=*) wsrep_urls="$val"; ;; --help) usage ;; *) if test -n "$pick_args" then append_arg_to_args "$arg" fi ;; esac done } # # First, try to find BASEDIR and ledir (where mysqld is) # if echo '/usr/share/mysql' | grep '^/' > /dev/null then relpkgdata=`echo '/usr/share/mysql' | sed -e 's,^/,,' -e 's,^/,,' -e 's,^,./,'` else # pkgdatadir is not relative to prefix relpkgdata='/usr/share/mysql' fi MY_PWD=`pwd` # Check for the directories we would expect from a binary release install if test -n "$MY_BASEDIR_VERSION" -a -d "$MY_BASEDIR_VERSION" then # BASEDIR is already overridden on command line. Do not re-set. # Use BASEDIR to discover le. if test -x "$MY_BASEDIR_VERSION/libexec/mysqld" then ledir="$MY_BASEDIR_VERSION/libexec" else ledir="$MY_BASEDIR_VERSION/bin" fi elif test -f "$relpkgdata"/english/errmsg.sys -a -x "$MY_PWD/bin/mysqld" then MY_BASEDIR_VERSION="$MY_PWD" # Where bin, share and data are ledir="$MY_PWD/bin" # Where mysqld is # Check for the directories we would expect from a source install elif test -f "$relpkgdata"/english/errmsg.sys -a -x "$MY_PWD/libexec/mysqld" then MY_BASEDIR_VERSION="$MY_PWD" # Where libexec, share and var are ledir="$MY_PWD/libexec" # Where mysqld is # Since we didn't find anything, used the compiled-in defaults else MY_BASEDIR_VERSION='/' ledir='/usr/sbin' fi # # Second, try to find the data directory # # Try where the binary installs put it if test -d $MY_BASEDIR_VERSION/data/mysql then DATADIR=$MY_BASEDIR_VERSION/data if test -z "$defaults" -a -r "$DATADIR/my.cnf" then defaults="--defaults-extra-file=$DATADIR/my.cnf" fi # Next try where the source installs put it elif test -d $MY_BASEDIR_VERSION/var/mysql then DATADIR=$MY_BASEDIR_VERSION/var # Or just give up and use our compiled-in default else DATADIR=/var/lib/mysql fi if test -z "$MYSQL_HOME" then if test -r "$MY_BASEDIR_VERSION/my.cnf" && test -r "$DATADIR/my.cnf" then log_error "WARNING: Found two instances of my.cnf - $MY_BASEDIR_VERSION/my.cnf and $DATADIR/my.cnf IGNORING $DATADIR/my.cnf" MYSQL_HOME=$MY_BASEDIR_VERSION elif test -r "$DATADIR/my.cnf" then log_error "WARNING: Found $DATADIR/my.cnf The data directory is a deprecated location for my.cnf, please move it to $MY_BASEDIR_VERSION/my.cnf" MYSQL_HOME=$DATADIR else MYSQL_HOME=$MY_BASEDIR_VERSION fi fi export MYSQL_HOME # Get first arguments from the my.cnf file, groups [mysqld] and [mysqld_safe] # and then merge with the command line arguments if test -x "$MY_BASEDIR_VERSION/bin/my_print_defaults" then print_defaults="$MY_BASEDIR_VERSION/bin/my_print_defaults" elif test -x ./bin/my_print_defaults then print_defaults="./bin/my_print_defaults" elif test -x /usr/bin/my_print_defaults then print_defaults="/usr/bin/my_print_defaults" elif test -x /usr/bin/mysql_print_defaults then print_defaults="/usr/bin/mysql_print_defaults" else print_defaults="my_print_defaults" fi append_arg_to_args () { args="$args "`shell_quote_string "$1"` } args= SET_USER=2 parse_arguments `$print_defaults $defaults --loose-verbose mysqld server` if test $SET_USER -eq 2 then SET_USER=0 fi parse_arguments `$print_defaults $defaults --loose-verbose mysqld_safe safe_mysqld` parse_arguments PICK-ARGS-FROM-ARGV "$@" # Determine what logging facility to use # Ensure that 'logger' exists, if it's requested if [ $want_syslog -eq 1 ] then my_which logger > /dev/null 2>&1 if [ $? -ne 0 ] then log_error "--syslog requested, but no 'logger' program found. Please ensure that 'logger' is in your PATH, or do not specify the --syslog option to mysqld_safe." exit 1 fi fi if [ -n "$err_log" -o $want_syslog -eq 0 ] then if [ -n "$err_log" ] then # mysqld adds ".err" if there is no extension on the --log-error # argument; must match that here, or mysqld_safe will write to a # different log file than mysqld # mysqld does not add ".err" to "--log-error=foo."; it considers a # trailing "." as an extension if expr "$err_log" : '.*\.[^/]*$' > /dev/null then : else err_log="$err_log".err fi case "$err_log" in /* ) ;; * ) err_log="$DATADIR/$err_log" ;; esac else err_log=$DATADIR/`/bin/hostname`.err fi append_arg_to_args "--log-error=$err_log" if [ $want_syslog -eq 1 ] then # User explicitly asked for syslog, so warn that it isn't used log_error "Can't log to error log and syslog at the same time. Remove all --log-error configuration options for --syslog to take effect." fi # Log to err_log file log_notice "Logging to '$err_log'." logging=file else if [ -n "$syslog_tag" ] then # Sanitize the syslog tag syslog_tag=`echo "$syslog_tag" | sed -e 's/[^a-zA-Z0-9_-]/_/g'` syslog_tag_mysqld_safe="${syslog_tag_mysqld_safe}-$syslog_tag" syslog_tag_mysqld="${syslog_tag_mysqld}-$syslog_tag" fi log_notice "Logging to syslog." logging=syslog fi USER_OPTION="" if test -w / -o "$USER" = "root" then if test "$user" != "root" -o $SET_USER = 1 then USER_OPTION="--user=$user" fi # Change the err log to the right user, if it is in use if [ $want_syslog -eq 0 ]; then touch "$err_log" chown $user "$err_log" fi if test -n "$open_files" then ulimit -n $open_files fi fi if test -n "$open_files" then append_arg_to_args "--open-files-limit=$open_files" fi safe_mysql_unix_port=${mysql_unix_port:-${MYSQL_UNIX_PORT:-/var/lib/mysql/mysql.sock}} # Make sure that directory for $safe_mysql_unix_port exists mysql_unix_port_dir=`dirname $safe_mysql_unix_port` if [ ! -d $mysql_unix_port_dir ] then mkdir $mysql_unix_port_dir chown $user $mysql_unix_port_dir chmod 755 $mysql_unix_port_dir fi # If the user doesn't specify a binary, we assume name "mysqld" if test -z "$MYSQLD" then MYSQLD=mysqld fi if test ! -x "$ledir/$MYSQLD" then log_error "The file $ledir/$MYSQLD does not exist or is not executable. Please cd to the mysql installation directory and restart this script from there as follows: ./bin/mysqld_safe& See http://dev.mysql.com/doc/mysql/en/mysqld-safe.html for more information" exit 1 fi if test -z "$pid_file" then pid_file="$DATADIR/`/bin/hostname`.pid" else case "$pid_file" in /* ) ;; * ) pid_file="$DATADIR/$pid_file" ;; esac fi append_arg_to_args "--pid-file=$pid_file" if test -n "$mysql_unix_port" then append_arg_to_args "--socket=$mysql_unix_port" fi if test -n "$mysql_tcp_port" then append_arg_to_args "--port=$mysql_tcp_port" fi if test $niceness -eq 0 then NOHUP_NICENESS="nohup" else NOHUP_NICENESS="nohup nice -$niceness" fi # Using nice with no args to get the niceness level is GNU-specific. # This check could be extended for other operating systems (e.g., # BSD could use "nohup sh -c 'ps -o nice -p $$' | tail -1"). # But, it also seems that GNU nohup is the only one which messes # with the priority, so this is okay. if nohup nice > /dev/null 2>&1 then normal_niceness=`nice` nohup_niceness=`nohup nice 2>/dev/null` numeric_nice_values=1 for val in $normal_niceness $nohup_niceness do case "$val" in -[0-9] | -[0-9][0-9] | -[0-9][0-9][0-9] | \ [0-9] | [0-9][0-9] | [0-9][0-9][0-9] ) ;; * ) numeric_nice_values=0 ;; esac done if test $numeric_nice_values -eq 1 then nice_value_diff=`expr $nohup_niceness - $normal_niceness` if test $? -eq 0 && test $nice_value_diff -gt 0 && \ nice --$nice_value_diff echo testing > /dev/null 2>&1 then # nohup increases the priority (bad), and we are permitted # to lower the priority with respect to the value the user # might have been given niceness=`expr $niceness - $nice_value_diff` NOHUP_NICENESS="nice -$niceness nohup" fi fi else if nohup echo testing > /dev/null 2>&1 then : else # nohup doesn't work on this system NOHUP_NICENESS="" fi fi # Try to set the core file size (even if we aren't root) because many systems # don't specify a hard limit on core file size. if test -n "$core_file_size" then ulimit -c $core_file_size fi # # If there exists an old pid file, check if the daemon is already running # Note: The switches to 'ps' may depend on your operating system if test -f "$pid_file" then PID=`cat "$pid_file"` if /bin/kill -0 $PID > /dev/null 2> /dev/null then if /bin/ps wwwp $PID | grep -v " grep" | grep -v mysqld_safe | grep -- "$MYSQLD" > /dev/null then # The pid contains a mysqld process log_error "A mysqld process already exists" exit 1 fi fi rm -f "$pid_file" if test -f "$pid_file" then log_error "Fatal error: Can't remove the pid file: $pid_file Please remove it manually and start $0 again; mysqld daemon not started" exit 1 fi fi # # Uncomment the following lines if you want all tables to be automatically # checked and repaired during startup. You should add sensible key_buffer # and sort_buffer values to my.cnf to improve check performance or require # less disk space. # Alternatively, you can start mysqld with the "myisam-recover" option. See # the manual for details. # # echo "Checking tables in $DATADIR" # $MY_BASEDIR_VERSION/bin/myisamchk --silent --force --fast --medium-check $DATADIR/*/*.MYI # $MY_BASEDIR_VERSION/bin/isamchk --silent --force $DATADIR/*/*.ISM # Does this work on all systems? #if type ulimit | grep "shell builtin" > /dev/null #then # ulimit -n 256 > /dev/null 2>&1 # Fix for BSD and FreeBSD systems #fi cmd="$NOHUP_NICENESS" for i in "$ledir/$MYSQLD" "$defaults" "--basedir=$MY_BASEDIR_VERSION" \ "--datadir=$DATADIR" "$USER_OPTION" do cmd="$cmd "`shell_quote_string "$i"` done cmd="$cmd $args" # Avoid 'nohup: ignoring input' warning nohup_redir="" test -n "$NOHUP_NICENESS" && nohup_redir=" < /dev/null" log_notice "Starting $MYSQLD daemon with databases from $DATADIR" while true do rm -f $safe_mysql_unix_port "$pid_file" # Some extra safety [ -n "$wsrep_urls" ] && url=`wsrep_pick_url $wsrep_urls` # check connect address if [ -z "$url" ] then wsrep_recovery "$cmd" eval_log_error "$cmd $wsrep_start_position_opt $nohup_redir" else wsrep_recovery "$cmd" eval_log_error "$cmd $wsrep_start_position_opt --wsrep_cluster_address=$url $nohup_redir" fi if test ! -f "$pid_file" # This is removed if normal shutdown then break fi if true && test $KILL_MYSQLD -eq 1 then # Test if one process was hanging. # This is only a fix for Linux (running as base 3 mysqld processes) # but should work for the rest of the servers. # The only thing is ps x => redhat 5 gives warnings when using ps -x. # kill -9 is used or the process won't react on the kill. numofproces=`ps xaww | grep -v "grep" | grep "$ledir/$MYSQLD\>" | grep -c "pid-file=$pid_file"` log_notice "Number of processes running now: $numofproces" I=1 while test "$I" -le "$numofproces" do PROC=`ps xaww | grep "$ledir/$MYSQLD\>" | grep -v "grep" | grep "pid-file=$pid_file" | sed -n '$p'` for T in $PROC do break done # echo "TEST $I - $T **" if kill -9 $T then log_error "$MYSQLD process hanging, pid $T - killed" else break fi I=`expr $I + 1` done fi log_notice "mysqld restarted" done log_notice "mysqld from pid file $pid_file ended" percona-xtradb-cluster-galera/scripts/mysql/debian/5.1.list0000644000000000000000000002113712247075736024134 0ustar rootroot00000000000000# This is a MySQL-wsrep package description for ESP package manager %include common.list %requires mysql-client-5.1 5.1.41 %provides mysql-server-core-5.1 %provides mysql-server-5.1 ############################################## # Below files sorted into paragraphs # # in the path alphabetical order # ############################################## d 755 root root $BINS_DST - f 755 root root $BINS_DST/msql2mysql $MYSQL_SRC/scripts/msql2mysql f 755 root root $BINS_DST/myisamchk $MYSQL_SRC/storage/myisam/myisamchk f 755 root root $BINS_DST/myisamlog $MYSQL_SRC/storage/myisam/myisamlog f 755 root root $BINS_DST/myisampack $MYSQL_SRC/storage/myisam/myisampack f 755 root root $BINS_DST/mysql_convert_table_format $MYSQL_SRC/scripts/mysql_convert_table_format f 755 root root $BINS_DST/mysql_fix_privilege_tables $MYSQL_SRC/scripts/mysql_fix_privilege_tables f 755 root root $BINS_DST/mysql_install_db $MYSQL_SRC/scripts/mysql_install_db f 755 root root $BINS_DST/mysql_secure_installation $MYSQL_SRC/scripts/mysql_secure_installation f 755 root root $BINS_DST/mysql_setpermission $MYSQL_SRC/scripts/mysql_setpermission f 755 root root $BINS_DST/mysql_tzinfo_to_sql $MYSQL_SRC/sql/mysql_tzinfo_to_sql f 755 root root $BINS_DST/mysql_upgrade $MYSQL_SRC/client/.libs/mysql_upgrade f 755 root root $BINS_DST/mysql_zap $MYSQL_SRC/scripts/mysql_zap f 755 root root $BINS_DST/mysqlbinlog $MYSQL_SRC/client/.libs/mysqlbinlog f 755 root root $BINS_DST/mysqld_multi $MYSQL_SRC/scripts/mysqld_multi f 755 root root $BINS_DST/mysqld_safe $GALERA_SRC/scripts/mysql/debian/mysqld_safe-5.1 f 755 root root $BINS_DST/mysqlhotcopy $MYSQL_SRC/scripts/mysqlhotcopy f 755 root root $BINS_DST/mysqltest $MYSQL_SRC/client/.libs/mysqltest f 755 root root $BINS_DST/replace $MYSQL_SRC/extra/replace f 755 root root $BINS_DST/resolve_stack_dump $MYSQL_SRC/extra/resolve_stack_dump f 755 root root $BINS_DST/resolveip $MYSQL_SRC/extra/resolveip d 755 root root $LIBS_DST - d 755 root root $LIBS_DST/plugin - f 755 root root $LIBS_DST/plugin/ha_innodb_plugin.so.0.0.0 $MYSQL_SRC/storage/innodb_plugin/.libs/ha_innodb_plugin.so.0.0.0 l 000 root root $LIBS_DST/plugin/ha_innodb_plugin.so.0 ha_innodb_plugin.so.0.0.0 l 000 root root $LIBS_DST/plugin/ha_innodb_plugin.so ha_innodb_plugin.so.0.0.0 # /usr/share/doc/... d 755 root root $DOCS_DST - f 644 root root $DOCS_DST/COPYING $MYSQL_SRC/COPYING d 755 root root $DOCS_DST/examples - f 644 root root $DOCS_DST/examples $MYSQL_SRC/support-files/*.cnf # manpages d 755 root root $MAN_DST/man1 - f 644 root root $MAN_DST/man1/innochecksum.1.gz $MYSQL_SRC/man/innochecksum.1.gz f 644 root root $MAN_DST/man1/msql2mysql.1.gz $MYSQL_SRC/man/msql2mysql.1.gz f 644 root root $MAN_DST/man1/myisamchk.1.gz $MYSQL_SRC/man/myisamchk.1.gz f 644 root root $MAN_DST/man1/myisamlog.1.gz $MYSQL_SRC/man/myisamlog.1.gz f 644 root root $MAN_DST/man1/myisampack.1.gz $MYSQL_SRC/man/myisampack.1.gz f 644 root root $MAN_DST/man1/mysql_convert_table_format.1.gz $MYSQL_SRC/man/mysql_convert_table_format.1.gz f 644 root root $MAN_DST/man1/mysql_fix_privilege_tables.1.gz $MYSQL_SRC/man/mysql_fix_privilege_tables.1.gz f 644 root root $MAN_DST/man1/mysql_install_db.1.gz $MYSQL_SRC/man/mysql_install_db.1.gz f 644 root root $MAN_DST/man1/mysql_secure_installation.1.gz $MYSQL_SRC/man/mysql_secure_installation.1.gz f 644 root root $MAN_DST/man1/mysql_setpermission.1.gz $MYSQL_SRC/man/mysql_setpermission.1.gz f 644 root root $MAN_DST/man1/mysql_tzinfo_to_sql.1.gz $MYSQL_SRC/man/mysql_tzinfo_to_sql.1.gz f 644 root root $MAN_DST/man1/mysql_upgrade.1.gz $MYSQL_SRC/man/mysql_upgrade.1.gz f 644 root root $MAN_DST/man1/mysql_zap.1.gz $MYSQL_SRC/man/mysql_zap.1.gz f 644 root root $MAN_DST/man1/mysqlbinlog.1.gz $MYSQL_SRC/man/mysqlbinlog.1.gz f 644 root root $MAN_DST/man1/mysqld_multi.1.gz $MYSQL_SRC/man/mysqld_multi.1.gz f 644 root root $MAN_DST/man1/mysqld_safe.1.gz $MYSQL_SRC/man/mysqld_safe.1.gz f 644 root root $MAN_DST/man1/mysqlhotcopy.1.gz $MYSQL_SRC/man/mysqlhotcopy.1.gz f 644 root root $MAN_DST/man1/mysqltest.1.gz $MYSQL_SRC/man/mysqltest.1.gz l 000 root root $MAN_DST/man1/mysqltest_embedded.1.gz mysqltest.1.gz f 644 root root $MAN_DST/man1/replace.1.gz $MYSQL_SRC/man/replace.1.gz f 644 root root $MAN_DST/man1/resolve_stack_dump.1.gz $MYSQL_SRC/man/resolve_stack_dump.1.gz f 644 root root $MAN_DST/man1/resolveip.1.gz $MYSQL_SRC/man/resolveip.1.gz d 755 root root $MAN_DST/man8 - f 644 root root $MAN_DST/man8/mysqld.8.gz $MYSQL_SRC/man/mysqld.8.gz d 755 root root $SBIN_DST - f 755 root root $SBIN_DST/mysqld $MYSQL_SRC/sql/mysqld d 755 root root $SHAR_DST/mysql-test - l 000 root root $SHAR_DST/mysql-test/mtr mysql-test-run.pl l 000 root root $SHAR_DST/mysql-test/mysql-test-run mysql-test-run.pl f 644 root root $SHAR_DST $MYSQL_SRC/support-files/config.*.ini f 644 root root $SHAR_DST/errmsg.txt $MYSQL_SRC/sql/share/errmsg.txt f 644 root root $SHAR_DST $MYSQL_SRC/scripts/*.sql f 644 root root $SHAR_DST/mysqld_multi.server $MYSQL_SRC/support-files/mysqld_multi.server f 644 root root $SHAR_DST/ndb-config-2-node.ini $MYSQL_SRC/support-files/ndb-config-2-node.ini d 755 root root $SHAR_DST/charsets - f 644 root root $SHAR_DST/charsets $MYSQL_SRC/sql/share/charsets/* $lang00=czech d 755 root root $SHAR_DST/${lang00} - f 644 root root $SHAR_DST/${lang00} $MYSQL_SRC/sql/share/${lang00}/* $lang01=danish d 755 root root $SHAR_DST/${lang01} - f 644 root root $SHAR_DST/${lang01} $MYSQL_SRC/sql/share/${lang01}/* $lang02=dutch d 755 root root $SHAR_DST/${lang02} - f 644 root root $SHAR_DST/${lang02} $MYSQL_SRC/sql/share/${lang02}/* $lang03=english d 755 root root $SHAR_DST/${lang03} - f 644 root root $SHAR_DST/${lang03} $MYSQL_SRC/sql/share/${lang03}/* $lang04=estonian d 755 root root $SHAR_DST/${lang04} - f 644 root root $SHAR_DST/${lang04} $MYSQL_SRC/sql/share/${lang04}/* $lang05=french d 755 root root $SHAR_DST/${lang05} - f 644 root root $SHAR_DST/${lang05} $MYSQL_SRC/sql/share/${lang05}/* $lang06=german d 755 root root $SHAR_DST/${lang06} - f 644 root root $SHAR_DST/${lang06} $MYSQL_SRC/sql/share/${lang06}/* $lang07=greek d 755 root root $SHAR_DST/${lang07} - f 644 root root $SHAR_DST/${lang07} $MYSQL_SRC/sql/share/${lang07}/* $lang08=hungarian d 755 root root $SHAR_DST/${lang08} - f 644 root root $SHAR_DST/${lang08} $MYSQL_SRC/sql/share/${lang08}/* $lang09=italian d 755 root root $SHAR_DST/${lang09} - f 644 root root $SHAR_DST/${lang09} $MYSQL_SRC/sql/share/${lang09}/* $lang10=japanese d 755 root root $SHAR_DST/${lang10} - f 644 root root $SHAR_DST/${lang10} $MYSQL_SRC/sql/share/${lang10}/* $lang11=korean d 755 root root $SHAR_DST/${lang11} - f 644 root root $SHAR_DST/${lang11} $MYSQL_SRC/sql/share/${lang11}/* $lang12=norwegian d 755 root root $SHAR_DST/${lang12} - f 644 root root $SHAR_DST/${lang12} $MYSQL_SRC/sql/share/${lang12}/* $lang13=norwegian-ny d 755 root root $SHAR_DST/${lang13} - f 644 root root $SHAR_DST/${lang13} $MYSQL_SRC/sql/share/${lang13}/* $lang14=polish d 755 root root $SHAR_DST/${lang14} - f 644 root root $SHAR_DST/${lang14} $MYSQL_SRC/sql/share/${lang14}/* $lang15=portuguese d 755 root root $SHAR_DST/${lang15} - f 644 root root $SHAR_DST/${lang15} $MYSQL_SRC/sql/share/${lang15}/* $lang16=romanian d 755 root root $SHAR_DST/${lang16} - f 644 root root $SHAR_DST/${lang16} $MYSQL_SRC/sql/share/${lang16}/* $lang17=russian d 755 root root $SHAR_DST/${lang17} - f 644 root root $SHAR_DST/${lang17} $MYSQL_SRC/sql/share/${lang17}/* $lang18=serbian d 755 root root $SHAR_DST/${lang18} - f 644 root root $SHAR_DST/${lang18} $MYSQL_SRC/sql/share/${lang18}/* $lang19=slovak d 755 root root $SHAR_DST/${lang19} - f 644 root root $SHAR_DST/${lang19} $MYSQL_SRC/sql/share/${lang19}/* $lang20=spanish d 755 root root $SHAR_DST/${lang20} - f 644 root root $SHAR_DST/${lang20} $MYSQL_SRC/sql/share/${lang20}/* $lang21=swedish d 755 root root $SHAR_DST/${lang21} - f 644 root root $SHAR_DST/${lang21} $MYSQL_SRC/sql/share/${lang21}/* $lang22=ukrainian d 755 root root $SHAR_DST/${lang22} - f 644 root root $SHAR_DST/${lang22} $MYSQL_SRC/sql/share/${lang22}/* #d 755 mysql root /var/run/mysqld - #d 755 mysql adm /var/log/mysql - d 755 root root /var/lib/mysql-upgrade - # percona-xtradb-cluster-galera/scripts/mysql/debian/5.5.list0000644000000000000000000002523012247075736024136 0ustar rootroot00000000000000# This is a MySQL-wsrep package description for ESP package manager %include common.list %requires libaio1 %requires mysql-client %replaces mysql-server-core-5.5 0.0.0 ${mysql_version} %replaces mysql-server-5.5 0.0.0 ${mysql_version} %provides mysql-server-core-5.5 %provides mysql-server-5.5 ############################################## # Below files sorted into paragraphs # # in the path alphabetical order # ############################################## d 755 root root $BINS_DST - #in client f 755 root root $BINS_DST/innochecksum $MYSQL_SRC/extra/innochecksum f 755 root root $BINS_DST/msql2mysql $MYSQL_SRC/scripts/msql2mysql #in client f 755 root root $BINS_DST/my_print_defaults $MYSQL_SRC/extra/my_print_defaults f 755 root root $BINS_DST/myisamchk $MYSQL_SRC/storage/myisam/myisamchk f 755 root root $BINS_DST/myisamlog $MYSQL_SRC/storage/myisam/myisamlog f 755 root root $BINS_DST/myisampack $MYSQL_SRC/storage/myisam/myisampack f 755 root root $BINS_DST/mysql_convert_table_format $MYSQL_SRC/scripts/mysql_convert_table_format #f 755 root root $BINS_DST/mysql_fix_privilege_tables $MYSQL_SRC/scripts/mysql_fix_privilege_tables f 755 root root $BINS_DST/mysql_install_db $MYSQL_SRC/scripts/mysql_install_db f 755 root root $BINS_DST/mysql_secure_installation $MYSQL_SRC/scripts/mysql_secure_installation f 755 root root $BINS_DST/mysql_setpermission $MYSQL_SRC/scripts/mysql_setpermission f 755 root root $BINS_DST/mysql_tzinfo_to_sql $MYSQL_SRC/sql/mysql_tzinfo_to_sql f 755 root root $BINS_DST/mysql_upgrade $MYSQL_SRC/client/mysql_upgrade f 755 root root $BINS_DST/mysql_zap $MYSQL_SRC/scripts/mysql_zap f 755 root root $BINS_DST/mysqlbinlog $MYSQL_SRC/client/mysqlbinlog f 755 root root $BINS_DST/mysqld_multi $MYSQL_SRC/scripts/mysqld_multi f 755 root root $BINS_DST/mysqld_safe $GALERA_SRC/scripts/mysql/debian/mysqld_safe-5.5 #f 755 root root $BINS_DST/mysqld_safe $MYSQL_SRC/scripts/mysqld_safe f 755 root root $BINS_DST/mysqlhotcopy $MYSQL_SRC/scripts/mysqlhotcopy f 755 root root $BINS_DST/mysqltest $MYSQL_SRC/client/mysqltest #in client f 755 root root $BINS_DST/perror $MYSQL_SRC/extra/perror f 755 root root $BINS_DST/replace $MYSQL_SRC/extra/replace f 755 root root $BINS_DST/resolve_stack_dump $MYSQL_SRC/extra/resolve_stack_dump f 755 root root $BINS_DST/resolveip $MYSQL_SRC/extra/resolveip f 755 root root $BINS_DST/wsrep_sst_xtrabackup $MYSQL_SRC/scripts/wsrep_sst_xtrabackup d 755 root root $LIBS_DST - d 755 root root $PLUGIN_DST - f 755 root root $PLUGIN_DST/adt_null.so $MYSQL_SRC/plugin/audit_null/adt_null.so f 755 root root $PLUGIN_DST/auth.so $MYSQL_SRC/plugin/auth/auth.so f 755 root root $PLUGIN_DST/auth_socket.so $MYSQL_SRC/plugin/auth/auth_socket.so f 755 root root $PLUGIN_DST/auth_test_plugin.so $MYSQL_SRC/plugin/auth/auth_test_plugin.so f 755 root root $PLUGIN_DST/libdaemon_example.so $MYSQL_SRC/plugin/daemon_example/libdaemon_example.so f 755 root root $PLUGIN_DST/mypluglib.so $MYSQL_SRC/plugin/fulltext/mypluglib.so f 755 root root $PLUGIN_DST/qa_auth_client.so $MYSQL_SRC/plugin/auth/qa_auth_client.so f 755 root root $PLUGIN_DST/qa_auth_interface.so $MYSQL_SRC/plugin/auth/qa_auth_interface.so f 755 root root $PLUGIN_DST/qa_auth_server.so $MYSQL_SRC/plugin/auth/qa_auth_server.so f 755 root root $PLUGIN_DST/semisync_master.so $MYSQL_SRC/plugin/semisync/semisync_master.so f 755 root root $PLUGIN_DST/semisync_slave.so $MYSQL_SRC/plugin/semisync/semisync_slave.so d 755 root root $SBIN_DST - f 755 root root $SBIN_DST/$MYSQLD_BINARY $MYSQL_SRC/sql/$MYSQLD_BINARY # /usr/share/doc/... d 755 root root $DOCS_DST - f 644 root root $DOCS_DST/COPYING $MYSQL_SRC/COPYING f 644 root root $DOCS_DST/ChangeLog $MYSQL_SRC/Docs/ChangeLog f 644 root root $DOCS_DST/INFO_BIN $MYSQL_SRC/Docs/INFO_BIN f 644 root root $DOCS_DST/INFO_SRC $MYSQL_SRC/Docs/INFO_SRC f 644 root root $DOCS_DST/INSTALL-BINARY $MYSQL_SRC/Docs/INSTALL-BINARY f 644 root root $DOCS_DST/README $MYSQL_SRC/README f 644 root root $DOCS_DST/myisam.txt $MYSQL_SRC/Docs/myisam.txt f 644 root root $DOCS_DST/mysql.info $MYSQL_SRC/Docs/mysql.info f 644 root root $DOCS_DST/sp-imp-spec.txt $MYSQL_SRC/Docs/sp-imp-spec.txt # manpages d 755 root root $MAN_DST/man1 - f 644 root root $MAN_DST/man1/comp_err.1.gz $MYSQL_SRC/man/comp_err.1.gz #in client f 644 root root $MAN_DST/man1/innochecksum.1.gz $MYSQL_SRC/man/innochecksum.1.gz f 644 root root $MAN_DST/man1/msql2mysql.1.gz $MYSQL_SRC/man/msql2mysql.1.gz #in client f 644 root root $MAN_DST/man1/my_print_defaults.1.gz $MYSQL_SRC/man/my_print_defaults.1.gz f 644 root root $MAN_DST/man1/myisamchk.1.gz $MYSQL_SRC/man/myisamchk.1.gz f 644 root root $MAN_DST/man1/myisamlog.1.gz $MYSQL_SRC/man/myisamlog.1.gz f 644 root root $MAN_DST/man1/myisampack.1.gz $MYSQL_SRC/man/myisampack.1.gz f 644 root root $MAN_DST/man1/mysql.server.1.gz $MYSQL_SRC/man/mysql.server.1.gz f 644 root root $MAN_DST/man1/mysql_convert_table_format.1.gz $MYSQL_SRC/man/mysql_convert_table_format.1.gz #f 644 root root $MAN_DST/man1/mysql_fix_privilege_tables.1.gz $MYSQL_SRC/man/mysql_fix_privilege_tables.1.gz f 644 root root $MAN_DST/man1/mysql_install_db.1.gz $MYSQL_SRC/man/mysql_install_db.1.gz f 644 root root $MAN_DST/man1/mysql_secure_installation.1.gz $MYSQL_SRC/man/mysql_secure_installation.1.gz f 644 root root $MAN_DST/man1/mysql_setpermission.1.gz $MYSQL_SRC/man/mysql_setpermission.1.gz f 644 root root $MAN_DST/man1/mysql_tzinfo_to_sql.1.gz $MYSQL_SRC/man/mysql_tzinfo_to_sql.1.gz f 644 root root $MAN_DST/man1/mysql_upgrade.1.gz $MYSQL_SRC/man/mysql_upgrade.1.gz f 644 root root $MAN_DST/man1/mysql_zap.1.gz $MYSQL_SRC/man/mysql_zap.1.gz f 644 root root $MAN_DST/man1/mysqlbinlog.1.gz $MYSQL_SRC/man/mysqlbinlog.1.gz f 644 root root $MAN_DST/man1/mysqld_multi.1.gz $MYSQL_SRC/man/mysqld_multi.1.gz f 644 root root $MAN_DST/man1/mysqld_safe.1.gz $MYSQL_SRC/man/mysqld_safe.1.gz f 644 root root $MAN_DST/man1/mysqlhotcopy.1.gz $MYSQL_SRC/man/mysqlhotcopy.1.gz f 644 root root $MAN_DST/man1/mysqltest.1.gz $MYSQL_SRC/man/mysqltest.1.gz #in client f 644 root root $MAN_DST/man1/perror.1.gz $MYSQL_SRC/man/perror.1.gz f 644 root root $MAN_DST/man1/replace.1.gz $MYSQL_SRC/man/replace.1.gz f 644 root root $MAN_DST/man1/resolve_stack_dump.1.gz $MYSQL_SRC/man/resolve_stack_dump.1.gz f 644 root root $MAN_DST/man1/resolveip.1.gz $MYSQL_SRC/man/resolveip.1.gz d 755 root root $MAN_DST/man8 - f 644 root root $MAN_DST/man8/mysqld.8.gz $MYSQL_SRC/man/mysqld.8.gz f 644 root root $SHAR_DST $MYSQL_SRC/support-files/config.*.ini f 644 root root $SHAR_DST $MYSQL_SRC/scripts/*.sql f 644 root root $SHAR_DST/binary-configure $MYSQL_SRC/support-files/binary-configure f 644 root root $SHAR_DST/errmsg-utf8.txt $MYSQL_SRC/sql/share/errmsg-utf8.txt f 644 root root $SHAR_DST $MYSQL_SRC/support-files/my-*.cnf f 644 root root $SHAR_DST/mysql-log-rotate $MYSQL_SRC/support-files/mysql-log-rotate f 755 root root $SHAR_DST/mysql.server $MYSQL_SRC/support-files/mysql.server f 644 root root $SHAR_DST/mysqld_multi.server $MYSQL_SRC/support-files/mysqld_multi.server # f 644 root root $SHAR_DST/ndb-config-2-node.ini $MYSQL_SRC/support-files/ndb-config-2-node.ini d 755 root root $SHAR_DST/charsets - f 644 root root $SHAR_DST/charsets $MYSQL_SRC/sql/share/charsets/* $lang00=czech d 755 root root $SHAR_DST/${lang00} - f 644 root root $SHAR_DST/${lang00} $MYSQL_SRC/sql/share/${lang00}/* $lang01=danish d 755 root root $SHAR_DST/${lang01} - f 644 root root $SHAR_DST/${lang01} $MYSQL_SRC/sql/share/${lang01}/* $lang02=dutch d 755 root root $SHAR_DST/${lang02} - f 644 root root $SHAR_DST/${lang02} $MYSQL_SRC/sql/share/${lang02}/* $lang03=english d 755 root root $SHAR_DST/${lang03} - f 644 root root $SHAR_DST/${lang03} $MYSQL_SRC/sql/share/${lang03}/* $lang04=estonian d 755 root root $SHAR_DST/${lang04} - f 644 root root $SHAR_DST/${lang04} $MYSQL_SRC/sql/share/${lang04}/* $lang05=french d 755 root root $SHAR_DST/${lang05} - f 644 root root $SHAR_DST/${lang05} $MYSQL_SRC/sql/share/${lang05}/* $lang06=german d 755 root root $SHAR_DST/${lang06} - f 644 root root $SHAR_DST/${lang06} $MYSQL_SRC/sql/share/${lang06}/* $lang07=greek d 755 root root $SHAR_DST/${lang07} - f 644 root root $SHAR_DST/${lang07} $MYSQL_SRC/sql/share/${lang07}/* $lang08=hungarian d 755 root root $SHAR_DST/${lang08} - f 644 root root $SHAR_DST/${lang08} $MYSQL_SRC/sql/share/${lang08}/* $lang09=italian d 755 root root $SHAR_DST/${lang09} - f 644 root root $SHAR_DST/${lang09} $MYSQL_SRC/sql/share/${lang09}/* $lang10=japanese d 755 root root $SHAR_DST/${lang10} - f 644 root root $SHAR_DST/${lang10} $MYSQL_SRC/sql/share/${lang10}/* $lang11=korean d 755 root root $SHAR_DST/${lang11} - f 644 root root $SHAR_DST/${lang11} $MYSQL_SRC/sql/share/${lang11}/* $lang12=norwegian d 755 root root $SHAR_DST/${lang12} - f 644 root root $SHAR_DST/${lang12} $MYSQL_SRC/sql/share/${lang12}/* $lang13=norwegian-ny d 755 root root $SHAR_DST/${lang13} - f 644 root root $SHAR_DST/${lang13} $MYSQL_SRC/sql/share/${lang13}/* $lang14=polish d 755 root root $SHAR_DST/${lang14} - f 644 root root $SHAR_DST/${lang14} $MYSQL_SRC/sql/share/${lang14}/* $lang15=portuguese d 755 root root $SHAR_DST/${lang15} - f 644 root root $SHAR_DST/${lang15} $MYSQL_SRC/sql/share/${lang15}/* $lang16=romanian d 755 root root $SHAR_DST/${lang16} - f 644 root root $SHAR_DST/${lang16} $MYSQL_SRC/sql/share/${lang16}/* $lang17=russian d 755 root root $SHAR_DST/${lang17} - f 644 root root $SHAR_DST/${lang17} $MYSQL_SRC/sql/share/${lang17}/* $lang18=serbian d 755 root root $SHAR_DST/${lang18} - f 644 root root $SHAR_DST/${lang18} $MYSQL_SRC/sql/share/${lang18}/* $lang19=slovak d 755 root root $SHAR_DST/${lang19} - f 644 root root $SHAR_DST/${lang19} $MYSQL_SRC/sql/share/${lang19}/* $lang20=spanish d 755 root root $SHAR_DST/${lang20} - f 644 root root $SHAR_DST/${lang20} $MYSQL_SRC/sql/share/${lang20}/* $lang21=swedish d 755 root root $SHAR_DST/${lang21} - f 644 root root $SHAR_DST/${lang21} $MYSQL_SRC/sql/share/${lang21}/* $lang22=ukrainian d 755 root root $SHAR_DST/${lang22} - f 644 root root $SHAR_DST/${lang22} $MYSQL_SRC/sql/share/${lang22}/* d 755 mysql root /var/run/mysqld - # percona-xtradb-cluster-galera/scripts/mysql/debian/5.6.list0000644000000000000000000002570112247075736024142 0ustar rootroot00000000000000# This is a MySQL-wsrep package description for ESP package manager %include common.list %requires libaio1 %requires mysql-client %replaces mysql-server-5.6 0.0.0 ${mysql_version} %replaces mysql-server-5.5 0.0.0 ${mysql_version} %provides mysql-server-5.6 ############################################## # Below files sorted into paragraphs # # in the path alphabetical order # ############################################## d 755 root root $BINS_DST - #in client f 755 root root $BINS_DST/innochecksum $MYSQL_SRC/extra/innochecksum f 755 root root $BINS_DST/msql2mysql $MYSQL_SRC/scripts/msql2mysql #in client f 755 root root $BINS_DST/my_print_defaults $MYSQL_SRC/extra/my_print_defaults f 755 root root $BINS_DST/myisamchk $MYSQL_SRC/storage/myisam/myisamchk f 755 root root $BINS_DST/myisamlog $MYSQL_SRC/storage/myisam/myisamlog f 755 root root $BINS_DST/myisampack $MYSQL_SRC/storage/myisam/myisampack f 755 root root $BINS_DST/mysql_convert_table_format $MYSQL_SRC/scripts/mysql_convert_table_format #f 755 root root $BINS_DST/mysql_fix_privilege_tables $MYSQL_SRC/scripts/mysql_fix_privilege_tables f 755 root root $BINS_DST/mysql_install_db $MYSQL_SRC/scripts/mysql_install_db f 755 root root $BINS_DST/mysql_secure_installation $MYSQL_SRC/scripts/mysql_secure_installation f 755 root root $BINS_DST/mysql_setpermission $MYSQL_SRC/scripts/mysql_setpermission f 755 root root $BINS_DST/mysql_tzinfo_to_sql $MYSQL_SRC/sql/mysql_tzinfo_to_sql f 755 root root $BINS_DST/mysql_upgrade $MYSQL_SRC/client/mysql_upgrade f 755 root root $BINS_DST/mysql_zap $MYSQL_SRC/scripts/mysql_zap f 755 root root $BINS_DST/mysqlbinlog $MYSQL_SRC/client/mysqlbinlog f 755 root root $BINS_DST/mysqld_multi $MYSQL_SRC/scripts/mysqld_multi f 755 root root $BINS_DST/mysqld_safe $GALERA_SRC/scripts/mysql/debian/mysqld_safe-5.5 #f 755 root root $BINS_DST/mysqld_safe $MYSQL_SRC/scripts/mysqld_safe f 755 root root $BINS_DST/mysqlhotcopy $MYSQL_SRC/scripts/mysqlhotcopy f 755 root root $BINS_DST/mysqltest $MYSQL_SRC/client/mysqltest #in client f 755 root root $BINS_DST/perror $MYSQL_SRC/extra/perror f 755 root root $BINS_DST/replace $MYSQL_SRC/extra/replace f 755 root root $BINS_DST/resolve_stack_dump $MYSQL_SRC/extra/resolve_stack_dump f 755 root root $BINS_DST/resolveip $MYSQL_SRC/extra/resolveip f 755 root root $BINS_DST/wsrep_sst_xtrabackup $MYSQL_SRC/scripts/wsrep_sst_xtrabackup d 755 root root $LIBS_DST - d 755 root root $PLUGIN_DST - f 755 root root $PLUGIN_DST/adt_null.so $MYSQL_SRC/plugin/audit_null/adt_null.so f 755 root root $PLUGIN_DST/auth.so $MYSQL_SRC/plugin/auth/auth.so f 755 root root $PLUGIN_DST/auth_socket.so $MYSQL_SRC/plugin/auth/auth_socket.so f 755 root root $PLUGIN_DST/auth_test_plugin.so $MYSQL_SRC/plugin/auth/auth_test_plugin.so f 755 root root $PLUGIN_DST/innodb_engine.so $MYSQL_SRC/plugin/innodb_memcached/innodb_memcache/innodb_engine.so f 755 root root $PLUGIN_DST/libdaemon_example.so $MYSQL_SRC/plugin/daemon_example/libdaemon_example.so f 755 root root $PLUGIN_DST/libmemcached.so $MYSQL_SRC/plugin/innodb_memcached/daemon_memcached/libmemcached.so f 755 root root $PLUGIN_DST/mypluglib.so $MYSQL_SRC/plugin/fulltext/mypluglib.so f 755 root root $PLUGIN_DST/qa_auth_client.so $MYSQL_SRC/plugin/auth/qa_auth_client.so f 755 root root $PLUGIN_DST/qa_auth_interface.so $MYSQL_SRC/plugin/auth/qa_auth_interface.so f 755 root root $PLUGIN_DST/qa_auth_server.so $MYSQL_SRC/plugin/auth/qa_auth_server.so f 755 root root $PLUGIN_DST/semisync_master.so $MYSQL_SRC/plugin/semisync/semisync_master.so f 755 root root $PLUGIN_DST/semisync_slave.so $MYSQL_SRC/plugin/semisync/semisync_slave.so f 755 root root $PLUGIN_DST/validate_password.so $MYSQL_SRC/plugin/password_validation/validate_password.so d 755 root root $SBIN_DST - f 755 root root $SBIN_DST/$MYSQLD_BINARY $MYSQL_SRC/sql/$MYSQLD_BINARY # /usr/share/doc/... d 755 root root $DOCS_DST - f 644 root root $DOCS_DST/COPYING $MYSQL_SRC/COPYING f 644 root root $DOCS_DST/ChangeLog $MYSQL_SRC/Docs/ChangeLog f 644 root root $DOCS_DST/INFO_BIN $MYSQL_SRC/Docs/INFO_BIN f 644 root root $DOCS_DST/INFO_SRC $MYSQL_SRC/Docs/INFO_SRC f 644 root root $DOCS_DST/INSTALL-BINARY $MYSQL_SRC/Docs/INSTALL-BINARY f 644 root root $DOCS_DST/README $MYSQL_SRC/README f 644 root root $DOCS_DST/myisam.txt $MYSQL_SRC/Docs/myisam.txt f 644 root root $DOCS_DST/mysql.info $MYSQL_SRC/Docs/mysql.info f 644 root root $DOCS_DST/sp-imp-spec.txt $MYSQL_SRC/Docs/sp-imp-spec.txt # manpages d 755 root root $MAN_DST/man1 - f 644 root root $MAN_DST/man1/comp_err.1.gz $MYSQL_SRC/man/comp_err.1.gz #in client f 644 root root $MAN_DST/man1/innochecksum.1.gz $MYSQL_SRC/man/innochecksum.1.gz f 644 root root $MAN_DST/man1/msql2mysql.1.gz $MYSQL_SRC/man/msql2mysql.1.gz #in client f 644 root root $MAN_DST/man1/my_print_defaults.1.gz $MYSQL_SRC/man/my_print_defaults.1.gz f 644 root root $MAN_DST/man1/myisamchk.1.gz $MYSQL_SRC/man/myisamchk.1.gz f 644 root root $MAN_DST/man1/myisamlog.1.gz $MYSQL_SRC/man/myisamlog.1.gz f 644 root root $MAN_DST/man1/myisampack.1.gz $MYSQL_SRC/man/myisampack.1.gz f 644 root root $MAN_DST/man1/mysql.server.1.gz $MYSQL_SRC/man/mysql.server.1.gz f 644 root root $MAN_DST/man1/mysql_convert_table_format.1.gz $MYSQL_SRC/man/mysql_convert_table_format.1.gz #f 644 root root $MAN_DST/man1/mysql_fix_privilege_tables.1.gz $MYSQL_SRC/man/mysql_fix_privilege_tables.1.gz f 644 root root $MAN_DST/man1/mysql_install_db.1.gz $MYSQL_SRC/man/mysql_install_db.1.gz f 644 root root $MAN_DST/man1/mysql_secure_installation.1.gz $MYSQL_SRC/man/mysql_secure_installation.1.gz f 644 root root $MAN_DST/man1/mysql_setpermission.1.gz $MYSQL_SRC/man/mysql_setpermission.1.gz f 644 root root $MAN_DST/man1/mysql_tzinfo_to_sql.1.gz $MYSQL_SRC/man/mysql_tzinfo_to_sql.1.gz f 644 root root $MAN_DST/man1/mysql_upgrade.1.gz $MYSQL_SRC/man/mysql_upgrade.1.gz f 644 root root $MAN_DST/man1/mysql_zap.1.gz $MYSQL_SRC/man/mysql_zap.1.gz f 644 root root $MAN_DST/man1/mysqlbinlog.1.gz $MYSQL_SRC/man/mysqlbinlog.1.gz f 644 root root $MAN_DST/man1/mysqld_multi.1.gz $MYSQL_SRC/man/mysqld_multi.1.gz f 644 root root $MAN_DST/man1/mysqld_safe.1.gz $MYSQL_SRC/man/mysqld_safe.1.gz f 644 root root $MAN_DST/man1/mysqlhotcopy.1.gz $MYSQL_SRC/man/mysqlhotcopy.1.gz f 644 root root $MAN_DST/man1/mysqltest.1.gz $MYSQL_SRC/man/mysqltest.1.gz #in client f 644 root root $MAN_DST/man1/perror.1.gz $MYSQL_SRC/man/perror.1.gz f 644 root root $MAN_DST/man1/replace.1.gz $MYSQL_SRC/man/replace.1.gz f 644 root root $MAN_DST/man1/resolve_stack_dump.1.gz $MYSQL_SRC/man/resolve_stack_dump.1.gz f 644 root root $MAN_DST/man1/resolveip.1.gz $MYSQL_SRC/man/resolveip.1.gz d 755 root root $MAN_DST/man8 - f 644 root root $MAN_DST/man8/mysqld.8.gz $MYSQL_SRC/man/mysqld.8.gz f 644 root root $SHAR_DST $MYSQL_SRC/support-files/config.*.ini f 644 root root $SHAR_DST $MYSQL_SRC/scripts/*.sql f 644 root root $SHAR_DST/binary-configure $MYSQL_SRC/support-files/binary-configure f 644 root root $SHAR_DST/errmsg-utf8.txt $MYSQL_SRC/sql/share/errmsg-utf8.txt f 644 root root $SHAR_DST $MYSQL_SRC/support-files/my-*.cnf f 644 root root $SHAR_DST/mysql-log-rotate $MYSQL_SRC/support-files/mysql-log-rotate f 755 root root $SHAR_DST/mysql.server $MYSQL_SRC/support-files/mysql.server f 644 root root $SHAR_DST/mysqld_multi.server $MYSQL_SRC/support-files/mysqld_multi.server # f 644 root root $SHAR_DST/ndb-config-2-node.ini $MYSQL_SRC/support-files/ndb-config-2-node.ini d 755 root root $SHAR_DST/charsets - f 644 root root $SHAR_DST/charsets $MYSQL_SRC/sql/share/charsets/* $lang00=czech d 755 root root $SHAR_DST/${lang00} - f 644 root root $SHAR_DST/${lang00} $MYSQL_SRC/sql/share/${lang00}/* $lang01=danish d 755 root root $SHAR_DST/${lang01} - f 644 root root $SHAR_DST/${lang01} $MYSQL_SRC/sql/share/${lang01}/* $lang02=dutch d 755 root root $SHAR_DST/${lang02} - f 644 root root $SHAR_DST/${lang02} $MYSQL_SRC/sql/share/${lang02}/* $lang03=english d 755 root root $SHAR_DST/${lang03} - f 644 root root $SHAR_DST/${lang03} $MYSQL_SRC/sql/share/${lang03}/* $lang04=estonian d 755 root root $SHAR_DST/${lang04} - f 644 root root $SHAR_DST/${lang04} $MYSQL_SRC/sql/share/${lang04}/* $lang05=french d 755 root root $SHAR_DST/${lang05} - f 644 root root $SHAR_DST/${lang05} $MYSQL_SRC/sql/share/${lang05}/* $lang06=german d 755 root root $SHAR_DST/${lang06} - f 644 root root $SHAR_DST/${lang06} $MYSQL_SRC/sql/share/${lang06}/* $lang07=greek d 755 root root $SHAR_DST/${lang07} - f 644 root root $SHAR_DST/${lang07} $MYSQL_SRC/sql/share/${lang07}/* $lang08=hungarian d 755 root root $SHAR_DST/${lang08} - f 644 root root $SHAR_DST/${lang08} $MYSQL_SRC/sql/share/${lang08}/* $lang09=italian d 755 root root $SHAR_DST/${lang09} - f 644 root root $SHAR_DST/${lang09} $MYSQL_SRC/sql/share/${lang09}/* $lang10=japanese d 755 root root $SHAR_DST/${lang10} - f 644 root root $SHAR_DST/${lang10} $MYSQL_SRC/sql/share/${lang10}/* $lang11=korean d 755 root root $SHAR_DST/${lang11} - f 644 root root $SHAR_DST/${lang11} $MYSQL_SRC/sql/share/${lang11}/* $lang12=norwegian d 755 root root $SHAR_DST/${lang12} - f 644 root root $SHAR_DST/${lang12} $MYSQL_SRC/sql/share/${lang12}/* $lang13=norwegian-ny d 755 root root $SHAR_DST/${lang13} - f 644 root root $SHAR_DST/${lang13} $MYSQL_SRC/sql/share/${lang13}/* $lang14=polish d 755 root root $SHAR_DST/${lang14} - f 644 root root $SHAR_DST/${lang14} $MYSQL_SRC/sql/share/${lang14}/* $lang15=portuguese d 755 root root $SHAR_DST/${lang15} - f 644 root root $SHAR_DST/${lang15} $MYSQL_SRC/sql/share/${lang15}/* $lang16=romanian d 755 root root $SHAR_DST/${lang16} - f 644 root root $SHAR_DST/${lang16} $MYSQL_SRC/sql/share/${lang16}/* $lang17=russian d 755 root root $SHAR_DST/${lang17} - f 644 root root $SHAR_DST/${lang17} $MYSQL_SRC/sql/share/${lang17}/* $lang18=serbian d 755 root root $SHAR_DST/${lang18} - f 644 root root $SHAR_DST/${lang18} $MYSQL_SRC/sql/share/${lang18}/* $lang19=slovak d 755 root root $SHAR_DST/${lang19} - f 644 root root $SHAR_DST/${lang19} $MYSQL_SRC/sql/share/${lang19}/* $lang20=spanish d 755 root root $SHAR_DST/${lang20} - f 644 root root $SHAR_DST/${lang20} $MYSQL_SRC/sql/share/${lang20}/* $lang21=swedish d 755 root root $SHAR_DST/${lang21} - f 644 root root $SHAR_DST/${lang21} $MYSQL_SRC/sql/share/${lang21}/* $lang22=ukrainian d 755 root root $SHAR_DST/${lang22} - f 644 root root $SHAR_DST/${lang22} $MYSQL_SRC/sql/share/${lang22}/* d 755 mysql root /var/run/mysqld - # percona-xtradb-cluster-galera/scripts/mysql/debian/common.list0000644000000000000000000001276012247075736025123 0ustar rootroot00000000000000# This is a MySQL-wsrep package description for ESP package manager # Debian specific part %product wsrep-enabled MySQL server %copyright MySQL AB, Codership Oy, All Rights Reserved %vendor Codership Oy %license COPYING %readme README %description MySQL server + wsrep patch (https://launchpad.net/codership-mysql) $mysql_version=${MYSQL_VER} $wsrep_version=${WSREP_VER} %version ${mysql_version}-${wsrep_version} %requires psmisc %requires debianutils 1.6 %requires libc6 2.4 %requires libdbi-perl %requires libdbd-mysql-perl 1.2202 %requires libgcc1 4.1.1 %requires libncurses5 5.6 %requires libstdc++6 4.1.1 %requires libwrap0 7.6 %requires perl %requires zlib1g 1.1.4 %replaces mysql-server-core 0.0.0 ${mysql_version} %replaces mysql-server-core-5.0 0.0.0 ${mysql_version} %replaces mysql-server-core-5.1 0.0.0 ${mysql_version} %replaces mysql-server 0.0.0 ${mysql_version} %replaces mysql-server-5.0 0.0.0 ${mysql_version} %replaces mysql-server-5.1 0.0.0 ${mysql_version} %provides mysql-server-core %provides mysql-server #%incompat mysql-server-core #%incompat mysql-server-core-4.1 #%incompat mysql-server-core-5.0 #%incompat mysql-server-core-5.1 #%incompat mysql-server #%incompat mysql-server-4.1 #%incompat mysql-server-5.0 #%incompat mysql-server-5.1 $prefix=/usr $CONF_DST=/etc/mysql $LIBS_DST=${prefix}/lib/mysql $PLUGIN_DST=${LIBS_DST}/plugin $SHAR_DST=${prefix}/share/mysql $SBIN_DST=${prefix}/sbin $BINS_DST=${prefix}/bin $DOCS_DST=${prefix}/share/doc/mysql-server-${MYSQL_MAJOR_VER}.${MYSQL_MINOR_VER} $MAN_DST=${prefix}/share/man # Distribution dependent files $SRC=$GALERA_SRC/scripts/mysql/debian $ETC=$SRC/etc d 755 root root /etc/init.d - f 755 root root /etc/init.d/mysql $SRC/mysql d 755 root root /etc/logrotate.d - f 755 root root /etc/logrotate.d/mysql-server $ETC/logrotate.d/mysql-server d 755 root root /etc/logcheck - d 755 root root /etc/logcheck/ignore.d.paranoid - f 644 root root /etc/logcheck/ignore.d.paranoid/mysql-server-${MYSQL_MAJOR_VER}_${MYSQL_MINOR_VER} $ETC/logcheck/ignore.d.paranoid/mysql-server-5_1 d 755 root root /etc/logcheck/ignore.d.server - f 644 root root /etc/logcheck/ignore.d.server/mysql-server-${MYSQL_MAJOR_VER}_${MYSQL_MINOR_VER} $ETC/logcheck/ignore.d.server/mysql-server-5_1 d 755 root root /etc/logcheck/ignore.d.workstation - f 644 root root /etc/logcheck/ignore.d.workstation/mysql-server-${MYSQL_MAJOR_VER}_${MYSQL_MINOR_VER} $ETC/logcheck/ignore.d.workstation/mysql-server-5_1 d 755 root root $CONF_DST f 755 root root $CONF_DST/debian-start $ETC/mysql/debian-start d 755 root root $CONF_DST/conf.d f 644 root root $CONF_DST/conf.d/mysqld_safe_syslog.cnf $ETC/mysql/conf.d/mysqld_safe_syslog.cnf d 755 root root $SHAR_DST - f 755 root root $SHAR_DST/debian-start.inc.sh $SRC/debian-start.inc.sh f 755 root root $SHAR_DST/echo_stderr $SRC/echo_stderr f 755 root root $BINS_DST/my_print_defaults_wsrep $MYSQL_SRC/extra/my_print_defaults ########################## # wsrep-specific files # ########################## c 640 mysql mysql $CONF_DST/conf.d/wsrep.cnf $MYSQL_SRC/support-files/wsrep.cnf f 755 root root $BINS_DST/wsrep_sst_common $MYSQL_SRC/scripts/wsrep_sst_common f 755 root root $BINS_DST/wsrep_sst_mysqldump $MYSQL_SRC/scripts/wsrep_sst_mysqldump f 755 root root $BINS_DST/wsrep_sst_rsync $MYSQL_SRC/scripts/wsrep_sst_rsync l 755 root root $BINS_DST/wsrep_sst_rsync_wan wsrep_sst_rsync #f 755 root root $BINS_DST/wsrep_sst_xtrabackup $MYSQL_SRC/scripts/wsrep_sst_xtrabackup f 644 root root $DOCS_DST/README-wsrep $MYSQL_SRC/Docs/README-wsrep f 644 root root $SHAR_DST/wsrep.cnf $MYSQL_SRC/support-files/wsrep.cnf f 644 root root $SHAR_DST/wsrep_notify $MYSQL_SRC/support-files/wsrep_notify ################################## # Distribution dependent scripts # ################################## $mysql_data=/var/lib/mysql # Add mysql group and user if there are none %preinstall </dev/null || addgroup --system mysql >/dev/null getent passwd mysql >/dev/null || \ adduser --system --disabled-login --ingroup mysql --home ${mysql_data} \ --gecos "MySQL Server" --shell /bin/false mysql >/dev/null [ -e "$BINS_DST/my_print_defaults" ] || \ ( cd $BINS_DST && ln -sf my_print_defaults_wsrep my_print_defaults ) EOF_PREINSTALL %postinstall </dev/null || exit $ ldconfig -n $LIBS_DST #test -d ${mysql_data} || (mysql_install_db --user=mysql --datadir=${mysql_data}) # it seems that we can run mysql_install_db regardless of existing tables. mysql_install_db --wsrep-on=0 --user=mysql --datadir=${mysql_data} --basedir=/usr # This is a fix/workaround for AppArmor profile provided with mysql-server deb [ ! -d /etc/apparmor.d/disable ] || \ ( cd /etc/apparmor.d/disable && ln -sf ../usr.sbin.mysqld ./ ) [ ! -x /etc/init.d/apparmor ] || /etc/init.d/apparmor restart EOF_POSTINSTALL %preremove </dev/null || exit $ [ ! -L $BINS_DST/my_print_defaults ] || rm -rf $BINS_DST/my_print_defaults [ ! -L /etc/apparmor.d/disable/usr.sbin.mysqld ] || rm -rf /etc/apparmor.d/disable/usr.sbin.mysqld [ ! -x /etc/init.d/apparmor ] || /etc/init.d/apparmor restart EOF_PREREMOVE # percona-xtradb-cluster-galera/scripts/mysql/debian/debian-start.inc.sh0000755000000000000000000000505512247075736026421 0ustar rootroot00000000000000#!/bin/bash # # This file is included by /etc/mysql/debian-start # ## Check all unclosed tables. # - Requires the server to be up. # - Is supposed to run silently in background. function check_for_crashed_tables() { set -e set -u # But do it in the background to not stall the boot process. logger -p daemon.info -i -t$0 "Triggering myisam-recover for all MyISAM tables" # Checking for $? is unreliable so the size of the output is checked. # Some table handlers like HEAP do not support CHECK TABLE. tempfile=`tempfile` # We have to use xargs in this case, because a for loop barfs on the # spaces in the thing to be looped over. LC_ALL=C $MYSQL --skip-column-names --batch -e ' select concat('\''select count(*) into @discard from `'\'', TABLE_SCHEMA, '\''`.`'\'', TABLE_NAME, '\''`'\'') from information_schema.TABLES where ENGINE='\''MyISAM'\' | \ xargs -i $MYSQL --skip-column-names --silent --batch \ --force -e "{}" >$tempfile if [ -s $tempfile ]; then ( /bin/echo -e "\n" \ "Improperly closed tables are also reported if clients are accessing\n" \ "the tables *now*. A list of current connections is below.\n"; $MYADMIN processlist status ) >> $tempfile # Check for presence as a dependency on mailx would require an MTA. if [ -x /usr/bin/mailx ]; then mailx -e -s"$MYCHECK_SUBJECT" $MYCHECK_RCPT < $tempfile fi (echo "$MYCHECK_SUBJECT"; cat $tempfile) | logger -p daemon.warn -i -t$0 fi rm $tempfile } ## Check for tables needing an upgrade. # - Requires the server to be up. # - Is supposed to run silently in background. function upgrade_system_tables_if_necessary() { set -e set -u logger -p daemon.info -i -t$0 "Upgrading MySQL tables if necessary." # Filter all "duplicate column", "duplicate key" and "unknown column" # errors as the script is designed to be idempotent. LC_ALL=C $MYUPGRADE \ 2>&1 \ | egrep -v '^(1|@had|ERROR (1054|1060|1061))' \ | logger -p daemon.warn -i -t$0 } ## Check for the presence of both, root accounts with and without password. # This might have been caused by a bug related to mysql_install_db (#418672). function check_root_accounts() { set -e set -u logger -p daemon.info -i -t$0 "Checking for insecure root accounts." ret=$( echo "SELECT count(*) FROM mysql.user WHERE user='root' and password='';" | $MYSQL --skip-column-names ) if [ "$ret" -ne "0" ]; then logger -p daemon.warn -i -t$0 "WARNING: mysql.user contains $ret root accounts without password!" fi } percona-xtradb-cluster-galera/scripts/mysql/debian/echo_stderr0000755000000000000000000000003312247075736025153 0ustar rootroot00000000000000#!/bin/bash echo "$*" 1>&2 percona-xtradb-cluster-galera/scripts/mysql/debian/etc/0000755000000000000000000000000012247075736023503 5ustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/mysql/debian/my.cnf0000644000000000000000000001012712247075736024046 0ustar rootroot00000000000000# # The MySQL database server configuration file. # # You can copy this to one of: # - "/etc/mysql/my.cnf" to set global options, # - "~/.my.cnf" to set user-specific options. # # One can use all long options that the program supports. # Run program with --help to get a list of available options and with # --print-defaults to see which it would actually understand and use. # # For explanations see # http://dev.mysql.com/doc/mysql/en/server-system-variables.html # This will be passed to all mysql clients # It has been reported that passwords should be enclosed with ticks/quotes # escpecially if they contain "#" chars... # Remember to edit /etc/mysql/debian.cnf when changing the socket location. [client] port = 3306 socket = /var/run/mysqld/mysqld.sock # Here is entries for some specific programs # The following values assume you have at least 32M ram # This was formally known as [safe_mysqld]. Both versions are currently parsed. [mysqld_safe] socket = /var/run/mysqld/mysqld.sock nice = 0 [mysqld] # # * Basic Settings # # # * IMPORTANT # If you make changes to these settings and your system uses apparmor, you may # also need to also adjust /etc/apparmor.d/usr.sbin.mysqld. # user = mysql pid-file = /var/run/mysqld/mysqld.pid socket = /var/run/mysqld/mysqld.sock port = 3306 basedir = /usr datadir = /var/lib/mysql tmpdir = /tmp skip-external-locking # # Instead of skip-networking the default is now to listen only on # localhost which is more compatible and is not less secure. # WSREP NOTE: for state transfer to work, bind-address should be an address # of external interface! If you define it explicitely, see SST # options in /etc/mysql/conf.d/wsrep.cnf #bind-address = 127.0.0.1 # # * Fine Tuning # key_buffer = 16M max_allowed_packet = 16M thread_stack = 192K thread_cache_size = 8 # This replaces the startup script and checks MyISAM tables if needed # the first time they are touched myisam-recover = BACKUP #max_connections = 100 #table_cache = 64 #thread_concurrency = 10 # # * Query Cache Configuration # query_cache_limit = 1M query_cache_size = 16M # # * Logging and Replication # # Both location gets rotated by the cronjob. # Be aware that this log type is a performance killer. # As of 5.1 you can enable the log at runtime! #general_log_file = /var/log/mysql/mysql.log #general_log = 1 # # Error logging goes to syslog due to /etc/mysql/conf.d/mysqld_safe_syslog.cnf. # # Here you can see queries with especially long duration #log_slow_queries = /var/log/mysql/mysql-slow.log #long_query_time = 2 #log-queries-not-using-indexes # # The following can be used as easy to replay backup logs or for replication. # note: if you are setting up a replication slave, see README.Debian about # other settings you may need to change. # WSREP NOTE: traditional MySQL replication and binlogging is not supported # and untested with this patch. Some binlog options may cause mysqld # crash. #server-id = 1 #log_bin = /var/log/mysql/mysql-bin.log #expire_logs_days = 10 #max_binlog_size = 100M #binlog_do_db = include_database_name #binlog_ignore_db = include_database_name # # * InnoDB # # InnoDB is enabled by default with a 10MB datafile in /var/lib/mysql/. # Read the manual for more InnoDB related options. There are many! # WSREP NOTE: check /etc/mysql/conf.d/wsrep.cnf for some mandatory InnoDB # options. Don't try to override them! # # * Security Features # # Read the manual, too, if you want chroot! # chroot = /var/lib/mysql/ # # For generating SSL certificates I recommend the OpenSSL GUI "tinyca". # # ssl-ca=/etc/mysql/cacert.pem # ssl-cert=/etc/mysql/server-cert.pem # ssl-key=/etc/mysql/server-key.pem [mysqldump] quick quote-names max_allowed_packet = 16M [mysql] #no-auto-rehash # faster start of mysql but no tab completition [isamchk] key_buffer = 16M # # * IMPORTANT: Additional settings that can override those from this file! # The files must end with '.cnf', otherwise they'll be ignored. # WSREP NOTE: additional wsrep configuration is in wsrep.cnf # !includedir /etc/mysql/conf.d/ percona-xtradb-cluster-galera/scripts/mysql/debian/mysql0000755000000000000000000001374312247075736024033 0ustar rootroot00000000000000#!/bin/bash # ### BEGIN INIT INFO # Provides: mysql # Required-Start: $remote_fs $syslog # Required-Stop: $remote_fs $syslog # Should-Start: $network $named $time # Should-Stop: $network $named $time # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start and stop the mysql database server daemon # Description: Controls the main MySQL database server daemon "mysqld" # and its wrapper script "mysqld_safe". ### END INIT INFO # set -e set -u ${DEBIAN_SCRIPT_DEBUG:+ set -v -x} test -x /usr/sbin/mysqld || exit 0 . /lib/lsb/init-functions SELF=$(cd $(dirname $0); pwd -P)/$(basename $0) CONF=/etc/mysql/my.cnf # MYADMIN="/usr/bin/mysqladmin --defaults-file=/etc/mysql/debian.cnf" # priority can be overriden and "-s" adds output to stderr ERR_LOGGER="logger -p daemon.err -t /etc/init.d/mysql -i" # Safeguard (relative paths, core dumps..) cd / umask 077 # mysqladmin likes to read /root/.my.cnf. This is usually not what I want # as many admins e.g. only store a password without a username there and # so break my scripts. export HOME=/etc/mysql/ ## Fetch a particular option from mysql's invocation. # # Usage: void mysqld_get_param option mysqld_get_param() { /usr/sbin/mysqld --print-defaults \ | tr " " "\n" \ | grep -- "--$1" \ | tail -n 1 \ | cut -d= -f2 } # Determine parameters once per script invocation datadir=`mysqld_get_param datadir` [ -z "$datadir" ] && datadir="/var/lib/mysql" pidfile=`mysqld_get_param pid-file` [ -z "$pidfile" ] && pidfile="$datadir/$(hostname).pid" ## Do some sanity checks before even trying to start mysqld. sanity_checks() { # check for config file if [ ! -r /etc/mysql/my.cnf ]; then log_warning_msg "$0: WARNING: /etc/mysql/my.cnf cannot be read. See README.Debian.gz" echo "WARNING: /etc/mysql/my.cnf cannot be read. See README.Debian.gz" | $ERR_LOGGER fi # check for diskspace shortage if LC_ALL=C BLOCKSIZE= df --portability $datadir/. | tail -n 1 | awk '{ exit ($4>4096) }'; then log_failure_msg "$0: ERROR: The partition with $datadir is too full!" echo "ERROR: The partition with $datadir is too full!" | $ERR_LOGGER exit 1 fi } ## Checks if there is a server running and if so if it is accessible. # # check_alive insists on a pingable server # check_dead also fails if there is a lost mysqld in the process list # # Usage: boolean mysqld_status [check_alive|check_dead] [warn|nowarn] mysqld_status() { # ping_output=`$MYADMIN ping 2>&1`; ping_alive=$(( ! $? )) ps_alive=0 if [ -f "$pidfile" ] && ps `cat $pidfile` >/dev/null 2>&1; then ps_alive=1; fi if [ "$1" = "check_alive" -a $ps_alive = 1 ] || [ "$1" = "check_dead" -a $ps_alive = 0 ]; then return 0 # EXIT_SUCCESS else if [ "$2" = "warn" ]; then # echo -e "$ps_alive processes alive and '$MYADMIN ping' resulted in\n$ping_output\n" | $ERR_LOGGER -p daemon.debug echo -e "$ps_alive processes alive\n" | $ERR_LOGGER -p daemon.debug fi return 1 # EXIT_FAILURE fi } # # main() # cmd=${1:-''} [ $# -ge 1 ] && shift other_args="$*" case "$cmd" in 'start') sanity_checks; # Start daemon log_daemon_msg "Starting MySQL database server" "mysqld" if mysqld_status check_alive nowarn; then log_progress_msg "already running" log_end_msg 0 else # Could be removed during boot test -e /var/run/mysqld || install -m 755 -o mysql -g root -d /var/run/mysqld # Check for additional wsrep options WSREP_OPTS=${WSREP_OPTS:-""} WSREP_PROVIDER=${WSREP_PROVIDER:-""} WSREP_CLUSTER_ADDRESS=${WSREP_CLUSTER_ADDRESS:-""} test -n "$WSREP_PROVIDER" && \ WSREP_OPTS="$WSREP_OPTS --wsrep_provider=$WSREP_PROVIDER" test -n "$WSREP_CLUSTER_ADDRESS" && \ WSREP_OPTS="$WSREP_OPTS --wsrep_cluster_address=$WSREP_CLUSTER_ADDRESS" # Start MySQL!. /usr/bin/mysqld_safe $WSREP_OPTS $other_args > /dev/null 2>&1 & # 6s was reported in #352070 to be too few when using ndbcluster for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14; do sleep 1 if mysqld_status check_alive nowarn ; then break; fi log_progress_msg "." done if mysqld_status check_alive warn; then log_end_msg 0 # Now start mysqlcheck or whatever the admin wants. # output=$(/etc/mysql/debian-start) # [ -n "$output" ] && log_action_msg "$output" else log_end_msg 1 log_failure_msg "Please take a look at the syslog" fi fi ;; 'stop') # * As a passwordless mysqladmin (e.g. via ~/.my.cnf) must be possible # at least for cron, we can rely on it here, too. (although we have # to specify it explicit as e.g. sudo environments points to the normal # users home and not /root) log_daemon_msg "Stopping MySQL database server" "mysqld" if ! mysqld_status check_dead nowarn; then log_daemon_msg "Killing MySQL database server by signal" "mysqld" pid=$(cat $pidfile || echo 0) if [ $pid -eq 0 ]; then log_failure_msg "Failed to get MySQL server pid" exit 1 fi kill -15 $pid server_down= for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do log_progress_msg "." sleep 1 if mysqld_status check_dead nowarn; then server_down=1; break; fi done fi if ! mysqld_status check_dead warn; then log_end_msg 1 log_failure_msg "Please stop MySQL manually and read /usr/share/doc/mysql-server-5.1/README.Debian.gz!" exit -1 else log_end_msg 0 fi ;; 'restart') set +e; $SELF stop; set -e $SELF start $other_args ;; 'reload'|'force-reload') log_daemon_msg "Reloading MySQL database server" "mysqld" $MYADMIN reload log_end_msg 0 ;; 'status') if mysqld_status check_alive nowarn; then # log_action_msg "$($MYADMIN version)" log_action_msg "MySQL is running (PID: $(cat $pidfile))" else log_action_msg "MySQL is stopped." exit 3 fi ;; *) echo "Usage: $SELF start|stop|restart|reload|force-reload|status" exit 1 ;; esac percona-xtradb-cluster-galera/scripts/mysql/debian/mysql-server-wsrep.list0000644000000000000000000000003512247075736027432 0ustar rootroot00000000000000%include ${MYSQL_MAJOR}.list percona-xtradb-cluster-galera/scripts/mysql/debian/mysqld_safe-5.10000755000000000000000000004312612247075736025474 0ustar rootroot00000000000000#!/bin/sh # Copyright Abandoned 1996 TCX DataKonsult AB & Monty Program KB & Detron HB # This file is public domain and comes with NO WARRANTY of any kind # # Script to start the MySQL daemon and restart it if it dies unexpectedly # # This should be executed in the MySQL base directory if you are using a # binary installation that is not installed in its compile-time default # location # # mysql.server works by first doing a cd to the base directory and from there # executing mysqld_safe KILL_MYSQLD=1; MYSQLD= niceness=0 # Initial logging status: error log is not open, and not using syslog logging=init want_syslog=0 syslog_tag= user='mysql' pid_file= err_log= syslog_tag_mysqld=mysqld syslog_tag_mysqld_safe=mysqld_safe umask 007 defaults= case "$1" in --no-defaults|--defaults-file=*|--defaults-extra-file=*) defaults="$1"; shift ;; esac usage () { cat <> "$err_log" ;; syslog) logger -t "$syslog_tag_mysqld_safe" -p "$priority" "$*" ;; *) echo "Internal program error (non-fatal):" \ " unknown logging method '$logging'" >&2 ;; esac } log_error () { log_generic daemon.error "$@" >&2 } log_notice () { log_generic daemon.notice "$@" } eval_log_error () { cmd="$1" case $logging in file) cmd="$cmd >> "`shell_quote_string "$err_log"`" 2>&1" ;; syslog) # mysqld often prefixes its messages with a timestamp, which is # redundant when logging to syslog (which adds its own timestamp) # However, we don't strip the timestamp with sed here, because # sed buffers output (only GNU sed supports a -u (unbuffered) option) # which means that messages may not get sent to syslog until the # mysqld process quits. cmd="$cmd 2>&1 | logger -t '$syslog_tag_mysqld' -p daemon.error & wait" ;; *) echo "Internal program error (non-fatal):" \ " unknown logging method '$logging'" >&2 ;; esac #echo "Running mysqld: [$cmd]" eval "$cmd" } shell_quote_string() { # This sed command makes sure that any special chars are quoted, # so the arg gets passed exactly to the server. echo "$1" | sed -e 's,\([^a-zA-Z0-9/_.=-]\),\\\1,g' } wsrep_pick_url() { [ $# -eq 0 ] return 0 if ! which nc >/dev/null; then log_error "ERROR: nc tool not found in PATH! Make sure you have it installed." return 1 fi local url # Assuming URL in the form scheme://host:port # If host and port are not NULL, the liveness of URL is assumed to be tested # If port part is absent, the url is returned literally and unconditionally # If every URL has port but none is reachable, nothing is returned for url in `echo $@ | sed s/,/\ /g` 0; do local host=`echo $url | cut -d \: -f 2 | sed s/^\\\/\\\///` local port=`echo $url | cut -d \: -f 3` [ -z "$port" ] && break nc -z "$host" $port >/dev/null && break done if [ "$url" == "0" ]; then log_error "ERROR: none of the URLs in '$@' is reachable." return 1 fi echo $url } parse_arguments() { # We only need to pass arguments through to the server if we don't # handle them here. So, we collect unrecognized options (passed on # the command line) into the args variable. pick_args= if test "$1" = PICK-ARGS-FROM-ARGV then pick_args=1 shift fi for arg do val=`echo "$arg" | sed -e "s;--[^=]*=;;"` case "$arg" in # these get passed explicitly to mysqld --basedir=*) MY_BASEDIR_VERSION="$val" ;; --datadir=*) DATADIR="$val" ;; --pid-file=*) pid_file="$val" ;; --user=*) user="$val"; SET_USER=1 ;; # these might have been set in a [mysqld_safe] section of my.cnf # they are added to mysqld command line to override settings from my.cnf --log-error=*) err_log="$val" ;; --port=*) mysql_tcp_port="$val" ;; --socket=*) mysql_unix_port="$val" ;; # mysqld_safe-specific options - must be set in my.cnf ([mysqld_safe])! --core-file-size=*) core_file_size="$val" ;; --ledir=*) ledir="$val" ;; --mysqld=*) MYSQLD="$val" ;; --mysqld-version=*) if test -n "$val" then MYSQLD="mysqld-$val" else MYSQLD="mysqld" fi ;; --nice=*) niceness="$val" ;; --open-files-limit=*) open_files="$val" ;; --open_files_limit=*) open_files="$val" ;; --skip-kill-mysqld*) KILL_MYSQLD=0 ;; --syslog) want_syslog=1 ;; --skip-syslog) want_syslog=0 ;; --syslog-tag=*) syslog_tag="$val" ;; --timezone=*) TZ="$val"; export TZ; ;; --wsrep[-_]urls=*) wsrep_urls="$val"; ;; --help) usage ;; *) if test -n "$pick_args" then append_arg_to_args "$arg" fi ;; esac done } # # First, try to find BASEDIR and ledir (where mysqld is) # if echo '/usr/share/mysql' | grep '^/usr' > /dev/null then relpkgdata=`echo '/usr/share/mysql' | sed -e 's,^/usr,,' -e 's,^/,,' -e 's,^,./,'` else # pkgdatadir is not relative to prefix relpkgdata='/usr/share/mysql' fi MY_PWD=`pwd` # Check for the directories we would expect from a binary release install if test -n "$MY_BASEDIR_VERSION" -a -d "$MY_BASEDIR_VERSION" then # BASEDIR is already overridden on command line. Do not re-set. # Use BASEDIR to discover le. if test -x "$MY_BASEDIR_VERSION/libexec/mysqld" then ledir="$MY_BASEDIR_VERSION/libexec" else ledir="$MY_BASEDIR_VERSION/bin" fi elif test -f "$relpkgdata"/english/errmsg.sys -a -x "$MY_PWD/bin/mysqld" then MY_BASEDIR_VERSION="$MY_PWD" # Where bin, share and data are ledir="$MY_PWD/bin" # Where mysqld is # Check for the directories we would expect from a source install elif test -f "$relpkgdata"/english/errmsg.sys -a -x "$MY_PWD/libexec/mysqld" then MY_BASEDIR_VERSION="$MY_PWD" # Where libexec, share and var are ledir="$MY_PWD/libexec" # Where mysqld is # Since we didn't find anything, used the compiled-in defaults else MY_BASEDIR_VERSION='/usr' ledir='/usr/sbin' fi # # Second, try to find the data directory # # Try where the binary installs put it if test -d $MY_BASEDIR_VERSION/data/mysql then DATADIR=$MY_BASEDIR_VERSION/data if test -z "$defaults" -a -r "$DATADIR/my.cnf" then defaults="--defaults-extra-file=$DATADIR/my.cnf" fi # Next try where the source installs put it elif test -d $MY_BASEDIR_VERSION/var/mysql then DATADIR=$MY_BASEDIR_VERSION/var # Or just give up and use our compiled-in default else DATADIR=/var/lib/mysql fi if test -z "$MYSQL_HOME" then if test -r "$MY_BASEDIR_VERSION/my.cnf" && test -r "$DATADIR/my.cnf" then log_error "WARNING: Found two instances of my.cnf - $MY_BASEDIR_VERSION/my.cnf and $DATADIR/my.cnf IGNORING $DATADIR/my.cnf" MYSQL_HOME=$MY_BASEDIR_VERSION elif test -r "$DATADIR/my.cnf" then log_error "WARNING: Found $DATADIR/my.cnf The data directory is a deprecated location for my.cnf, please move it to $MY_BASEDIR_VERSION/my.cnf" MYSQL_HOME=$DATADIR else MYSQL_HOME=$MY_BASEDIR_VERSION fi fi export MYSQL_HOME # Get first arguments from the my.cnf file, groups [mysqld] and [mysqld_safe] # and then merge with the command line arguments if test -x "$MY_BASEDIR_VERSION/bin/my_print_defaults" then print_defaults="$MY_BASEDIR_VERSION/bin/my_print_defaults" elif test -x ./bin/my_print_defaults then print_defaults="./bin/my_print_defaults" elif test -x /usr/bin/my_print_defaults then print_defaults="/usr/bin/my_print_defaults" elif test -x /usr/bin/mysql_print_defaults then print_defaults="/usr/bin/mysql_print_defaults" else print_defaults="my_print_defaults" fi append_arg_to_args () { args="$args "`shell_quote_string "$1"` } args= SET_USER=2 parse_arguments `$print_defaults $defaults --loose-verbose mysqld server` if test $SET_USER -eq 2 then SET_USER=0 fi parse_arguments `$print_defaults $defaults --loose-verbose mysqld_safe safe_mysqld` parse_arguments PICK-ARGS-FROM-ARGV "$@" # Determine what logging facility to use # Ensure that 'logger' exists, if it's requested if [ $want_syslog -eq 1 ] then my_which logger > /dev/null 2>&1 if [ $? -ne 0 ] then log_error "--syslog requested, but no 'logger' program found. Please ensure that 'logger' is in your PATH, or do not specify the --syslog option to mysqld_safe." exit 1 fi fi if [ -n "$err_log" -o $want_syslog -eq 0 ] then if [ -n "$err_log" ] then # mysqld adds ".err" if there is no extension on the --log-error # argument; must match that here, or mysqld_safe will write to a # different log file than mysqld # mysqld does not add ".err" to "--log-error=foo."; it considers a # trailing "." as an extension if expr "$err_log" : '.*\.[^/]*$' > /dev/null then : else err_log="$err_log".err fi case "$err_log" in /* ) ;; * ) err_log="$DATADIR/$err_log" ;; esac else err_log=$DATADIR/`/bin/hostname`.err fi append_arg_to_args "--log-error=$err_log" if [ $want_syslog -eq 1 ] then # User explicitly asked for syslog, so warn that it isn't used log_error "Can't log to error log and syslog at the same time. Remove all --log-error configuration options for --syslog to take effect." fi # Log to err_log file log_notice "Logging to '$err_log'." logging=file else if [ -n "$syslog_tag" ] then # Sanitize the syslog tag syslog_tag=`echo "$syslog_tag" | sed -e 's/[^a-zA-Z0-9_-]/_/g'` syslog_tag_mysqld_safe="${syslog_tag_mysqld_safe}-$syslog_tag" syslog_tag_mysqld="${syslog_tag_mysqld}-$syslog_tag" fi log_notice "Logging to syslog." logging=syslog fi USER_OPTION="" if test -w / -o "$USER" = "root" then if test "$user" != "root" -o $SET_USER = 1 then USER_OPTION="--user=$user" fi # Change the err log to the right user, if it is in use if [ $want_syslog -eq 0 ]; then touch "$err_log" chown $user "$err_log" fi if test -n "$open_files" then ulimit -n $open_files fi fi if test -n "$open_files" then append_arg_to_args "--open-files-limit=$open_files" fi safe_mysql_unix_port=${mysql_unix_port:-${MYSQL_UNIX_PORT:-/var/run/mysqld/mysqld.sock}} # Make sure that directory for $safe_mysql_unix_port exists mysql_unix_port_dir=`dirname $safe_mysql_unix_port` if [ ! -d $mysql_unix_port_dir ] then mkdir $mysql_unix_port_dir chown $user $mysql_unix_port_dir chmod 755 $mysql_unix_port_dir fi # If the user doesn't specify a binary, we assume name "mysqld" if test -z "$MYSQLD" then MYSQLD=mysqld fi if test ! -x "$ledir/$MYSQLD" then log_error "The file $ledir/$MYSQLD does not exist or is not executable. Please cd to the mysql installation directory and restart this script from there as follows: ./bin/mysqld_safe& See http://dev.mysql.com/doc/mysql/en/mysqld-safe.html for more information" exit 1 fi if test -z "$pid_file" then pid_file="$DATADIR/`/bin/hostname`.pid" else case "$pid_file" in /* ) ;; * ) pid_file="$DATADIR/$pid_file" ;; esac fi append_arg_to_args "--pid-file=$pid_file" if test -n "$mysql_unix_port" then append_arg_to_args "--socket=$mysql_unix_port" fi if test -n "$mysql_tcp_port" then append_arg_to_args "--port=$mysql_tcp_port" fi if test $niceness -eq 0 then NOHUP_NICENESS="nohup" else NOHUP_NICENESS="nohup nice -$niceness" fi # Using nice with no args to get the niceness level is GNU-specific. # This check could be extended for other operating systems (e.g., # BSD could use "nohup sh -c 'ps -o nice -p $$' | tail -1"). # But, it also seems that GNU nohup is the only one which messes # with the priority, so this is okay. if nohup nice > /dev/null 2>&1 then normal_niceness=`nice` nohup_niceness=`nohup nice 2>/dev/null` numeric_nice_values=1 for val in $normal_niceness $nohup_niceness do case "$val" in -[0-9] | -[0-9][0-9] | -[0-9][0-9][0-9] | \ [0-9] | [0-9][0-9] | [0-9][0-9][0-9] ) ;; * ) numeric_nice_values=0 ;; esac done if test $numeric_nice_values -eq 1 then nice_value_diff=`expr $nohup_niceness - $normal_niceness` if test $? -eq 0 && test $nice_value_diff -gt 0 && \ nice --$nice_value_diff echo testing > /dev/null 2>&1 then # nohup increases the priority (bad), and we are permitted # to lower the priority with respect to the value the user # might have been given niceness=`expr $niceness - $nice_value_diff` NOHUP_NICENESS="nice -$niceness nohup" fi fi else if nohup echo testing > /dev/null 2>&1 then : else # nohup doesn't work on this system NOHUP_NICENESS="" fi fi # Try to set the core file size (even if we aren't root) because many systems # don't specify a hard limit on core file size. if test -n "$core_file_size" then ulimit -c $core_file_size fi # # If there exists an old pid file, check if the daemon is already running # Note: The switches to 'ps' may depend on your operating system if test -f "$pid_file" then PID=`cat "$pid_file"` if /bin/kill -0 $PID > /dev/null 2> /dev/null then if /bin/ps wwwp $PID | grep -v " grep" | grep -v mysqld_safe | grep -- "$MYSQLD" > /dev/null then # The pid contains a mysqld process log_error "A mysqld process already exists" exit 1 fi fi rm -f "$pid_file" if test -f "$pid_file" then log_error "Fatal error: Can't remove the pid file: $pid_file Please remove it manually and start $0 again; mysqld daemon not started" exit 1 fi fi # # From now on, we catch signals to do a proper shutdown of mysqld # when signalled to do so. # trap '/usr/bin/mysqladmin --defaults-extra-file=/etc/mysql/debian.cnf refresh & wait' 1 # HUP trap '/usr/bin/mysqladmin --defaults-extra-file=/etc/mysql/debian.cnf shutdown' 2 3 15 # INT QUIT and TERM # # Uncomment the following lines if you want all tables to be automatically # checked and repaired during startup. You should add sensible key_buffer # and sort_buffer values to my.cnf to improve check performance or require # less disk space. # Alternatively, you can start mysqld with the "myisam-recover" option. See # the manual for details. # # echo "Checking tables in $DATADIR" # $MY_BASEDIR_VERSION/bin/myisamchk --silent --force --fast --medium-check $DATADIR/*/*.MYI # $MY_BASEDIR_VERSION/bin/isamchk --silent --force $DATADIR/*/*.ISM # Does this work on all systems? #if type ulimit | grep "shell builtin" > /dev/null #then # ulimit -n 256 > /dev/null 2>&1 # Fix for BSD and FreeBSD systems #fi cmd="$NOHUP_NICENESS" for i in "$ledir/$MYSQLD" "$defaults" "--basedir=$MY_BASEDIR_VERSION" \ "--datadir=$DATADIR" "$USER_OPTION" do cmd="$cmd "`shell_quote_string "$i"` done cmd="$cmd $args" # Avoid 'nohup: ignoring input' warning test -n "$NOHUP_NICENESS" && cmd="$cmd < /dev/null" log_notice "Starting $MYSQLD daemon with databases from $DATADIR" while true do rm -f $safe_mysql_unix_port "$pid_file" # Some extra safety [ -n "$wsrep_urls" ] && url=`wsrep_pick_url $wsrep_urls` # check connect address if [ -z "$url" ] then eval_log_error "$cmd" else eval_log_error "$cmd --wsrep_cluster_address=$url" fi if test ! -f "$pid_file" # This is removed if normal shutdown then break fi if true && test $KILL_MYSQLD -eq 1 then # Test if one process was hanging. # This is only a fix for Linux (running as base 3 mysqld processes) # but should work for the rest of the servers. # The only thing is ps x => redhat 5 gives warnings when using ps -x. # kill -9 is used or the process won't react on the kill. numofproces=`ps xaww | grep -v "grep" | grep "$ledir/$MYSQLD\>" | grep -c "pid-file=$pid_file"` log_notice "Number of processes running now: $numofproces" I=1 while test "$I" -le "$numofproces" do PROC=`ps xaww | grep "$ledir/$MYSQLD\>" | grep -v "grep" | grep "pid-file=$pid_file" | sed -n '$p'` for T in $PROC do break done # echo "TEST $I - $T **" if kill -9 $T then log_error "$MYSQLD process hanging, pid $T - killed" else break fi I=`expr $I + 1` done fi log_notice "mysqld restarted" done log_notice "mysqld from pid file $pid_file ended" percona-xtradb-cluster-galera/scripts/mysql/debian/mysqld_safe-5.50000755000000000000000000006323112247075736025477 0ustar rootroot00000000000000#!/bin/sh # Copyright Abandoned 1996 TCX DataKonsult AB & Monty Program KB & Detron HB # This file is public domain and comes with NO WARRANTY of any kind # # Script to start the MySQL daemon and restart it if it dies unexpectedly # # This should be executed in the MySQL base directory if you are using a # binary installation that is not installed in its compile-time default # location # # mysql.server works by first doing a cd to the base directory and from there # executing mysqld_safe # Initialize script globals KILL_MYSQLD=1; MYSQLD= niceness=0 mysqld_ld_preload= mysqld_ld_library_path= # Initial logging status: error log is not open, and not using syslog logging=init want_syslog=0 syslog_tag= user='mysql' pid_file= err_log= syslog_tag_mysqld=mysqld syslog_tag_mysqld_safe=mysqld_safe umask 007 defaults= case "$1" in --no-defaults|--defaults-file=*|--defaults-extra-file=*) defaults="$1"; shift ;; esac usage () { cat <> "$err_log" ;; syslog) logger -t "$syslog_tag_mysqld_safe" -p "$priority" "$*" ;; *) echo "Internal program error (non-fatal):" \ " unknown logging method '$logging'" >&2 ;; esac } log_error () { log_generic daemon.error "$@" >&2 } log_notice () { log_generic daemon.notice "$@" } eval_log_error () { local cmd="$1" case $logging in file) cmd="$cmd >> "`shell_quote_string "$err_log"`" 2>&1" ;; syslog) # mysqld often prefixes its messages with a timestamp, which is # redundant when logging to syslog (which adds its own timestamp) # However, we don't strip the timestamp with sed here, because # sed buffers output (only GNU sed supports a -u (unbuffered) option) # which means that messages may not get sent to syslog until the # mysqld process quits. cmd="$cmd 2>&1 | logger -t '$syslog_tag_mysqld' -p daemon.error & wait" ;; *) echo "Internal program error (non-fatal):" \ " unknown logging method '$logging'" >&2 ;; esac #echo "Running mysqld: [$cmd]" eval "$cmd" } shell_quote_string() { # This sed command makes sure that any special chars are quoted, # so the arg gets passed exactly to the server. echo "$1" | sed -e 's,\([^a-zA-Z0-9/_.=-]\),\\\1,g' } wsrep_pick_url() { [ $# -eq 0 ] return 0 if ! which nc >/dev/null; then log_error "ERROR: nc tool not found in PATH! Make sure you have it installed." return 1 fi local url # Assuming URL in the form scheme://host:port # If host and port are not NULL, the liveness of URL is assumed to be tested # If port part is absent, the url is returned literally and unconditionally # If every URL has port but none is reachable, nothing is returned for url in `echo $@ | sed s/,/\ /g` 0; do local host=`echo $url | cut -d \: -f 2 | sed s/^\\\/\\\///` local port=`echo $url | cut -d \: -f 3` [ -z "$port" ] && break nc -z "$host" $port >/dev/null && break done if [ "$url" == "0" ]; then log_error "ERROR: none of the URLs in '$@' is reachable." return 1 fi echo $url } # Run mysqld with --wsrep-recover and parse recovered position from log. # Position will be stored in wsrep_start_position_opt global. wsrep_start_position_opt="" wsrep_recover_position() { local mysqld_cmd="$@" local wr_logfile=$(mktemp) local euid=$(id -u) local ret=0 [ "$euid" = "0" ] && chown $user $wr_logfile chmod 600 $wr_logfile log_notice "WSREP: Running position recovery with --log_error=$wr_logfile" $mysqld_cmd --log_error=$wr_logfile --wsrep-recover local rp="$(grep 'WSREP: Recovered position:' $wr_logfile)" if [ -z "$rp" ]; then local skipped="$(grep WSREP $wr_logfile | grep 'skipping position recovery')" if [ -z "$skipped" ]; then log_error "WSREP: Failed to recover position: " `cat $wr_logfile`; ret=1 else log_notice "WSREP: Position recovery skipped" fi else local start_pos="$(echo $rp | sed 's/.*WSREP\:\ Recovered\ position://' \ | sed 's/^[ \t]*//')" log_notice "WSREP: Recovered position $start_pos" wsrep_start_position_opt="--wsrep_start_position=$start_pos" fi rm $wr_logfile return $ret } parse_arguments() { # We only need to pass arguments through to the server if we don't # handle them here. So, we collect unrecognized options (passed on # the command line) into the args variable. pick_args= if test "$1" = PICK-ARGS-FROM-ARGV then pick_args=1 shift fi for arg do # the parameter after "=", or the whole $arg if no match val=`echo "$arg" | sed -e 's;^--[^=]*=;;'` # what's before "=", or the whole $arg if no match optname=`echo "$arg" | sed -e 's/^\(--[^=]*\)=.*$/\1/'` # replace "_" by "-" ; mysqld_safe must accept "_" like mysqld does. optname_subst=`echo "$optname" | sed 's/_/-/g'` arg=`echo $arg | sed "s/^$optname/$optname_subst/"` case "$arg" in # these get passed explicitly to mysqld --basedir=*) MY_BASEDIR_VERSION="$val" ;; --datadir=*) DATADIR="$val" ;; --pid-file=*) pid_file="$val" ;; --plugin-dir=*) PLUGIN_DIR="$val" ;; --user=*) user="$val"; SET_USER=1 ;; # these might have been set in a [mysqld_safe] section of my.cnf # they are added to mysqld command line to override settings from my.cnf --log-error=*) err_log="$val" ;; --port=*) mysql_tcp_port="$val" ;; --socket=*) mysql_unix_port="$val" ;; # mysqld_safe-specific options - must be set in my.cnf ([mysqld_safe])! --core-file-size=*) core_file_size="$val" ;; --ledir=*) ledir="$val" ;; --malloc-lib=*) set_malloc_lib "$val" ;; --mysqld=*) MYSQLD="$val" ;; --mysqld-version=*) if test -n "$val" then MYSQLD="mysqld-$val" PLUGIN_VARIANT="/$val" else MYSQLD="mysqld" fi ;; --nice=*) niceness="$val" ;; --open-files-limit=*) open_files="$val" ;; --open_files_limit=*) open_files="$val" ;; --skip-kill-mysqld*) KILL_MYSQLD=0 ;; --syslog) want_syslog=1 ;; --skip-syslog) want_syslog=0 ;; --syslog-tag=*) syslog_tag="$val" ;; --timezone=*) TZ="$val"; export TZ; ;; --wsrep[-_]urls=*) wsrep_urls="$val"; ;; --wsrep[-_]provider=*) if test -n "$val" && test "$val" != "none" then wsrep_restart=1 fi ;; --help) usage ;; *) if test -n "$pick_args" then append_arg_to_args "$arg" fi ;; esac done } # Add a single shared library to the list of libraries which will be added to # LD_PRELOAD for mysqld # # Since LD_PRELOAD is a space-separated value (for historical reasons), if a # shared lib's path contains spaces, that path will be prepended to # LD_LIBRARY_PATH and stripped from the lib value. add_mysqld_ld_preload() { lib_to_add="$1" log_notice "Adding '$lib_to_add' to LD_PRELOAD for mysqld" case "$lib_to_add" in *' '*) # Must strip path from lib, and add it to LD_LIBRARY_PATH lib_file=`basename "$lib_to_add"` case "$lib_file" in *' '*) # The lib file itself has a space in its name, and can't # be used in LD_PRELOAD log_error "library name '$lib_to_add' contains spaces and can not be used with LD_PRELOAD" exit 1 ;; esac lib_path=`dirname "$lib_to_add"` lib_to_add="$lib_file" [ -n "$mysqld_ld_library_path" ] && mysqld_ld_library_path="$mysqld_ld_library_path:" mysqld_ld_library_path="$mysqld_ld_library_path$lib_path" ;; esac # LD_PRELOAD is a space-separated [ -n "$mysqld_ld_preload" ] && mysqld_ld_preload="$mysqld_ld_preload " mysqld_ld_preload="${mysqld_ld_preload}$lib_to_add" } # Returns LD_PRELOAD (and LD_LIBRARY_PATH, if needed) text, quoted to be # suitable for use in the eval that calls mysqld. # # All values in mysqld_ld_preload are prepended to LD_PRELOAD. mysqld_ld_preload_text() { text= if [ -n "$mysqld_ld_preload" ]; then new_text="$mysqld_ld_preload" [ -n "$LD_PRELOAD" ] && new_text="$new_text $LD_PRELOAD" text="${text}LD_PRELOAD="`shell_quote_string "$new_text"`' ' fi if [ -n "$mysqld_ld_library_path" ]; then new_text="$mysqld_ld_library_path" [ -n "$LD_LIBRARY_PATH" ] && new_text="$new_text:$LD_LIBRARY_PATH" text="${text}LD_LIBRARY_PATH="`shell_quote_string "$new_text"`' ' fi echo "$text" } mysql_config= get_mysql_config() { if [ -z "$mysql_config" ]; then mysql_config=`echo "$0" | sed 's,/[^/][^/]*$,/mysql_config,'` if [ ! -x "$mysql_config" ]; then log_error "Can not run mysql_config $@ from '$mysql_config'" exit 1 fi fi "$mysql_config" "$@" } # set_malloc_lib LIB # - If LIB is empty, do nothing and return # - If LIB is 'tcmalloc', look for tcmalloc shared library in /usr/lib # then pkglibdir. tcmalloc is part of the Google perftools project. # - If LIB is an absolute path, assume it is a malloc shared library # # Put LIB in mysqld_ld_preload, which will be added to LD_PRELOAD when # running mysqld. See ld.so for details. set_malloc_lib() { malloc_lib="$1" if [ "$malloc_lib" = tcmalloc ]; then pkglibdir=`get_mysql_config --variable=pkglibdir` malloc_lib= # This list is kept intentionally simple. Simply set --malloc-lib # to a full path if another location is desired. for libdir in /usr/lib "$pkglibdir" "$pkglibdir/mysql"; do for flavor in _minimal '' _and_profiler _debug; do tmp="$libdir/libtcmalloc$flavor.so" #log_notice "DEBUG: Checking for malloc lib '$tmp'" [ -r "$tmp" ] || continue malloc_lib="$tmp" break 2 done done if [ -z "$malloc_lib" ]; then log_error "no shared library for --malloc-lib=tcmalloc found in /usr/lib or $pkglibdir" exit 1 fi fi # Allow --malloc-lib='' to override other settings [ -z "$malloc_lib" ] && return case "$malloc_lib" in /*) if [ ! -r "$malloc_lib" ]; then log_error "--malloc-lib '$malloc_lib' can not be read and will not be used" exit 1 fi ;; *) log_error "--malloc-lib must be an absolute path or 'tcmalloc'; " \ "ignoring value '$malloc_lib'" exit 1 ;; esac add_mysqld_ld_preload "$malloc_lib" } # # First, try to find BASEDIR and ledir (where mysqld is) # if echo '/usr/share/mysql' | grep '^/usr' > /dev/null then relpkgdata=`echo '/usr/share/mysql' | sed -e 's,^/usr,,' -e 's,^/,,' -e 's,^,./,'` else # pkgdatadir is not relative to prefix relpkgdata='/usr/share/mysql' fi MY_PWD=`pwd` # Check for the directories we would expect from a binary release install if test -n "$MY_BASEDIR_VERSION" -a -d "$MY_BASEDIR_VERSION" then # BASEDIR is already overridden on command line. Do not re-set. # Use BASEDIR to discover le. if test -x "$MY_BASEDIR_VERSION/libexec/mysqld" then ledir="$MY_BASEDIR_VERSION/libexec" elif test -x "$MY_BASEDIR_VERSION/sbin/mysqld" then ledir="$MY_BASEDIR_VERSION/sbin" else ledir="$MY_BASEDIR_VERSION/bin" fi elif test -f "$relpkgdata"/english/errmsg.sys -a -x "$MY_PWD/bin/mysqld" then MY_BASEDIR_VERSION="$MY_PWD" # Where bin, share and data are ledir="$MY_PWD/bin" # Where mysqld is # Check for the directories we would expect from a source install elif test -f "$relpkgdata"/english/errmsg.sys -a -x "$MY_PWD/libexec/mysqld" then MY_BASEDIR_VERSION="$MY_PWD" # Where libexec, share and var are ledir="$MY_PWD/libexec" # Where mysqld is elif test -f "$relpkgdata"/english/errmsg.sys -a -x "$MY_PWD/sbin/mysqld" then MY_BASEDIR_VERSION="$MY_PWD" # Where sbin, share and var are ledir="$MY_PWD/sbin" # Where mysqld is # Since we didn't find anything, used the compiled-in defaults else MY_BASEDIR_VERSION='/usr' ledir='/usr/sbin' fi # # Second, try to find the data directory # # Try where the binary installs put it if test -d $MY_BASEDIR_VERSION/data/mysql then DATADIR=$MY_BASEDIR_VERSION/data if test -z "$defaults" -a -r "$DATADIR/my.cnf" then defaults="--defaults-extra-file=$DATADIR/my.cnf" fi # Next try where the source installs put it elif test -d $MY_BASEDIR_VERSION/var/mysql then DATADIR=$MY_BASEDIR_VERSION/var # Or just give up and use our compiled-in default else DATADIR=/var/lib/mysql fi # # Try to find the plugin directory # # Use user-supplied argument if [ -n "${PLUGIN_DIR}" ]; then plugin_dir="${PLUGIN_DIR}" else # Try to find plugin dir relative to basedir for dir in lib/mysql/plugin lib/plugin do if [ -d "${MY_BASEDIR_VERSION}/${dir}" ]; then plugin_dir="${MY_BASEDIR_VERSION}/${dir}" break fi done # Give up and use compiled-in default if [ -z "${plugin_dir}" ]; then plugin_dir='/usr/lib/mysql/plugin' fi fi plugin_dir="${plugin_dir}${PLUGIN_VARIANT}" if test -z "$MYSQL_HOME" then if test -r "$MY_BASEDIR_VERSION/my.cnf" && test -r "$DATADIR/my.cnf" then log_error "WARNING: Found two instances of my.cnf - $MY_BASEDIR_VERSION/my.cnf and $DATADIR/my.cnf IGNORING $DATADIR/my.cnf" MYSQL_HOME=$MY_BASEDIR_VERSION elif test -r "$DATADIR/my.cnf" then log_error "WARNING: Found $DATADIR/my.cnf The data directory is a deprecated location for my.cnf, please move it to $MY_BASEDIR_VERSION/my.cnf" MYSQL_HOME=$DATADIR else MYSQL_HOME=$MY_BASEDIR_VERSION fi fi export MYSQL_HOME # Get first arguments from the my.cnf file, groups [mysqld] and [mysqld_safe] # and then merge with the command line arguments if test -x "$MY_BASEDIR_VERSION/bin/my_print_defaults" then print_defaults="$MY_BASEDIR_VERSION/bin/my_print_defaults" elif test -x `dirname $0`/my_print_defaults then print_defaults="`dirname $0`/my_print_defaults" elif test -x ./bin/my_print_defaults then print_defaults="./bin/my_print_defaults" elif test -x /usr/bin/my_print_defaults then print_defaults="/usr/bin/my_print_defaults" elif test -x /usr/bin/mysql_print_defaults then print_defaults="/usr/bin/mysql_print_defaults" else print_defaults="my_print_defaults" fi append_arg_to_args () { args="$args "`shell_quote_string "$1"` } args= SET_USER=2 parse_arguments `$print_defaults $defaults --loose-verbose mysqld server` if test $SET_USER -eq 2 then SET_USER=0 fi parse_arguments `$print_defaults $defaults --loose-verbose mysqld_safe safe_mysqld` parse_arguments PICK-ARGS-FROM-ARGV "$@" # Determine what logging facility to use # Ensure that 'logger' exists, if it's requested if [ $want_syslog -eq 1 ] then my_which logger > /dev/null 2>&1 if [ $? -ne 0 ] then log_error "--syslog requested, but no 'logger' program found. Please ensure that 'logger' is in your PATH, or do not specify the --syslog option to mysqld_safe." exit 1 fi fi if [ -n "$err_log" -o $want_syslog -eq 0 ] then if [ -n "$err_log" ] then # mysqld adds ".err" if there is no extension on the --log-error # argument; must match that here, or mysqld_safe will write to a # different log file than mysqld # mysqld does not add ".err" to "--log-error=foo."; it considers a # trailing "." as an extension if expr "$err_log" : '.*\.[^/]*$' > /dev/null then : else err_log="$err_log".err fi case "$err_log" in /* ) ;; * ) err_log="$DATADIR/$err_log" ;; esac else err_log=$DATADIR/`hostname`.err fi append_arg_to_args "--log-error=$err_log" if [ $want_syslog -eq 1 ] then # User explicitly asked for syslog, so warn that it isn't used log_error "Can't log to error log and syslog at the same time. Remove all --log-error configuration options for --syslog to take effect." fi # Log to err_log file log_notice "Logging to '$err_log'." logging=file else if [ -n "$syslog_tag" ] then # Sanitize the syslog tag syslog_tag=`echo "$syslog_tag" | sed -e 's/[^a-zA-Z0-9_-]/_/g'` syslog_tag_mysqld_safe="${syslog_tag_mysqld_safe}-$syslog_tag" syslog_tag_mysqld="${syslog_tag_mysqld}-$syslog_tag" fi log_notice "Logging to syslog." logging=syslog fi USER_OPTION="" if test -w / -o "$USER" = "root" then if test "$user" != "root" -o $SET_USER = 1 then USER_OPTION="--user=$user" fi # Change the err log to the right user, if it is in use if [ $want_syslog -eq 0 ]; then touch "$err_log" chown $user "$err_log" fi if test -n "$open_files" then ulimit -n $open_files fi fi if test -n "$open_files" then append_arg_to_args "--open-files-limit=$open_files" fi safe_mysql_unix_port=${mysql_unix_port:-${MYSQL_UNIX_PORT:-/var/run/mysqld/mysqld.sock}} # Make sure that directory for $safe_mysql_unix_port exists mysql_unix_port_dir=`dirname $safe_mysql_unix_port` if [ ! -d $mysql_unix_port_dir ] then mkdir $mysql_unix_port_dir chown $user $mysql_unix_port_dir chmod 755 $mysql_unix_port_dir fi # If the user doesn't specify a binary, we assume name "mysqld" if test -z "$MYSQLD" then MYSQLD=mysqld fi if test ! -x "$ledir/$MYSQLD" then log_error "The file $ledir/$MYSQLD does not exist or is not executable. Please cd to the mysql installation directory and restart this script from there as follows: ./bin/mysqld_safe& See http://dev.mysql.com/doc/mysql/en/mysqld-safe.html for more information" exit 1 fi if test -z "$pid_file" then pid_file="$DATADIR/`hostname`.pid" else case "$pid_file" in /* ) ;; * ) pid_file="$DATADIR/$pid_file" ;; esac fi append_arg_to_args "--pid-file=$pid_file" if test -n "$mysql_unix_port" then append_arg_to_args "--socket=$mysql_unix_port" fi if test -n "$mysql_tcp_port" then append_arg_to_args "--port=$mysql_tcp_port" fi if test $niceness -eq 0 then NOHUP_NICENESS="nohup" else NOHUP_NICENESS="nohup nice -$niceness" fi # Using nice with no args to get the niceness level is GNU-specific. # This check could be extended for other operating systems (e.g., # BSD could use "nohup sh -c 'ps -o nice -p $$' | tail -1"). # But, it also seems that GNU nohup is the only one which messes # with the priority, so this is okay. if nohup nice > /dev/null 2>&1 then normal_niceness=`nice` nohup_niceness=`nohup nice 2>/dev/null` numeric_nice_values=1 for val in $normal_niceness $nohup_niceness do case "$val" in -[0-9] | -[0-9][0-9] | -[0-9][0-9][0-9] | \ [0-9] | [0-9][0-9] | [0-9][0-9][0-9] ) ;; * ) numeric_nice_values=0 ;; esac done if test $numeric_nice_values -eq 1 then nice_value_diff=`expr $nohup_niceness - $normal_niceness` if test $? -eq 0 && test $nice_value_diff -gt 0 && \ nice --$nice_value_diff echo testing > /dev/null 2>&1 then # nohup increases the priority (bad), and we are permitted # to lower the priority with respect to the value the user # might have been given niceness=`expr $niceness - $nice_value_diff` NOHUP_NICENESS="nice -$niceness nohup" fi fi else if nohup echo testing > /dev/null 2>&1 then : else # nohup doesn't work on this system NOHUP_NICENESS="" fi fi # Try to set the core file size (even if we aren't root) because many systems # don't specify a hard limit on core file size. if test -n "$core_file_size" then ulimit -c $core_file_size fi # # If there exists an old pid file, check if the daemon is already running # Note: The switches to 'ps' may depend on your operating system if test -f "$pid_file" then PID=`cat "$pid_file"` if kill -0 $PID > /dev/null 2> /dev/null then if ps wwwp $PID | grep -v mysqld_safe | grep -- $MYSQLD > /dev/null then # The pid contains a mysqld process log_error "A mysqld process already exists" exit 1 fi fi rm -f "$pid_file" if test -f "$pid_file" then log_error "Fatal error: Can't remove the pid file: $pid_file Please remove it manually and start $0 again; mysqld daemon not started" exit 1 fi fi # # From now on, we catch signals to do a proper shutdown of mysqld # when signalled to do so. # trap '/usr/bin/mysqladmin --defaults-extra-file=/etc/mysql/debian.cnf refresh & wait' 1 # HUP trap '/usr/bin/mysqladmin --defaults-extra-file=/etc/mysql/debian.cnf shutdown' 2 3 15 # INT QUIT and TERM # # Uncomment the following lines if you want all tables to be automatically # checked and repaired during startup. You should add sensible key_buffer # and sort_buffer values to my.cnf to improve check performance or require # less disk space. # Alternatively, you can start mysqld with the "myisam-recover" option. See # the manual for details. # # echo "Checking tables in $DATADIR" # $MY_BASEDIR_VERSION/bin/myisamchk --silent --force --fast --medium-check $DATADIR/*/*.MYI # $MY_BASEDIR_VERSION/bin/isamchk --silent --force $DATADIR/*/*.ISM # Does this work on all systems? #if type ulimit | grep "shell builtin" > /dev/null #then # ulimit -n 256 > /dev/null 2>&1 # Fix for BSD and FreeBSD systems #fi cmd="`mysqld_ld_preload_text`$NOHUP_NICENESS" for i in "$ledir/$MYSQLD" "$defaults" "--basedir=$MY_BASEDIR_VERSION" \ "--datadir=$DATADIR" "--plugin-dir=$plugin_dir" "$USER_OPTION" do cmd="$cmd "`shell_quote_string "$i"` done cmd="$cmd $args" # Avoid 'nohup: ignoring input' warning nohup_redir="" test -n "$NOHUP_NICENESS" && nohup_redir=" < /dev/null" log_notice "Starting $MYSQLD daemon with databases from $DATADIR" # variable to track the current number of "fast" (a.k.a. subsecond) restarts fast_restart=0 # maximum number of restarts before trottling kicks in max_fast_restarts=5 # flag whether a usable sleep command exists have_sleep=1 # maximum number of wsrep restarts max_wsrep_restarts=0 while true do rm -f $safe_mysql_unix_port "$pid_file" # Some extra safety start_time=`date +%M%S` # this sets wsrep_start_position_opt wsrep_recover_position "$cmd" [ $? -ne 0 ] && exit 1 # [ -n "$wsrep_urls" ] && url=`wsrep_pick_url $wsrep_urls` # check connect address if [ -z "$url" ] then eval_log_error "$cmd $wsrep_start_position_opt $nohup_redir" else eval_log_error "$cmd $wsrep_start_position_opt --wsrep_cluster_address=$url $nohup_redir" fi end_time=`date +%M%S` if test ! -f "$pid_file" # This is removed if normal shutdown then break fi # sanity check if time reading is sane and there's sleep if test $end_time -gt 0 -a $have_sleep -gt 0 then # throttle down the fast restarts if test $end_time -eq $start_time then fast_restart=`expr $fast_restart + 1` if test $fast_restart -ge $max_fast_restarts then log_notice "The server is respawning too fast. Sleeping for 1 second." sleep 1 sleep_state=$? if test $sleep_state -gt 0 then log_notice "The server is respawning too fast and no working sleep command. Turning off trottling." have_sleep=0 fi fast_restart=0 fi else fast_restart=0 fi fi if true && test $KILL_MYSQLD -eq 1 then # Test if one process was hanging. # This is only a fix for Linux (running as base 3 mysqld processes) # but should work for the rest of the servers. # The only thing is ps x => redhat 5 gives warnings when using ps -x. # kill -9 is used or the process won't react on the kill. numofproces=`ps xaww | grep -v "grep" | grep "$ledir/$MYSQLD\>" | grep -c "pid-file=$pid_file"` log_notice "Number of processes running now: $numofproces" I=1 while test "$I" -le "$numofproces" do PROC=`ps xaww | grep "$ledir/$MYSQLD\>" | grep -v "grep" | grep "pid-file=$pid_file" | sed -n '$p'` for T in $PROC do break done # echo "TEST $I - $T **" if kill -9 $T then log_error "$MYSQLD process hanging, pid $T - killed" else break fi I=`expr $I + 1` done fi if [ -n "$wsrep_restart" ] then if [ $wsrep_restart -le $max_wsrep_restarts ] then wsrep_restart=`expr $wsrep_restart + 1` log_notice "WSREP: sleeping 15 seconds before restart" sleep 15 else log_notice "WSREP: not restarting wsrep node automatically" break fi fi log_notice "mysqld restarted" done log_notice "mysqld from pid file $pid_file ended" percona-xtradb-cluster-galera/scripts/mysql/debian/etc/init.d/0000755000000000000000000000000012247075736024670 5ustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/mysql/debian/etc/logcheck/0000755000000000000000000000000012247075736025262 5ustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/mysql/debian/etc/logrotate.d/0000755000000000000000000000000012247075736025725 5ustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/mysql/debian/etc/mysql/0000755000000000000000000000000012247075736024650 5ustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/mysql/debian/etc/init.d/mysql0000755000000000000000000001250712247075736025770 0ustar rootroot00000000000000#!/bin/bash # ### BEGIN INIT INFO # Provides: mysql # Required-Start: $remote_fs $syslog # Required-Stop: $remote_fs $syslog # Should-Start: $network $named $time # Should-Stop: $network $named $time # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start and stop the mysql database server daemon # Description: Controls the main MySQL database server daemon "mysqld" # and its wrapper script "mysqld_safe". ### END INIT INFO # set -e set -u ${DEBIAN_SCRIPT_DEBUG:+ set -v -x} test -x /usr/sbin/mysqld || exit 0 . /lib/lsb/init-functions SELF=$(cd $(dirname $0); pwd -P)/$(basename $0) CONF=/etc/mysql/my.cnf MYADMIN="/usr/bin/mysqladmin --defaults-file=/etc/mysql/debian.cnf" # priority can be overriden and "-s" adds output to stderr ERR_LOGGER="logger -p daemon.err -t /etc/init.d/mysql -i" # Safeguard (relative paths, core dumps..) cd / umask 077 # mysqladmin likes to read /root/.my.cnf. This is usually not what I want # as many admins e.g. only store a password without a username there and # so break my scripts. export HOME=/etc/mysql/ ## Fetch a particular option from mysql's invocation. # # Usage: void mysqld_get_param option mysqld_get_param() { /usr/sbin/mysqld --print-defaults \ | tr " " "\n" \ | grep -- "--$1" \ | tail -n 1 \ | cut -d= -f2 } ## Do some sanity checks before even trying to start mysqld. sanity_checks() { # check for config file if [ ! -r /etc/mysql/my.cnf ]; then log_warning_msg "$0: WARNING: /etc/mysql/my.cnf cannot be read. See README.Debian.gz" echo "WARNING: /etc/mysql/my.cnf cannot be read. See README.Debian.gz" | $ERR_LOGGER fi # check for diskspace shortage datadir=`mysqld_get_param datadir` if LC_ALL=C BLOCKSIZE= df --portability $datadir/. | tail -n 1 | awk '{ exit ($4>4096) }'; then log_failure_msg "$0: ERROR: The partition with $datadir is too full!" echo "ERROR: The partition with $datadir is too full!" | $ERR_LOGGER exit 1 fi } ## Checks if there is a server running and if so if it is accessible. # # check_alive insists on a pingable server # check_dead also fails if there is a lost mysqld in the process list # # Usage: boolean mysqld_status [check_alive|check_dead] [warn|nowarn] mysqld_status () { ping_output=`$MYADMIN ping 2>&1`; ping_alive=$(( ! $? )) ps_alive=0 pidfile=`mysqld_get_param pid-file` if [ -f "$pidfile" ] && ps `cat $pidfile` >/dev/null 2>&1; then ps_alive=1; fi if [ "$1" = "check_alive" -a $ping_alive = 1 ] || [ "$1" = "check_dead" -a $ping_alive = 0 -a $ps_alive = 0 ]; then return 0 # EXIT_SUCCESS else if [ "$2" = "warn" ]; then echo -e "$ps_alive processes alive and '$MYADMIN ping' resulted in\n$ping_output\n" | $ERR_LOGGER -p daemon.debug fi return 1 # EXIT_FAILURE fi } # # main() # case "${1:-''}" in 'start') sanity_checks; # Start daemon log_daemon_msg "Starting MySQL database server" "mysqld" if mysqld_status check_alive nowarn; then log_progress_msg "already running" log_end_msg 0 else # Could be removed during boot test -e /var/run/mysqld || install -m 755 -o mysql -g root -d /var/run/mysqld # Start MySQL! /usr/bin/mysqld_safe > /dev/null 2>&1 & # 6s was reported in #352070 to be too few when using ndbcluster for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14; do sleep 1 if mysqld_status check_alive nowarn ; then break; fi log_progress_msg "." done if mysqld_status check_alive warn; then log_end_msg 0 # Now start mysqlcheck or whatever the admin wants. output=$(/etc/mysql/debian-start) [ -n "$output" ] && log_action_msg "$output" else log_end_msg 1 log_failure_msg "Please take a look at the syslog" fi fi ;; 'stop') # * As a passwordless mysqladmin (e.g. via ~/.my.cnf) must be possible # at least for cron, we can rely on it here, too. (although we have # to specify it explicit as e.g. sudo environments points to the normal # users home and not /root) log_daemon_msg "Stopping MySQL database server" "mysqld" if ! mysqld_status check_dead nowarn; then set +e shutdown_out=`$MYADMIN shutdown 2>&1`; r=$? set -e if [ "$r" -ne 0 ]; then log_end_msg 1 [ "$VERBOSE" != "no" ] && log_failure_msg "Error: $shutdown_out" log_daemon_msg "Killing MySQL database server by signal" "mysqld" killall -15 mysqld server_down= for i in 1 2 3 4 5 6 7 8 9 10; do sleep 1 if mysqld_status check_dead nowarn; then server_down=1; break; fi done if test -z "$server_down"; then killall -9 mysqld; fi fi fi if ! mysqld_status check_dead warn; then log_end_msg 1 log_failure_msg "Please stop MySQL manually and read /usr/share/doc/mysql-server-5.1/README.Debian.gz!" exit -1 else log_end_msg 0 fi ;; 'restart') set +e; $SELF stop; set -e $SELF start ;; 'reload'|'force-reload') log_daemon_msg "Reloading MySQL database server" "mysqld" $MYADMIN reload log_end_msg 0 ;; 'status') if mysqld_status check_alive nowarn; then log_action_msg "$($MYADMIN version)" else log_action_msg "MySQL is stopped." exit 3 fi ;; *) echo "Usage: $SELF start|stop|restart|reload|force-reload|status" exit 1 ;; esac percona-xtradb-cluster-galera/scripts/mysql/debian/etc/logcheck/ignore.d.paranoid/0000755000000000000000000000000012247075736030563 5ustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/mysql/debian/etc/logcheck/ignore.d.server/0000755000000000000000000000000012247075736030274 5ustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/mysql/debian/etc/logcheck/ignore.d.workstation/0000755000000000000000000000000012247075736031352 5ustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/mysql/debian/etc/logcheck/ignore.d.paranoid/mysql-server-5_10000644000000000000000000000131012247075736033534 0ustar rootroot00000000000000/etc/init.d/mysql\[[0-9]+\]: Check that mysqld is running and that the socket: '/var/run/mysqld/mysqld.sock' exists\!$ /etc/init.d/mysql\[[0-9]+\]: '/usr/bin/mysqladmin --defaults-(extra-)?file=/etc/mysql/debian.cnf ping' resulted in$ /etc/mysql/debian-start\[[0-9]+\]: Checking for crashed MySQL tables\.$ mysqld\[[0-9]+\]: $ mysqld\[[0-9]+\]: Version: .* socket: '/var/run/mysqld/mysqld.sock' port: 3306$ mysqld\[[0-9]+\]: Warning: Ignoring user change to 'mysql' because the user was set to 'mysql' earlier on the command line$ mysqld_safe\[[0-9]+\]: started$ usermod\[[0-9]+\]: change user `mysql' GID from `([0-9]+)' to `\1'$ usermod\[[0-9]+\]: change user `mysql' shell from `/bin/false' to `/bin/false'$ percona-xtradb-cluster-galera/scripts/mysql/debian/etc/logcheck/ignore.d.server/mysql-server-5_10000644000000000000000000000433612247075736033260 0ustar rootroot00000000000000/etc/init.d/mysql\[[0-9]+\]: [0-9]+ processes alive and '/usr/bin/mysqladmin --defaults-(extra-)?file=/etc/mysql/debian.cnf ping' resulted in$ /etc/init.d/mysql\[[0-9]+\]: Check that mysqld is running and that the socket: '/var/run/mysqld/mysqld.sock' exists\!$ /etc/init.d/mysql\[[0-9]+\]: '/usr/bin/mysqladmin --defaults-(extra-)?file=/etc/mysql/debian.cnf ping' resulted in$ /etc/mysql/debian-start\[[0-9]+\]: Checking for crashed MySQL tables\.$ mysqld\[[0-9]+\]: ?$ mysqld\[[0-9]+\]: .*InnoDB: Shutdown completed mysqld\[[0-9]+\]: .*InnoDB: Started; mysqld\[[0-9]+\]: .*InnoDB: Starting shutdown\.\.\.$ mysqld\[[0-9]+\]: .*\[Note\] /usr/sbin/mysqld: Normal shutdown$ mysqld\[[0-9]+\]: .*\[Note\] /usr/sbin/mysqld: ready for connections\.$ mysqld\[[0-9]+\]: .*\[Note\] /usr/sbin/mysqld: Shutdown complete$ mysqld\[[0-9]+\]: /usr/sbin/mysqld: ready for connections\.$ mysqld\[[0-9]+\]: .*/usr/sbin/mysqld: Shutdown Complete$ mysqld\[[0-9]+\]: Version: .* socket mysqld\[[0-9]+\]: Warning: Ignoring user change to 'mysql' because the user was set to 'mysql' earlier on the command line$ mysqld_safe\[[0-9]+\]: ?$ mysqld_safe\[[0-9]+\]: able to use the new GRANT command!$ mysqld_safe\[[0-9]+\]: ended$ mysqld_safe\[[0-9]+\]: http://www.mysql.com$ mysqld_safe\[[0-9]+\]: NOTE: If you are upgrading from a MySQL <= 3.22.10 you should run$ mysqld_safe\[[0-9]+\]: PLEASE REMEMBER TO SET A PASSWORD FOR THE MySQL root USER !$ mysqld_safe\[[0-9]+\]: Please report any problems with the /usr/bin/mysqlbug script!$ mysqld_safe\[[0-9]+\]: See the manual for more instructions.$ mysqld_safe\[[0-9]+\]: started$ mysqld_safe\[[0-9]+\]: Support MySQL by buying support/licenses at https://order.mysql.com$ mysqld_safe\[[0-9]+\]: The latest information about MySQL is available on the web at$ mysqld_safe\[[0-9]+\]: the /usr/bin/mysql_fix_privilege_tables. Otherwise you will not be$ mysqld_safe\[[0-9]+\]: To do so, start the server, then issue the following commands:$ mysqld_safe\[[0-9]+\]: /usr/bin/mysqladmin -u root -h app109 password 'new-password'$ mysqld_safe\[[0-9]+\]: /usr/bin/mysqladmin -u root password 'new-password'$ usermod\[[0-9]+\]: change user `mysql' GID from `([0-9]+)' to `\1'$ usermod\[[0-9]+\]: change user `mysql' shell from `/bin/false' to `/bin/false'$ ././@LongLink0000000000000000000000000000014600000000000013026 Lustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/mysql/debian/etc/logcheck/ignore.d.workstation/mysql-server-5_1percona-xtradb-cluster-galera/scripts/mysql/debian/etc/logcheck/ignore.d.workstation/mysql-server-5_0000644000000000000000000000433612247075736034255 0ustar rootroot00000000000000/etc/init.d/mysql\[[0-9]+\]: [0-9]+ processes alive and '/usr/bin/mysqladmin --defaults-(extra-)?file=/etc/mysql/debian.cnf ping' resulted in$ /etc/init.d/mysql\[[0-9]+\]: Check that mysqld is running and that the socket: '/var/run/mysqld/mysqld.sock' exists\!$ /etc/init.d/mysql\[[0-9]+\]: '/usr/bin/mysqladmin --defaults-(extra-)?file=/etc/mysql/debian.cnf ping' resulted in$ /etc/mysql/debian-start\[[0-9]+\]: Checking for crashed MySQL tables\.$ mysqld\[[0-9]+\]: ?$ mysqld\[[0-9]+\]: .*InnoDB: Shutdown completed mysqld\[[0-9]+\]: .*InnoDB: Started; mysqld\[[0-9]+\]: .*InnoDB: Starting shutdown\.\.\.$ mysqld\[[0-9]+\]: .*\[Note\] /usr/sbin/mysqld: Normal shutdown$ mysqld\[[0-9]+\]: .*\[Note\] /usr/sbin/mysqld: ready for connections\.$ mysqld\[[0-9]+\]: .*\[Note\] /usr/sbin/mysqld: Shutdown complete$ mysqld\[[0-9]+\]: /usr/sbin/mysqld: ready for connections\.$ mysqld\[[0-9]+\]: .*/usr/sbin/mysqld: Shutdown Complete$ mysqld\[[0-9]+\]: Version: .* socket mysqld\[[0-9]+\]: Warning: Ignoring user change to 'mysql' because the user was set to 'mysql' earlier on the command line$ mysqld_safe\[[0-9]+\]: ?$ mysqld_safe\[[0-9]+\]: able to use the new GRANT command!$ mysqld_safe\[[0-9]+\]: ended$ mysqld_safe\[[0-9]+\]: http://www.mysql.com$ mysqld_safe\[[0-9]+\]: NOTE: If you are upgrading from a MySQL <= 3.22.10 you should run$ mysqld_safe\[[0-9]+\]: PLEASE REMEMBER TO SET A PASSWORD FOR THE MySQL root USER !$ mysqld_safe\[[0-9]+\]: Please report any problems with the /usr/bin/mysqlbug script!$ mysqld_safe\[[0-9]+\]: See the manual for more instructions.$ mysqld_safe\[[0-9]+\]: started$ mysqld_safe\[[0-9]+\]: Support MySQL by buying support/licenses at https://order.mysql.com$ mysqld_safe\[[0-9]+\]: The latest information about MySQL is available on the web at$ mysqld_safe\[[0-9]+\]: the /usr/bin/mysql_fix_privilege_tables. Otherwise you will not be$ mysqld_safe\[[0-9]+\]: To do so, start the server, then issue the following commands:$ mysqld_safe\[[0-9]+\]: /usr/bin/mysqladmin -u root -h app109 password 'new-password'$ mysqld_safe\[[0-9]+\]: /usr/bin/mysqladmin -u root password 'new-password'$ usermod\[[0-9]+\]: change user `mysql' GID from `([0-9]+)' to `\1'$ usermod\[[0-9]+\]: change user `mysql' shell from `/bin/false' to `/bin/false'$ percona-xtradb-cluster-galera/scripts/mysql/debian/etc/logrotate.d/mysql-server0000644000000000000000000000150512247075736030322 0ustar rootroot00000000000000# - I put everything in one block and added sharedscripts, so that mysql gets # flush-logs'd only once. # Else the binary logs would automatically increase by n times every day. # - The error log is obsolete, messages go to syslog now. /var/log/mysql.log /var/log/mysql/mysql.log /var/log/mysql/mysql-slow.log { daily rotate 7 missingok create 640 mysql adm compress sharedscripts postrotate test -x /usr/bin/mysqladmin || exit 0 # If this fails, check debian.conf! MYADMIN="/usr/bin/mysqladmin --defaults-file=/etc/mysql/debian.cnf" if [ -z "`$MYADMIN ping 2>/dev/null`" ]; then # Really no mysqld or rather a missing debian-sys-maint user? # If this occurs and is not a error please report a bug. if ps cax | grep -q mysqld; then exit 1 fi else $MYADMIN flush-logs fi endscript } percona-xtradb-cluster-galera/scripts/mysql/debian/etc/mysql/conf.d/0000755000000000000000000000000012247075736026017 5ustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/mysql/debian/etc/mysql/debian-start0000755000000000000000000000225612247075736027160 0ustar rootroot00000000000000#!/bin/bash # # This script is executed by "/etc/init.d/mysql" on every (re)start. # # Changes to this file will be preserved when updating the Debian package. # source /usr/share/mysql/debian-start.inc.sh MYSQL="/usr/bin/mysql --defaults-file=/etc/mysql/debian.cnf" MYADMIN="/usr/bin/mysqladmin --defaults-file=/etc/mysql/debian.cnf" MYUPGRADE="/usr/bin/mysql_upgrade --defaults-extra-file=/etc/mysql/debian.cnf" MYCHECK="/usr/bin/mysqlcheck --defaults-file=/etc/mysql/debian.cnf" MYCHECK_SUBJECT="WARNING: mysqlcheck has found corrupt tables" MYCHECK_PARAMS="--all-databases --fast --silent" MYCHECK_RCPT="root" # The following commands should be run when the server is up but in background # where they do not block the server start and in one shell instance so that # they run sequentially. They are supposed not to echo anything to stdout. # If you want to disable the check for crashed tables comment # "check_for_crashed_tables" out. # (There may be no output to stdout inside the background process!) echo "Checking for corrupt, not cleanly closed and upgrade needing tables." ( upgrade_system_tables_if_necessary; check_root_accounts; check_for_crashed_tables; ) >&2 & exit 0 percona-xtradb-cluster-galera/scripts/mysql/debian/etc/mysql/conf.d/mysqld_safe_syslog.cnf0000644000000000000000000000002512247075736032413 0ustar rootroot00000000000000[mysqld_safe] syslog percona-xtradb-cluster-galera/scripts/mysql/freebsd/LICENSE0000644000000000000000000000012112247075736024117 0ustar rootroot00000000000000This package has a single license: GPLv3 (GNU General Public License version 3). percona-xtradb-cluster-galera/scripts/mysql/freebsd/catalog.mk0000644000000000000000000000037212247075736025065 0ustar rootroot00000000000000_LICENSE=GPLv3 _LICENSE_NAME=GNU General Public License version 3 _LICENSE_PERMS=dist-mirror dist-sell pkg-mirror pkg-sell auto-accept _LICENSE_GROUPS=FSF GPL OSI _LICENSE_DISTFILES=mysql-%{MYSQL_VER}.tar.gz mysql-${MYSQL_VER}_wsrep_${RELEASE}.patch percona-xtradb-cluster-galera/scripts/mysql/freebsd/client-comment0000644000000000000000000000006212247075736025757 0ustar rootroot00000000000000wsrep-enabled multithreaded SQL database (client) percona-xtradb-cluster-galera/scripts/mysql/freebsd/client-descr0000644000000000000000000000031512247075736025416 0ustar rootroot00000000000000MySQL is a very fast, multi-threaded, multi-user and robust SQL (Structured Query Language) database server. WWW: http://www.mysql.com/ Built with wsrep patch %{RELEASE}. WWW: http://www.codership.com/ percona-xtradb-cluster-galera/scripts/mysql/freebsd/client-ldconfig0000644000000000000000000000002512247075736026101 0ustar rootroot00000000000000/usr/local/lib/mysql percona-xtradb-cluster-galera/scripts/mysql/freebsd/client-message0000644000000000000000000000000012247075736025731 0ustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/mysql/freebsd/client-mtree0000644000000000000000000004223012247075736025434 0ustar rootroot00000000000000# $FreeBSD: /tmp/pcvs/ports/Templates/BSD.local.dist,v 1.3 2010-11-12 20:57:14 pav Exp $ # # Please see the file src/etc/mtree/README before making changes to this file. # /set type=dir uname=root gname=wheel mode=0755 . bin .. etc devd .. man.d .. pam.d .. rc.d .. .. include X11 .. .. info .. lib X11 app-defaults .. fonts local .. .. .. .. libdata ldconfig .. ldconfig32 .. pkgconfig .. .. libexec .. man /set uname=man cat1 .. cat2 .. cat3 .. cat4 .. cat5 .. cat6 .. cat7 .. cat8 .. cat9 .. catl .. catn .. de.ISO8859-1 uname=root cat1 .. cat2 .. cat3 .. cat4 .. cat5 .. cat6 .. cat7 .. cat8 .. cat9 .. catl .. catn .. /set uname=root man1 .. man2 .. man3 .. man4 .. man5 .. man6 .. man7 .. man8 .. man9 .. manl .. mann .. .. en.ISO8859-1 /set uname=man cat1 .. cat1aout .. cat2 .. cat3 .. cat4 i386 .. .. cat5 .. cat6 .. cat7 .. cat8 i386 .. .. cat9 i386 .. .. catn .. .. ja uname=root cat1 .. cat2 .. cat3 .. cat4 .. cat5 .. cat6 .. cat7 .. cat8 .. cat9 .. catl .. catn .. /set uname=root man1 .. man2 .. man3 .. man4 .. man5 .. man6 .. man7 .. man8 .. man9 .. manl .. mann .. .. man1 .. man2 .. man3 .. man4 .. man5 .. man6 .. man7 .. man8 .. man9 .. manl .. mann .. ru.KOI8-R /set uname=man cat1 .. cat2 .. cat3 .. cat4 .. cat5 .. cat6 .. cat7 .. cat8 .. cat9 .. catl .. catn .. /set uname=root man1 .. man2 .. man3 .. man4 .. man5 .. man6 .. man7 .. man8 .. man9 .. manl .. mann .. .. .. sbin .. share aclocal .. dict .. doc ja .. .. emacs site-lisp .. .. examples .. java classes .. .. locale af LC_MESSAGES .. .. am LC_MESSAGES .. .. ar LC_MESSAGES .. .. az LC_MESSAGES .. .. be LC_MESSAGES .. .. bg LC_MESSAGES .. .. bn LC_MESSAGES .. .. br LC_MESSAGES .. .. bs LC_MESSAGES .. .. ca LC_MESSAGES .. .. cs LC_MESSAGES .. .. cy LC_MESSAGES .. .. da LC_MESSAGES .. .. de LC_MESSAGES .. .. de_AT LC_MESSAGES .. .. dk LC_MESSAGES .. .. ee LC_MESSAGES .. .. el LC_MESSAGES .. .. en LC_MESSAGES .. .. en_AU LC_MESSAGES .. .. en_CA LC_MESSAGES .. .. en_GB LC_MESSAGES .. .. eo LC_MESSAGES .. .. es LC_MESSAGES .. .. es_ES LC_MESSAGES .. .. es_MX LC_MESSAGES .. .. et LC_MESSAGES .. .. eu LC_MESSAGES .. .. fa LC_MESSAGES .. .. fa_IR LC_MESSAGES .. .. fi LC_MESSAGES .. .. fr LC_MESSAGES .. .. fr_FR LC_MESSAGES .. .. ga LC_MESSAGES .. .. gl LC_MESSAGES .. .. gu LC_MESSAGES .. .. he LC_MESSAGES .. .. hi LC_MESSAGES .. .. hr LC_MESSAGES .. .. hu LC_MESSAGES .. .. id LC_MESSAGES .. .. is LC_MESSAGES .. .. it LC_MESSAGES .. .. ja LC_MESSAGES .. .. ka LC_MESSAGES .. .. kn LC_MESSAGES .. .. ko LC_MESSAGES .. .. li LC_MESSAGES .. .. lt LC_MESSAGES .. .. lv LC_MESSAGES .. .. mk LC_MESSAGES .. .. ml LC_MESSAGES .. .. mn LC_MESSAGES .. .. ms LC_MESSAGES .. .. mt LC_MESSAGES .. .. nb LC_MESSAGES .. .. ne LC_MESSAGES .. .. nl LC_MESSAGES .. .. nn LC_MESSAGES .. .. no LC_MESSAGES .. .. or LC_MESSAGES .. .. pa LC_MESSAGES .. .. pl LC_MESSAGES .. .. pt LC_MESSAGES .. .. pt_BR LC_MESSAGES .. .. pt_PT LC_MESSAGES .. .. ro LC_MESSAGES .. .. ru LC_MESSAGES .. .. sk LC_MESSAGES .. .. sl LC_MESSAGES .. .. sq LC_MESSAGES .. .. sr LC_MESSAGES .. .. sr@Latn LC_MESSAGES .. .. sv LC_MESSAGES .. .. ta LC_MESSAGES .. .. tg LC_MESSAGES .. .. th LC_MESSAGES .. .. tk LC_MESSAGES .. .. tr LC_MESSAGES .. .. uk LC_MESSAGES .. .. uz LC_MESSAGES .. .. vi LC_MESSAGES .. .. wa LC_MESSAGES .. .. zh LC_MESSAGES .. .. zh_CN LC_MESSAGES .. .. zh_CN.GB2312 LC_MESSAGES .. .. zh_TW LC_MESSAGES .. .. zh_TW.Big5 LC_MESSAGES .. .. .. misc .. nls C .. af_ZA.ISO8859-1 .. af_ZA.ISO8859-15 .. af_ZA.UTF-8 .. am_ET.UTF-8 .. be_BY.CP1131 .. be_BY.CP1251 .. be_BY.ISO8859-5 .. be_BY.UTF-8 .. bg_BG.CP1251 .. bg_BG.UTF-8 .. ca_ES.ISO8859-1 .. ca_ES.ISO8859-15 .. ca_ES.UTF-8 .. cs_CZ.ISO8859-2 .. cs_CZ.UTF-8 .. da_DK.ISO8859-1 .. da_DK.ISO8859-15 .. da_DK.UTF-8 .. de_AT.ISO8859-1 .. de_AT.ISO8859-15 .. de_AT.UTF-8 .. de_CH.ISO8859-1 .. de_CH.ISO8859-15 .. de_CH.UTF-8 .. de_DE.ISO8859-1 .. de_DE.ISO8859-15 .. de_DE.UTF-8 .. el_GR.ISO8859-7 .. el_GR.UTF-8 .. en_AU.ISO8859-1 .. en_AU.ISO8859-15 .. en_AU.US-ASCII .. en_AU.UTF-8 .. en_CA.ISO8859-1 .. en_CA.ISO8859-15 .. en_CA.US-ASCII .. en_CA.UTF-8 .. en_GB.ISO8859-1 .. en_GB.ISO8859-15 .. en_GB.US-ASCII .. en_GB.UTF-8 .. en_IE.UTF-8 .. en_NZ.ISO8859-1 .. en_NZ.ISO8859-15 .. en_NZ.US-ASCII .. en_NZ.UTF-8 .. en_US.ISO8859-1 .. en_US.ISO8859-15 .. en_US.UTF-8 .. es_ES.ISO8859-1 .. es_ES.ISO8859-15 .. es_ES.UTF-8 .. et_EE.ISO8859-15 .. et_EE.UTF-8 .. fi_FI.ISO8859-1 .. fi_FI.ISO8859-15 .. fi_FI.UTF-8 .. fr_BE.ISO8859-1 .. fr_BE.ISO8859-15 .. fr_BE.UTF-8 .. fr_CA.ISO8859-1 .. fr_CA.ISO8859-15 .. fr_CA.UTF-8 .. fr_CH.ISO8859-1 .. fr_CH.ISO8859-15 .. fr_CH.UTF-8 .. fr_FR.ISO8859-1 .. fr_FR.ISO8859-15 .. fr_FR.UTF-8 .. he_IL.UTF-8 .. hi_IN.ISCII-DEV .. hr_HR.ISO8859-2 .. hr_HR.UTF-8 .. hu_HU.ISO8859-2 .. hu_HU.UTF-8 .. hy_AM.ARMSCII-8 .. hy_AM.UTF-8 .. is_IS.ISO8859-1 .. is_IS.ISO8859-15 .. is_IS.UTF-8 .. it_CH.ISO8859-1 .. it_CH.ISO8859-15 .. it_CH.UTF-8 .. it_IT.ISO8859-1 .. it_IT.ISO8859-15 .. it_IT.UTF-8 .. ja_JP.SJIS .. ja_JP.UTF-8 .. ja_JP.eucJP .. kk_KZ.PT154 .. kk_KZ.UTF-8 .. ko_KR.CP949 .. ko_KR.UTF-8 .. ko_KR.eucKR .. la_LN.ISO8859-1 .. la_LN.ISO8859-15 .. la_LN.ISO8859-2 .. la_LN.ISO8859-4 .. la_LN.US-ASCII .. lt_LT.ISO8859-13 .. lt_LT.ISO8859-4 .. lt_LT.UTF-8 .. nl_BE.ISO8859-1 .. nl_BE.ISO8859-15 .. nl_BE.UTF-8 .. nl_NL.ISO8859-1 .. nl_NL.ISO8859-15 .. nl_NL.UTF-8 .. no_NO.ISO8859-1 .. no_NO.ISO8859-15 .. no_NO.UTF-8 .. pl_PL.ISO8859-2 .. pl_PL.UTF-8 .. pt_BR.ISO8859-1 .. pt_BR.UTF-8 .. pt_PT.ISO8859-1 .. pt_PT.ISO8859-15 .. pt_PT.UTF-8 .. ro_RO.ISO8859-2 .. ro_RO.UTF-8 .. ru_RU.CP1251 .. ru_RU.CP866 .. ru_RU.ISO8859-5 .. ru_RU.KOI8-R .. ru_RU.UTF-8 .. sk_SK.ISO8859-2 .. sk_SK.UTF-8 .. sl_SI.ISO8859-2 .. sl_SI.UTF-8 .. sr_YU.ISO8859-2 .. sr_YU.ISO8859-5 .. sr_YU.UTF-8 .. sv_SE.ISO8859-1 .. sv_SE.ISO8859-15 .. sv_SE.UTF-8 .. tr_TR.ISO8859-9 .. tr_TR.UTF-8 .. uk_UA.ISO8859-5 .. uk_UA.KOI8-U .. uk_UA.UTF-8 .. zh_CN.GB18030 .. zh_CN.GB2312 .. zh_CN.GBK .. zh_CN.UTF-8 .. zh_CN.eucCN .. zh_HK.Big5HKSCS .. zh_HK.UTF-8 .. zh_TW.Big5 .. zh_TW.UTF-8 .. .. pixmaps .. sgml .. skel .. xml .. .. www .. .. percona-xtradb-cluster-galera/scripts/mysql/freebsd/client-plist0000644000000000000000000000726412247075736025463 0ustar rootroot00000000000000@comment PKG_FORMAT_REVISION:1.1 @name mysql-client-%{MYSQL_VER}_wsrep_%{RELEASE} @comment ORIGIN:databases/mysql%{MAJORMINOR}-client_wsrep @cwd /usr/local @srcdir %{SRCDIR} @comment "=== dependencies ===" @comment "require /usr/local/lib/gcc48/libstdc++.so" @comment // @pkgdep gcc-4.8.2.s20130808 @comment DEPORIGIN:lang/gcc48 @comment // @pkgdep openssl-1.0.1_8 @comment DEPORIGIN:security/openssl @comment // @pkgdep libexecinfo-1.1_3 @comment DEPORIGIN:devel/libexecinfo @conflicts mysql-client-[34].* @conflicts mysql-wsrep-client-5.[0-46-9].* @conflicts mariadb-client-5.* @conflicts percona-client-5.* @comment "=== preinstall stage ===" @exec echo "===> Linking /usr/local/bin/bash to /bin/bash" @exec [ -x /bin/bash ] && echo "Using existing /bin/bash." || ln -s ../usr/local/bin/bash /bin/bash @comment "=== file section ===" @owner root @group wheel @mode 0444 share/licenses/mysql-client-%{MYSQL_VER}_wsrep_%{RELEASE}/catalog.mk share/licenses/mysql-client-%{MYSQL_VER}_wsrep_%{RELEASE}/LICENSE share/licenses/mysql-client-%{MYSQL_VER}_wsrep_%{RELEASE}/GPLv3 man/man1/comp_err.1.gz man/man1/msql2mysql.1.gz man/man1/mysql.1.gz man/man1/mysql_config.1.gz man/man1/mysql_find_rows.1.gz man/man1/mysql_waitpid.1.gz man/man1/mysqlaccess.1.gz man/man1/mysqladmin.1.gz man/man1/mysqlbinlog.1.gz man/man1/mysqlcheck.1.gz man/man1/mysqldump.1.gz man/man1/mysqlimport.1.gz man/man1/mysqlshow.1.gz man/man1/mysqlslap.1.gz @mode 0555 bin/msql2mysql bin/mysql bin/mysql_config bin/mysql_find_rows bin/mysql_waitpid bin/mysqlaccess bin/mysqlaccess.conf bin/mysqladmin bin/mysqlbinlog bin/mysqlcheck bin/mysqldump bin/mysqlimport bin/mysqlshow bin/mysqlslap @mode 0444 include/mysql/decimal.h include/mysql/errmsg.h include/mysql/keycache.h include/mysql/m_ctype.h include/mysql/m_string.h include/mysql/my_alloc.h include/mysql/my_attribute.h include/mysql/my_compiler.h include/mysql/my_config.h include/mysql/my_dbug.h include/mysql/my_dir.h include/mysql/my_getopt.h include/mysql/my_global.h include/mysql/my_list.h include/mysql/my_net.h include/mysql/my_pthread.h include/mysql/my_sys.h include/mysql/my_xml.h include/mysql/mysql/client_plugin.h include/mysql/mysql/innodb_priv.h include/mysql/mysql/plugin.h include/mysql/mysql/plugin_audit.h include/mysql/mysql/plugin_auth.h include/mysql/mysql/plugin_auth_common.h include/mysql/mysql/plugin_ftparser.h include/mysql/mysql/psi/mysql_file.h include/mysql/mysql/psi/mysql_thread.h include/mysql/mysql/psi/psi.h include/mysql/mysql/psi/psi_abi_v1.h include/mysql/mysql/psi/psi_abi_v2.h include/mysql/mysql/service_my_snprintf.h include/mysql/mysql/service_thd_alloc.h include/mysql/mysql/service_thd_wait.h include/mysql/mysql/service_thread_scheduler.h include/mysql/mysql/services.h include/mysql/mysql/thread_pool_priv.h include/mysql/mysql.h include/mysql/mysql_com.h include/mysql/mysql_embed.h include/mysql/mysql_time.h include/mysql/mysql_version.h include/mysql/mysqld_ername.h include/mysql/mysqld_error.h include/mysql/sql_common.h include/mysql/sql_state.h include/mysql/sslopt-case.h include/mysql/sslopt-longopts.h include/mysql/sslopt-vars.h include/mysql/typelib.h lib/mysql/libmysqlclient.a lib/mysql/libmysqlclient.so lib/mysql/libmysqlclient.so.18 lib/mysql/libmysqlclient_r.a lib/mysql/libmysqlclient_r.so lib/mysql/libmysqlclient_r.so.18 lib/mysql/libmysqlservices.a share/aclocal/mysql.m4 libdata/ldconfig/mysql%{MAJORMINOR}-client @comment "=== postinstall stage ===" @exec /sbin/ldconfig -m /usr/local/lib/mysql @comment "=== postremove stage ===" @dirrm share/licenses/mysql-client-%{MYSQL_VER}_wsrep_%{RELEASE} @dirrm include/mysql/mysql/psi @dirrm include/mysql/mysql @dirrm include/mysql @unexec rmdir "%D/lib/mysql" 2>/dev/null || true @unexec service ldconfig start >/dev/null percona-xtradb-cluster-galera/scripts/mysql/freebsd/mysql-server.sh0000755000000000000000000000504612247075736026135 0ustar rootroot00000000000000#!/bin/sh # # $FreeBSD: tags/RELEASE_9_1_0/databases/mysql55-server/files/mysql-server.in 302141 2012-08-05 23:19:36Z dougb $ # # PROVIDE: mysql # REQUIRE: LOGIN # KEYWORD: shutdown # # Add the following line to /etc/rc.conf to enable mysql: # mysql_enable (bool): Set to "NO" by default. # Set it to "YES" to enable MySQL. # mysql_limits (bool): Set to "NO" by default. # Set it to yes to run `limits -e -U mysql` # just before mysql starts. # mysql_dbdir (str): Default to "/var/db/mysql" # Base database directory. # mysql_pidfile (str): Custum PID file path and name. # Default to "${mysql_dbdir}/${hostname}.pid". # mysql_args (str): Custom additional arguments to be passed # to mysqld_safe (default empty). # . /etc/rc.subr name="mysql" rcvar=mysql_enable load_rc_config $name : ${mysql_enable="NO"} : ${mysql_limits="NO"} : ${mysql_dbdir="/var/db/mysql"} mysql_user="mysql" mysql_limits_args="-e -U ${mysql_user}" pidfile=${mysql_pidfile:-"${mysql_dbdir}/`/bin/hostname`.pid"} command="/usr/sbin/daemon" command_args="-c -f /usr/local/bin/mysqld_safe --defaults-extra-file=${mysql_dbdir}/my.cnf --user=${mysql_user} --datadir=${mysql_dbdir} --pid-file=${pidfile} ${mysql_args}" procname="/usr/local/libexec/mysqld" start_precmd="${name}_prestart" start_postcmd="${name}_poststart" mysql_install_db="/usr/local/bin/mysql_install_db" mysql_install_db_args="--basedir=/usr/local --datadir=${mysql_dbdir} --force" service_startup_timeout=900 startup_sleep=1 sst_progress_file=${mysql_dbdir}/sst_in_progress extra_commands="bootstrap" bootstrap_cmd="mysql_bootstrap" export LD_LIBRARY_PATH=/usr/local/lib/gcc44 mysql_bootstrap() { # Bootstrap the cluster, start the first node that initiate the cluster check_startmsgs && echo "Bootstrapping cluster" shift $0 start $rc_extra_args --wsrep-new-cluster } mysql_create_auth_tables() { eval $mysql_install_db $mysql_install_db_args >/dev/null 2>/dev/null [ $? -eq 0 ] && chown -R ${mysql_user}:${mysql_user} ${mysql_dbdir} } mysql_prestart() { if [ ! -d "${mysql_dbdir}/mysql/." ]; then mysql_create_auth_tables || return 1 fi if checkyesno mysql_limits; then eval `/usr/bin/limits ${mysql_limits_args}` 2>/dev/null else return 0 fi } mysql_poststart() { local timeout=${service_startup_timeout} while [ ! -f "${pidfile}" -a ${timeout} -gt 0 ]; do if test -e $sst_progress_file && [ $startup_sleep -ne 100 ]; then check_startmsgs && echo "SST in progress, setting sleep higher" startup_sleep=100 fi timeout=$(( timeout - 1 )) sleep $startup_sleep done return 0 } run_rc_command "$1" percona-xtradb-cluster-galera/scripts/mysql/freebsd/server-comment0000644000000000000000000000006212247075736026007 0ustar rootroot00000000000000wsrep-enabled multithreaded SQL database (server) percona-xtradb-cluster-galera/scripts/mysql/freebsd/server-descr0000644000000000000000000000031512247075736025446 0ustar rootroot00000000000000MySQL is a very fast, multi-threaded, multi-user and robust SQL (Structured Query Language) database server. WWW: http://www.mysql.com/ Built with wsrep patch %{RELEASE}. WWW: http://www.codership.com/ percona-xtradb-cluster-galera/scripts/mysql/freebsd/server-message0000644000000000000000000000040612247075736025773 0ustar rootroot00000000000000************************************************************************ Remember to run mysql_upgrade the first time you start the MySQL server after an upgrade from an earlier version. ************************************************************************ percona-xtradb-cluster-galera/scripts/mysql/freebsd/server-mtree0000644000000000000000000004223012247075736025464 0ustar rootroot00000000000000# $FreeBSD: /tmp/pcvs/ports/Templates/BSD.local.dist,v 1.3 2010-11-12 20:57:14 pav Exp $ # # Please see the file src/etc/mtree/README before making changes to this file. # /set type=dir uname=root gname=wheel mode=0755 . bin .. etc devd .. man.d .. pam.d .. rc.d .. .. include X11 .. .. info .. lib X11 app-defaults .. fonts local .. .. .. .. libdata ldconfig .. ldconfig32 .. pkgconfig .. .. libexec .. man /set uname=man cat1 .. cat2 .. cat3 .. cat4 .. cat5 .. cat6 .. cat7 .. cat8 .. cat9 .. catl .. catn .. de.ISO8859-1 uname=root cat1 .. cat2 .. cat3 .. cat4 .. cat5 .. cat6 .. cat7 .. cat8 .. cat9 .. catl .. catn .. /set uname=root man1 .. man2 .. man3 .. man4 .. man5 .. man6 .. man7 .. man8 .. man9 .. manl .. mann .. .. en.ISO8859-1 /set uname=man cat1 .. cat1aout .. cat2 .. cat3 .. cat4 i386 .. .. cat5 .. cat6 .. cat7 .. cat8 i386 .. .. cat9 i386 .. .. catn .. .. ja uname=root cat1 .. cat2 .. cat3 .. cat4 .. cat5 .. cat6 .. cat7 .. cat8 .. cat9 .. catl .. catn .. /set uname=root man1 .. man2 .. man3 .. man4 .. man5 .. man6 .. man7 .. man8 .. man9 .. manl .. mann .. .. man1 .. man2 .. man3 .. man4 .. man5 .. man6 .. man7 .. man8 .. man9 .. manl .. mann .. ru.KOI8-R /set uname=man cat1 .. cat2 .. cat3 .. cat4 .. cat5 .. cat6 .. cat7 .. cat8 .. cat9 .. catl .. catn .. /set uname=root man1 .. man2 .. man3 .. man4 .. man5 .. man6 .. man7 .. man8 .. man9 .. manl .. mann .. .. .. sbin .. share aclocal .. dict .. doc ja .. .. emacs site-lisp .. .. examples .. java classes .. .. locale af LC_MESSAGES .. .. am LC_MESSAGES .. .. ar LC_MESSAGES .. .. az LC_MESSAGES .. .. be LC_MESSAGES .. .. bg LC_MESSAGES .. .. bn LC_MESSAGES .. .. br LC_MESSAGES .. .. bs LC_MESSAGES .. .. ca LC_MESSAGES .. .. cs LC_MESSAGES .. .. cy LC_MESSAGES .. .. da LC_MESSAGES .. .. de LC_MESSAGES .. .. de_AT LC_MESSAGES .. .. dk LC_MESSAGES .. .. ee LC_MESSAGES .. .. el LC_MESSAGES .. .. en LC_MESSAGES .. .. en_AU LC_MESSAGES .. .. en_CA LC_MESSAGES .. .. en_GB LC_MESSAGES .. .. eo LC_MESSAGES .. .. es LC_MESSAGES .. .. es_ES LC_MESSAGES .. .. es_MX LC_MESSAGES .. .. et LC_MESSAGES .. .. eu LC_MESSAGES .. .. fa LC_MESSAGES .. .. fa_IR LC_MESSAGES .. .. fi LC_MESSAGES .. .. fr LC_MESSAGES .. .. fr_FR LC_MESSAGES .. .. ga LC_MESSAGES .. .. gl LC_MESSAGES .. .. gu LC_MESSAGES .. .. he LC_MESSAGES .. .. hi LC_MESSAGES .. .. hr LC_MESSAGES .. .. hu LC_MESSAGES .. .. id LC_MESSAGES .. .. is LC_MESSAGES .. .. it LC_MESSAGES .. .. ja LC_MESSAGES .. .. ka LC_MESSAGES .. .. kn LC_MESSAGES .. .. ko LC_MESSAGES .. .. li LC_MESSAGES .. .. lt LC_MESSAGES .. .. lv LC_MESSAGES .. .. mk LC_MESSAGES .. .. ml LC_MESSAGES .. .. mn LC_MESSAGES .. .. ms LC_MESSAGES .. .. mt LC_MESSAGES .. .. nb LC_MESSAGES .. .. ne LC_MESSAGES .. .. nl LC_MESSAGES .. .. nn LC_MESSAGES .. .. no LC_MESSAGES .. .. or LC_MESSAGES .. .. pa LC_MESSAGES .. .. pl LC_MESSAGES .. .. pt LC_MESSAGES .. .. pt_BR LC_MESSAGES .. .. pt_PT LC_MESSAGES .. .. ro LC_MESSAGES .. .. ru LC_MESSAGES .. .. sk LC_MESSAGES .. .. sl LC_MESSAGES .. .. sq LC_MESSAGES .. .. sr LC_MESSAGES .. .. sr@Latn LC_MESSAGES .. .. sv LC_MESSAGES .. .. ta LC_MESSAGES .. .. tg LC_MESSAGES .. .. th LC_MESSAGES .. .. tk LC_MESSAGES .. .. tr LC_MESSAGES .. .. uk LC_MESSAGES .. .. uz LC_MESSAGES .. .. vi LC_MESSAGES .. .. wa LC_MESSAGES .. .. zh LC_MESSAGES .. .. zh_CN LC_MESSAGES .. .. zh_CN.GB2312 LC_MESSAGES .. .. zh_TW LC_MESSAGES .. .. zh_TW.Big5 LC_MESSAGES .. .. .. misc .. nls C .. af_ZA.ISO8859-1 .. af_ZA.ISO8859-15 .. af_ZA.UTF-8 .. am_ET.UTF-8 .. be_BY.CP1131 .. be_BY.CP1251 .. be_BY.ISO8859-5 .. be_BY.UTF-8 .. bg_BG.CP1251 .. bg_BG.UTF-8 .. ca_ES.ISO8859-1 .. ca_ES.ISO8859-15 .. ca_ES.UTF-8 .. cs_CZ.ISO8859-2 .. cs_CZ.UTF-8 .. da_DK.ISO8859-1 .. da_DK.ISO8859-15 .. da_DK.UTF-8 .. de_AT.ISO8859-1 .. de_AT.ISO8859-15 .. de_AT.UTF-8 .. de_CH.ISO8859-1 .. de_CH.ISO8859-15 .. de_CH.UTF-8 .. de_DE.ISO8859-1 .. de_DE.ISO8859-15 .. de_DE.UTF-8 .. el_GR.ISO8859-7 .. el_GR.UTF-8 .. en_AU.ISO8859-1 .. en_AU.ISO8859-15 .. en_AU.US-ASCII .. en_AU.UTF-8 .. en_CA.ISO8859-1 .. en_CA.ISO8859-15 .. en_CA.US-ASCII .. en_CA.UTF-8 .. en_GB.ISO8859-1 .. en_GB.ISO8859-15 .. en_GB.US-ASCII .. en_GB.UTF-8 .. en_IE.UTF-8 .. en_NZ.ISO8859-1 .. en_NZ.ISO8859-15 .. en_NZ.US-ASCII .. en_NZ.UTF-8 .. en_US.ISO8859-1 .. en_US.ISO8859-15 .. en_US.UTF-8 .. es_ES.ISO8859-1 .. es_ES.ISO8859-15 .. es_ES.UTF-8 .. et_EE.ISO8859-15 .. et_EE.UTF-8 .. fi_FI.ISO8859-1 .. fi_FI.ISO8859-15 .. fi_FI.UTF-8 .. fr_BE.ISO8859-1 .. fr_BE.ISO8859-15 .. fr_BE.UTF-8 .. fr_CA.ISO8859-1 .. fr_CA.ISO8859-15 .. fr_CA.UTF-8 .. fr_CH.ISO8859-1 .. fr_CH.ISO8859-15 .. fr_CH.UTF-8 .. fr_FR.ISO8859-1 .. fr_FR.ISO8859-15 .. fr_FR.UTF-8 .. he_IL.UTF-8 .. hi_IN.ISCII-DEV .. hr_HR.ISO8859-2 .. hr_HR.UTF-8 .. hu_HU.ISO8859-2 .. hu_HU.UTF-8 .. hy_AM.ARMSCII-8 .. hy_AM.UTF-8 .. is_IS.ISO8859-1 .. is_IS.ISO8859-15 .. is_IS.UTF-8 .. it_CH.ISO8859-1 .. it_CH.ISO8859-15 .. it_CH.UTF-8 .. it_IT.ISO8859-1 .. it_IT.ISO8859-15 .. it_IT.UTF-8 .. ja_JP.SJIS .. ja_JP.UTF-8 .. ja_JP.eucJP .. kk_KZ.PT154 .. kk_KZ.UTF-8 .. ko_KR.CP949 .. ko_KR.UTF-8 .. ko_KR.eucKR .. la_LN.ISO8859-1 .. la_LN.ISO8859-15 .. la_LN.ISO8859-2 .. la_LN.ISO8859-4 .. la_LN.US-ASCII .. lt_LT.ISO8859-13 .. lt_LT.ISO8859-4 .. lt_LT.UTF-8 .. nl_BE.ISO8859-1 .. nl_BE.ISO8859-15 .. nl_BE.UTF-8 .. nl_NL.ISO8859-1 .. nl_NL.ISO8859-15 .. nl_NL.UTF-8 .. no_NO.ISO8859-1 .. no_NO.ISO8859-15 .. no_NO.UTF-8 .. pl_PL.ISO8859-2 .. pl_PL.UTF-8 .. pt_BR.ISO8859-1 .. pt_BR.UTF-8 .. pt_PT.ISO8859-1 .. pt_PT.ISO8859-15 .. pt_PT.UTF-8 .. ro_RO.ISO8859-2 .. ro_RO.UTF-8 .. ru_RU.CP1251 .. ru_RU.CP866 .. ru_RU.ISO8859-5 .. ru_RU.KOI8-R .. ru_RU.UTF-8 .. sk_SK.ISO8859-2 .. sk_SK.UTF-8 .. sl_SI.ISO8859-2 .. sl_SI.UTF-8 .. sr_YU.ISO8859-2 .. sr_YU.ISO8859-5 .. sr_YU.UTF-8 .. sv_SE.ISO8859-1 .. sv_SE.ISO8859-15 .. sv_SE.UTF-8 .. tr_TR.ISO8859-9 .. tr_TR.UTF-8 .. uk_UA.ISO8859-5 .. uk_UA.KOI8-U .. uk_UA.UTF-8 .. zh_CN.GB18030 .. zh_CN.GB2312 .. zh_CN.GBK .. zh_CN.UTF-8 .. zh_CN.eucCN .. zh_HK.Big5HKSCS .. zh_HK.UTF-8 .. zh_TW.Big5 .. zh_TW.UTF-8 .. .. pixmaps .. sgml .. skel .. xml .. .. www .. .. percona-xtradb-cluster-galera/scripts/mysql/freebsd/server-plist0000644000000000000000000001720212247075736025504 0ustar rootroot00000000000000@comment PKG_FORMAT_REVISION:1.1 @name mysql-server-%{MYSQL_VER}_wsrep_%{RELEASE} @comment ORIGIN:databases/mysql%{MAJORMINOR}-server_wsrep @cwd /usr/local @srcdir %{SRCDIR} @comment "=== dependencies ===" @comment "require /usr/local/lib/gcc48/libstdc++.so" @comment // @pkgdep gcc-4.8.2.s20130808 @comment DEPORIGIN:lang/gcc48 @comment // @pkgdep openssl-1.0.1_8 @comment DEPORIGIN:security/openssl @comment // @pkgdep libexecinfo-1.1_3 @comment DEPORIGIN:devel/libexecinfo @comment // @pkgdep lsof-4.88.d,8 @comment DEPORIGIN:sysutils/lsof @comment // @pkgdep sudo-1.8.7_1 @comment DEPORIGIN:security/sudo @comment // @pkgdep rsync-3.0.9_3 @comment DEPORIGIN:net/rsync @pkgdep mysql-client-%{MYSQL_VER}_wsrep_%{RELEASE} @comment DEPORIGIN:databases/mysql%{MAJORMINOR}-client_wsrep @conflicts mysql-server-[34].* @conflicts mysql-server-5.[0-46-9].* @conflicts mariadb-server-5.* @conflicts percona-server-5.* @comment "=== preinstall stage ===" @exec echo "===> Linking /usr/local/bin/bash to /bin/bash" @exec [ -x /bin/bash ] && echo "Using existing /bin/bash." || ln -s ../usr/local/bin/bash /bin/bash @exec echo "===> Creating users and/or groups." @exec if ! /usr/sbin/pw groupshow mysql >/dev/null 2>&1; then echo "Creating group 'mysql' with gid '88'."; /usr/sbin/pw groupadd mysql -g 88; else echo "Using existing group 'mysql'."; fi @exec if ! /usr/sbin/pw usershow mysql >/dev/null 2>&1; then echo "Creating user 'mysql' with uid '88'."; /usr/sbin/pw useradd mysql -u 88 -g 88 -c "MySQL Daemon" -d /var/db/mysql -s /usr/sbin/nologin; else echo "Using existing user 'mysql'."; fi @exec install -d -g 88 -o 88 /var/db/mysql @comment "=== preremove stage ===" @unexec %D/etc/rc.d/mysql-server forcestop 2>/dev/null || true @comment "=== file section ===" @owner root @group wheel @mode 0444 share/licenses/mysql-server-%{MYSQL_VER}_wsrep_%{RELEASE}/catalog.mk share/licenses/mysql-server-%{MYSQL_VER}_wsrep_%{RELEASE}/LICENSE share/licenses/mysql-server-%{MYSQL_VER}_wsrep_%{RELEASE}/GPLv3 @comment "added: man/man1/innochecksum.1.gz" man/man1/innochecksum.1.gz man/man1/my_print_defaults.1.gz man/man1/myisam_ftdump.1.gz man/man1/myisamchk.1.gz man/man1/myisamlog.1.gz man/man1/myisampack.1.gz man/man1/mysql.server.1.gz man/man1/mysql_convert_table_format.1.gz man/man1/mysql_fix_extensions.1.gz man/man1/mysql_install_db.1.gz man/man1/mysql_plugin.1.gz man/man1/mysql_secure_installation.1.gz man/man1/mysql_setpermission.1.gz man/man1/mysql_tzinfo_to_sql.1.gz man/man1/mysql_upgrade.1.gz man/man1/mysql_zap.1.gz man/man1/mysqlbug.1.gz man/man1/mysqld_multi.1.gz man/man1/mysqld_safe.1.gz man/man1/mysqldumpslow.1.gz man/man1/mysqlhotcopy.1.gz man/man1/mysqlman.1.gz man/man1/mysqltest.1.gz man/man1/perror.1.gz man/man1/replace.1.gz man/man1/resolve_stack_dump.1.gz man/man1/resolveip.1.gz man/man8/mysqld.8.gz @mode 0555 bin/innochecksum bin/my_print_defaults bin/myisam_ftdump bin/myisamchk bin/myisamlog bin/myisampack bin/mysql_convert_table_format bin/mysql_fix_extensions bin/mysql_install_db bin/mysql_plugin bin/mysql_secure_installation bin/mysql_setpermission bin/mysql_tzinfo_to_sql bin/mysql_upgrade bin/mysql_zap bin/mysqlbug bin/mysqld_multi bin/mysqld_safe bin/mysqldumpslow bin/mysqlhotcopy bin/mysqltest bin/perror bin/replace bin/resolve_stack_dump bin/resolveip @mode 0444 @ignore lib/mysql/libmysqld.a lib/mysql/plugin/adt_null.so lib/mysql/plugin/auth.so lib/mysql/plugin/auth_test_plugin.so lib/mysql/plugin/daemon_example.ini @ignore lib/mysql/plugin/ha_archive.so @ignore lib/mysql/plugin/ha_blackhole.so @ignore lib/mysql/plugin/ha_example.so @ignore lib/mysql/plugin/ha_federated.so lib/mysql/plugin/libdaemon_example.so lib/mysql/plugin/mypluglib.so lib/mysql/plugin/qa_auth_client.so lib/mysql/plugin/qa_auth_interface.so lib/mysql/plugin/qa_auth_server.so lib/mysql/plugin/semisync_master.so lib/mysql/plugin/semisync_slave.so @mode 0555 libexec/mysqld share/mysql/binary-configure @mode 0444 share/mysql/charsets/Index.xml share/mysql/charsets/README share/mysql/charsets/armscii8.xml share/mysql/charsets/ascii.xml share/mysql/charsets/cp1250.xml share/mysql/charsets/cp1251.xml share/mysql/charsets/cp1256.xml share/mysql/charsets/cp1257.xml share/mysql/charsets/cp850.xml share/mysql/charsets/cp852.xml share/mysql/charsets/cp866.xml share/mysql/charsets/dec8.xml share/mysql/charsets/geostd8.xml share/mysql/charsets/greek.xml share/mysql/charsets/hebrew.xml share/mysql/charsets/hp8.xml share/mysql/charsets/keybcs2.xml share/mysql/charsets/koi8r.xml share/mysql/charsets/koi8u.xml share/mysql/charsets/latin1.xml share/mysql/charsets/latin2.xml share/mysql/charsets/latin5.xml share/mysql/charsets/latin7.xml share/mysql/charsets/macce.xml share/mysql/charsets/macroman.xml share/mysql/charsets/swe7.xml share/mysql/config.huge.ini share/mysql/config.medium.ini share/mysql/config.small.ini share/mysql/czech/errmsg.sys share/mysql/danish/errmsg.sys share/mysql/dutch/errmsg.sys share/mysql/english/errmsg.sys share/mysql/errmsg-utf8.txt share/mysql/estonian/errmsg.sys share/mysql/fill_help_tables.sql share/mysql/french/errmsg.sys share/mysql/german/errmsg.sys share/mysql/greek/errmsg.sys share/mysql/hungarian/errmsg.sys share/mysql/italian/errmsg.sys share/mysql/japanese/errmsg.sys share/mysql/korean/errmsg.sys share/mysql/magic share/mysql/my-huge.cnf share/mysql/my-innodb-heavy-4G.cnf share/mysql/my-large.cnf share/mysql/my-medium.cnf share/mysql/my-small.cnf share/mysql/mysql-log-rotate share/mysql/mysql.server share/mysql/mysql_system_tables.sql share/mysql/mysql_system_tables_data.sql share/mysql/mysql_test_data_timezone.sql share/mysql/mysqld_multi.server share/mysql/ndb-config-2-node.ini share/mysql/norwegian-ny/errmsg.sys share/mysql/norwegian/errmsg.sys share/mysql/polish/errmsg.sys share/mysql/portuguese/errmsg.sys share/mysql/romanian/errmsg.sys share/mysql/russian/errmsg.sys share/mysql/serbian/errmsg.sys share/mysql/slovak/errmsg.sys share/mysql/spanish/errmsg.sys share/mysql/swedish/errmsg.sys share/mysql/ukrainian/errmsg.sys @comment "wsrep specific @mode 0555 bin/wsrep_sst_common bin/wsrep_sst_mysqldump bin/wsrep_sst_rsync bin/wsrep_sst_rsync_wan bin/wsrep_sst_xtrabackup @mode 0444 share/mysql/my_wsrep.cnf share/mysql/wsrep_notify share/doc/mysql%{MAJORMINOR}-server_wsrep/README share/doc/mysql%{MAJORMINOR}-server_wsrep/QUICK_START @mode 0555 etc/rc.d/mysql-server info/mysql.info @comment "=== postinstall stage ===" @exec install-info --quiet %D/info/mysql.info %D/info/dir @comment "=== postremove stage ===" @dirrm share/licenses/mysql-server-%{MYSQL_VER}_wsrep_%{RELEASE} @dirrm lib/mysql/plugin @unexec rmdir "%D/lib/mysql" 2>/dev/null || true @dirrm share/mysql/charsets @dirrm share/mysql/czech @dirrm share/mysql/danish @dirrm share/mysql/dutch @dirrm share/mysql/english @dirrm share/mysql/estonian @dirrm share/mysql/french @dirrm share/mysql/german @dirrm share/mysql/greek @dirrm share/mysql/hungarian @dirrm share/mysql/italian @dirrm share/mysql/japanese @dirrm share/mysql/korean @dirrm share/mysql/norwegian @dirrm share/mysql/norwegian-ny @dirrm share/mysql/polish @dirrm share/mysql/portuguese @dirrm share/mysql/romanian @dirrm share/mysql/russian @dirrm share/mysql/serbian @dirrm share/mysql/slovak @dirrm share/mysql/spanish @dirrm share/mysql/swedish @dirrm share/mysql/ukrainian @dirrm share/mysql @unexec [ -f %D/info/mysql.info ] && install-info --quiet --delete %D/info/mysql.info %D/info/dir || true @unexec if [ -f %D/info/dir ]; then if sed -e '1,/Menu:/d' %D/info/dir | grep -q '^[*] '; then true; else rm %D/info/dir; fi; fi @unexec if /usr/sbin/pw user show mysql >/dev/null 2>&1; then echo "==> You should manually remove the \"mysql\" user. "; fi @unexec if /usr/sbin/pw group show mysql >/dev/null 2>&1; then echo "==> You should manually remove the \"mysql\" group. "; fi percona-xtradb-cluster-galera/scripts/openrep/common0000644000000000000000000000024212247075736023222 0ustar rootroot00000000000000set -x MYSQL_BIN=${MYSQL_BIN:-"$MYSQL_ROOT/libexec/mysqld"} MYSQL_DATA_DIR=${MYSQL_DATA_DIR:-"$MYSQL_ROOT/var"} MYSQL_CNF=${MYSQL_CNF:-"$MYSQL_ROOT/etc/my.cnf"} percona-xtradb-cluster-galera/scripts/openrep/configure0000755000000000000000000000000012247075736025615 2redundantustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/openrep/demote0000755000000000000000000000000012247075736025111 2redundantustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/openrep/flush0000755000000000000000000000000012247075736025676 2not_supportedustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/openrep/halt0000755000000000000000000000000012247075736024240 2serviceustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/openrep/kill0000755000000000000000000000000012247075736024243 2serviceustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/openrep/not_supported0000755000000000000000000000021112247075736024636 0ustar rootroot00000000000000#!/bin/sh # Copuright (C) 2009 Codership Oy echo "Operation not supported: $(basename $0 | sed s/\.sh//)" exit 0 percona-xtradb-cluster-galera/scripts/openrep/offline0000755000000000000000000000000012247075736024732 2serviceustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/openrep/online0000755000000000000000000000000012247075736024574 2serviceustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/openrep/plugin.cnf0000644000000000000000000000122112247075736023773 0ustar rootroot00000000000000MYSQL_BASE_DIR= # MYSQLD= # MYSQL_DATA_DIR= # MYSQL_CNF= MYSQL_ROOT_USER=root MYSQL_ROOT_PSWD=rootpass WSREP_PROVIDER= WSREP_CLUSTER_ADDRESS="dummy://" # WSREP_CLUSTER_NAME= # WSREP_NODE_NAME= # WSREP_NODE_INCOMING_ADDRESS= WSREP_SST_METHOD=mysqldump WSREP_SST_AUTH=$MYSQL_ROOT_USER:$MYSQL_ROOT_PSWD # WSREP_SST_ADDRESS= # WSREP_SST_DONOR="" # # Parameters below rarely require changing # WSREP_SLAVE_THREADS=1 WSREP_LOCAL_CACHE_SIZE=20971520 WSREP_START_POSITION= WSREP_DEBUG=0 WSREP_AUTO_INCREMENT_CONTROL=1 WSREP_RETRY_AUTOCOMMIT=1 WSREP_CONVERT_LOCK_TO_TRX=1 WSREP_DRUPAL_282555_WORKAROUND=1 WSREP_WS_PERSISTENCY=0 # WSREP_DBUG= # WSREP_DATA_HOME_DIR= percona-xtradb-cluster-galera/scripts/openrep/prepare0000755000000000000000000000000012247075736025272 2redundantustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/openrep/promote0000755000000000000000000000000012247075736025321 2redundantustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/openrep/provision0000755000000000000000000000000012247075736026605 2not_supportedustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/openrep/redundant0000755000000000000000000000021012247075736023714 0ustar rootroot00000000000000#!/bin/sh # Copuright (C) 2009 Codership Oy echo "Operation is redundant: $(basename $0 | sed s/\.sh//)" exit 0 percona-xtradb-cluster-galera/scripts/openrep/release0000755000000000000000000000000012247075736026175 2not_supportedustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/openrep/service0000755000000000000000000001446712247075736023413 0ustar rootroot00000000000000#!/bin/bash -x # Copyright (C) 2009 Codership Oy PLUGIN_BASE_DIR=$(cd $(dirname $0); pwd -P) SELF=$PLUGIN_BASE_DIR/$(basename $0) WSREP_CLUSTER_NAME=${1:-"tor_galera_cluster"} WSREP_NODE_NAME=${2:-"$(hostname)"} PLUGIN_CONF=${3:-"$PLUGIN_BASE_DIR/plugin.cnf"} . $PLUGIN_CONF #=============== Fall back to reasonable defaults ======================= # MySQL configuration file MYSQL_CNF=${MYSQL_CNF:-"$MYSQL_BASE_DIR/etc/my.cnf"} if test -s "$MYSQL_CNF" then DEFAULTS_OPTION=" --defaults-file=$MYSQL_CNF " my_cnf_datadir=$(grep ^datadir $MYSQL_CNF | sed s/[^/]*//) else DEFAULTS_OPTION=" --no-defaults " fi # If it was not given explicitely, use the one from my.cnf MYSQL_DATA_DIR=${MYSQL_DATA_DIR:-"$my_cnf_datadir"} # If it was not found in my.cnf, use distribution default MYSQL_DATA_DIR=${MYSQL_DATA_DIR:-"$MYSQL_BASE_DIR/var"} # use mysqld server directly, better not have automatic restarting MYSQLD=${MYSQLD:-"$MYSQL_BASE_DIR/libexec/mysqld"} MYSQLADMIN=${MYSQLADMIN:-"$MYSQL_BASE_DIR/bin/mysqladmin"} # Port, socket and pid files MYSQL_PORT=${MYSQL_PORT:-3306} MYSQL_SOCKET=${MYSQL_SOCKET:-"$MYSQL_DATA_DIR/mysqld.sock"} MYSQL_PID=${MYSQL_PID:-"$MYSQL_DATA_DIR/mysqld.pid"} # Shutdown wait timeout. MYSQL_SHUTDOWN_WAIT=60 #============= Nothing servicable below ================================ # User to run as if started under superuser MYSQLD_USER=$(whoami) if test "$MYSQLD_USER" = "root" then MYSQLD_USER=mysql fi #ROOT_USER=${ROOT_USER:-"-uroot"} #ROOT_PSWD=${ROOT_PSWD:-"-prootpass"} #mysql_log="$MYSQL_DATA_DIR/$(hostname).log" usage() { cat - << EOF usage: service Commands: check : check cosistency either locally or through network start : start servers stop : stop servers restart : stop and start servers status : show running servers EOF } # Checks if a process with a given PID is still running find_pid() { ps axc | grep mysqld | grep -w ^\ *$1 > /dev/null } galera_start() { local failed if ! test -x $MYSQLD then echo "$MYSQLD executable not found" exit -1 fi if test -f $MYSQL_PID then echo "Found existing '$MYSQL_PID'. Please run '$0 stop'" exit -1; fi if test -f $WSREP_PROVIDER || test $WSREP_PROVIDER == "none" then WSREP_OPTS="$WSREP_OPTS --wsrep_provider=$WSREP_PROVIDER" else echo "WSREP provider '$WSREP_PROVIDER' not found" exit -1 fi WSREP_OPTS="$WSREP_OPTS \ --wsrep_cluster_name=$WSREP_CLUSTER_NAME \ --wsrep_cluster_address=$WSREP_CLUSTER_ADDRESS \ --wsrep_sst_method=$WSREP_SST_METHOD \ --wsrep_local_cache_size=$WSREP_LOCAL_CACHE_SIZE \ --wsrep_start_position=$WSREP_START_POSITION \ --wsrep_debug=$WSREP_DEBUG \ --wsrep_auto_increment_control=$WSREP_AUTO_INCREMENT_CONTROL \ --wsrep_retry_autocommit=$WSREP_RETRY_AUTOCOMMIT \ --wsrep_convert_LOCK_to_trx=$WSREP_CONVERT_LOCK_TO_TRX \ --wsrep_drupal_282555_workaround=$WSREP_DRUPAL_282555_WORKAROUND \ --wsrep_ws_persistency=$WSREP_WS_PERSISTENCY \ --wsrep_slave_threads=$WSREP_SLAVE_THREADS " MYSQLD_OPTS=" --user=$MYSQLD_USER \ --basedir=$MYSQL_BASE_DIR \ --datadir=$MYSQL_DATA_DIR \ --pid-file=$MYSQL_PID \ --port=$MYSQL_PORT \ --socket=$MYSQL_SOCKET \ --skip-locking \ --binlog_format=ROW \ --default-storage-engine=InnoDB " INNODB_OPTS=" --innodb_autoinc_lock_mode=2 \ --innodb_flush_log_at_trx_commit=0 \ --innodb_doublewrite=0" err_log="$MYSQL_DATA_DIR/$(hostname).err" echo -n "Starting mysqld instance with data dir $MYSQL_DATA_DIR and listening at port $MYSQL_PORT and socket $MYSQL_SOCKET..." LD_LIBRARY_PATH=$(cd $(dirname $WSREP_PROVIDER) && pwd -P) export LD_LIBRARY_PATH export PATH=$MYSQL_BASE_DIR/bin:$PATH nohup $MYSQLD $DEFAULTS_OPTION $MYSQLD_OPTS $INNODB_OPTS $WSREP_OPTS \ 1>/dev/null 2>>$err_log & my_pid=$! # echo "Waiting for pid file" while ! test -r $MYSQL_PID do sleep 1 if find_pid $my_pid then # process is alive, wait for pid file echo -n "." else failed="yes" break fi done if test "$failed" != "yes" then echo " Done (PID:$(cat $MYSQL_PID))" else echo " Failed (PID:$my_pid)" fi } galera_stop() { # check pid file if test -r $MYSQL_PID then # check if corresponding mysqld is running # if ps axc | grep mysqld | grep $(cat $MYSQL_PID) >/dev/null 2>&1 local my_pid=$(cat $MYSQL_PID) if find_pid $my_pid then echo -n "Killing PID $my_pid" kill $my_pid # wait for pid file to disappear for second in $(seq 1 $MYSQL_SHUTDOWN_WAIT) do echo -n "." sleep 1 if test ! -r $MYSQL_PID then break fi done echo "" if test "$second" = "$MYSQL_SHUTDOWN_WAIT" then echo -n "Failed to stop mysqld safely. Killing with -9... " kill -9 $my_pid fi else echo -n "Removing stale PID file $MYSQL_PID... " fi rm -rf $MYSQL_PID echo "Done" else echo "PID file not found: $MYSQL_PID" fi } galera_restart() { galera_stop galera_start } dump() { #local ROUTINES="--routines" # don't dump routines yet, will cause false err. local DUMP_OPTIONS=" --skip-opt --compact --flush-logs --lock-all-tables \ --quick --create-options --set-charset --skip-comments $ROUTINES " DB=${DB:-"--all-databases"} #set -x mysqldump $DUMP_OPTIONS $ROOT_USER $ROOT_PSWD -h127.0.0.1 -P$MYSQL_PORT \ $IGNORE_TABLES $DB #set +x } checksum() { CS=`dump | md5sum` echo $CS } # write set level, SQL, RBR or ROW WS_LEVEL="RBR" #DB="test" # use 'test' database if none given IGNORE_TABLES="" case $(basename $0) in 'dump') COMMAND="dump" ;; 'check') COMMAND="checksum" ;; 'online') COMMAND=galera_start ;; 'stop'|'halt'|'kill'|'offline') COMMAND=galera_stop ;; 'restart') COMMAND=galera_restart ;; 'status') COMMAND=status ;; *) echo $0 # must be command usage exit 1 ;; esac $COMMAND # percona-xtradb-cluster-galera/scripts/openrep/status0000755000000000000000000000000012247075736026100 2not_supportedustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/openrep/stop0000755000000000000000000000000012247075736024275 2serviceustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/openrep/waitevent0000755000000000000000000000000012247075736026563 2not_supportedustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/packages/README0000644000000000000000000005265312247075736023012 0ustar rootroot00000000000000Codership Oy http://www.codership.com DISCLAIMER THIS SOFTWARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL CODERSHIP OY BE HELD LIABLE TO ANY PARTY FOR ANY DAMAGES RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE. Trademark Information. All trademarks are the property of their respective owners. Licensing Information. Galera is copyright (c) 2007-2013 Codership Oy Please see COPYING file that came with this distribution. This product uses asio C++ library which is Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) licensed under Boost Software License. Source code can be found at https://launchpad.net/galera GALERA v2.x CONTENTS: ========= 1. WHAT IS GALERA 2. GALERA USE CASES 3. GALERA CONFIGURATION PARAMETERS 4. GALERA ARBITRATOR 5. WRITESET CACHE 6. INCREMENTAL STATE TRANSFER 7. SPECIAL NOTES 1. WHAT IS GALERA Galera is a synchronous multi-master replication engine that provides its service through wsrep API (https://launchpad.net/wsrep). It features optimistic transaction execution and commit time replication and certification of writesets. Since it replicates only final changes to the database it is transparent to triggers, stored procedures and non-deterministic functions. Galera nodes are connected to each other in a N-to-N fashion through a group communication backend which provides automatic reconfiguration in the event of a node failure or a new node added to cluster: ,-------. ,-------. ,--------. | node1 |-----| node2 |<---| client | `-------' G `-------' `--------' \ / ,-------. ,--------. | node3 |<---| client | `-------' `--------' Node states are synchronized by replicating transaction changes at commit time. The cluster is virtually synchronous: this means that each node commits transactions in exactly the same order, although not necessarily at the same physical moment. (The latter is not that important as it may seem, since in most cases DBMS gives no guarantee on when the transaction is actually processed.) Built-in flow control keeps nodes within fraction of a second from each other, this is more than enough for most practical purposes. Main features of a Galera database cluster: * Truly highly available: no committed transaction is ever lost in case of a node crash. All nodes always have consistent state. * True multi-master: all cluster nodes can handle WRITE load concurrently. * Highly transparent. (See SPECIAL NOTES below) * Scalable even with WRITE-intensive applications. * Automatic synchronization of new nodes. 2. GALERA USE CASES There is a number of ways how Galera replication can be utilized. They can be categorized in three groups: 1) Seeking High Availability only. In this case client application connects to only one node, the rest serving as hot backups: ,-------------. | application | `-------------' | | | DB backups ,-------. ,-------. ,-------. | node1 | | node2 | | node3 | `-------' `-------' `-------' <===== cluster nodes =====> In the case of primary node failure or maintenance shutdown application can instantly switch to another node without any special failover procedure. 2) Seeking High Availability and improved performance through uniform load distribution. If there are several client connections to the database, they can be uniformly distributed between cluster nodes resulting in better performance. The exact degree of performance improvement depends on application's load profile. Note, that transaction rollback rate may also increase. ,-------------. | clients | `-------------' | | | | ,-------------. | application | `-------------' / | \ ,-------. ,-------. ,-------. | node1 | | node2 | | node3 | `-------' `-------' `-------' <===== cluster nodes =====> In the case of a node failure application can keep on using the remaining healthy nodes. In this setup application can also be clustered with a dedicated application instance per database node, thus achieving HA not only for the database, but for the whole application stack: ,-------------. | clients | `-------------' // || \\ ,------. ,------. ,------. | app1 | | app2 | | app3 | `------' `------' `------' | | | ,-------. ,-------. ,-------. | node1 | | node2 | | node3 | `-------' `-------' `-------' <====== cluster nodes ======> 3) Seeking High Availability and improved performance through smart load distribution. Uniform load distribution can cause undesirably high rollback rate. Directing transactions which access the same set of tables to the same node can considerably improve performance by reducing the number of rollbacks. Also, if your application can distinguish between read/write and read-only transactions, the following configuration may be quite efficient: ,---------------------. | application | `---------------------' writes / | reads \ reads ,-------. ,-------. ,-------. | node1 | | node2 | | node3 | `-------' `-------' `-------' <========= cluster nodes =========> 3. GALERA PARAMETERS 3.1 Cluster URL. Galera can use URL (RFC 3986) syntax for addressing with optional parameters passed in the URL query part. Galera cluster address looks as follows: ://[?option1=value1[&option2=value2]] e.g.: gcomm://192.168.0.1:4567?gmcast.listen_addr=0.0.0.0:5678 Currently Galera supports the following backends: 'dummy' - is a bypass backend for debugging/profiling purposes. It does not connect to or replicate anything and the rest of the URL address string is ignored. 'gcomm' - is a Codership's own Group Communication backend that provides Virtual Synchrony quality of service. It uses TCP for membership service and TCP (and UDP multicast as of version 0.8) for data replication. Normally one would use just the simplest form of the address URL: gcomm:// - if one wants to start a new cluster. gcomm://
- if one wants to join an existing cluster. In that case
is the address of one of the cluster members 3.2 Galera parameters. There is quite a few galera configuration parameters which affect its behavior and performance. Of particular interest to an end-user are the following: To configure gcomm listen address: gmcast.listen_addr To configure how fast cluster reacts on node failure or connection loss: evs.suspect_timeout evs.inactive_timeout evs.inactive_check_period evs.keepalive_period To fine-tune performance (especially in high latency networks): evs.user_send_window evs.send_window To relax or tighten replication flow control: gcs.fc_limit gcs.fc_factor For a full parameter list please see http://www.codership.com/wiki/doku.php?id=galera_parameters 3.2.1 GMCast parameters group. All parameters in this group are prefixed by 'gmcast.' (see example above). group String denoting group name. Max length of string is 16. Peer nodes accept GMCast connection only if the group names match. It is set automatically from wsrep options. listen_addr Listening address for GMCast. Address is currently passed in URI format (for example tcp://192.168.3.1:4567) and it should be passed as the last configuration parameter in order to avoid confusion. If parameter value is undefined, GMCast starts listening all interfaces at default port 4567 mcast_addr Multicast address in dotted decimal notation, enables using multicast to transmit group communication messages. Defaults to none. Must have the same value on all nodes. mcast_port Port used for UDP multicast messages. Defaults to listen_addr port. Must have same value on all of the nodes. mcast_ttl Time to live for multicast packets. Defaults to 1. 3.2.2 EVS parameter group. All parameters in this group are prefixed by 'evs.'. All values for the timeout options below should follow ISO 8601 standard for the time interval representation (e.g. 02:01:37.2 -> PT2H1M37.2S == PT121M37.2S == PT7297.2S) suspect_timeout This timeout controls how long node can remain silent until it is put under suspicion. If majority of the current group agree that the node is under suspicion, it is discarded from group and new group view is formed immediately. If majority of the group does not agree about suspicion, is waited until forming of new group will be attempted. Default value is 5 seconds. inactive_timeout This timeout control how long node can remain completely silent until it is discarded from the group. This is hard limit, unlike , and the node is discarded even if it becomes live during the formation of the new group (so it is inclusive of ). Default value is 15 seconds. inactive_check_period This period controls how often node liveness is checked. Default is 1 second and there is no need to change this unless or is adjusted to smaller value. Minimum is 0.1 seconds and maximum is /2. keepalive_period This timeout controls how often keepalive messages are sent into network. Node liveness is determined with these keepalives, so the value sould be considerably smaller than . Default value is 1 second, minimum is 0.1 seconds and maximum is /3. consensus_timeout This timeout defines how long forming of new group is attempted. If there is no consensus after this time has passed since starting of consensus protocol, every node discards all other nodes from the group and forming of new group is attempted through singleton groups. Default value is 30 seconds, minimum is and maximum is *5. join_retrans_period This parameter controls how often join messages are retransmitted during group formation. There is usually no need to adjust this value. Default value is 0.3 seconds, minimum is 0.1 seconds and maximum is /3. view_forget_timeout This timeout controls how long information about known group views is maintained. This information is needed to filter out delayed messages from previous views that are not live anymore. Default value is 5 minutes and there is usually no need to change it. debug_log_mask This mask controls what debug information is printed in the logs if debug logging is turned on. Mask value is bitwise-OR from values gcomm::evs::Proto::DebugFlags. By default only state information is printed. info_log_mask This mask controls what info log is printed in the logs. Mask value is bitwise-or from values gcomm::evs::Proto::InfoFlags. stats_report_period This parameter controls how often statistics information is printed in the log. This parameter has effect only if statistics reporting is enabled via Conf::EvsInfoLogMask. Default value is 1 minute. send_window This parameter controls how many messages protocol layer is allowed to send without getting all acknowledgements for any of them. Default value is 32. user_send_window Like , but for messages which sending is initiated by a call from the upper layer. Default value is 16. 3.2.3 GCS parameter group All parameters in this group are prefixed by 'gcs.'. fc_debug Post debug statistics about SST flow control every that many writesets. Default: 0. fc_factor Resume replication after recv queue drops below that fraction of gcs.fc_limit. Default: 0.5. fc_limit Pause replication if recv queue exceeds that many writesets. Default: 16. For master-slave setups this can be increased considerably. fc_master_slave Should we assume that there is only one master in the group? Default: NO. sync_donor Should we enable flow control in DONOR state the same way as in SYNCED state. Useful for non-blocking state transfers. Default: NO. max_packet_size All writesets exceeding that size will be fragmented. Default: 32616. max_throttle How much we can throttle replication rate during state transfer (to avoid running out of memory). Set it to 0.0 if stopping replication is acceptable for the sake of completing state transfer. Default: 0.25. recv_q_hard_limit Maximum allowed size of recv queue. This should normally be half of (RAM + swap). If this limit is exceeded, Galera will abort the server. Default: LLONG_MAX. recv_q_soft_limit A fraction of gcs.recv_q_hard_limit after which replication rate will be throttled. Default: 0.25. The degree of throttling is a linear function of recv queue size and goes from 1.0 (“full rate”) at gcs.recv_q_soft_limit to gcs.max_throttle at gcs.recv_q_hard_limit. Note that “full rate”, as estimated between 0 and gcs.recv_q_soft_limit is a very approximate estimate of a regular replication rate. 3.2.4 Replicator parameter group All parameters in this group are prefixed by 'replicator.'. commit_order Whether we should allow Out-Of-Order committing (improves parallel applying performance). Possible settings: 0 – BYPASS: all commit order monitoring is turned off (useful for measuring performance penalty) 1 – OOOC: allow out of order committing for all transactions 2 – LOCAL_OOOC: allow out of order committing only for local transactions 3 – NO_OOOC: no out of order committing is allowed (strict total order committing) Default: 3. 3.2.5 GCache parameter group All parameters in this group are prefixed by 'gcache.'. dir Directory where GCache should place its files. Default: working directory. name Name of the main store file (ring buffer). Default: “galera.cache”. size Size of the main store file (ring buffer). This will be preallocated on startup. Default: 128Mb. page_size Size of a page in the page store. The limit on overall page store is free disk space. Pages are prefixed by “gcache.page”. Default: 128Mb. keep_pages_size Total size of the page store pages to keep for caching purposes. If only page storage is enabled, one page is always present. Default: 0. mem_size Size of the malloc() store (read: RAM). For configurations with spare RAM. Default: 0. 3.2.6 SSL parameters All parameters in this group are prefixed by 'socket.'. ssl_cert Certificate file in PEM format. ssl_key A private key for the certificate above, unencrypted, in PEM format. ssl A boolean value to disable SSL even if certificate and key are configured. Default: yes (SSL is enabled if ssl_cert and ssl_key are set) To generate private key/certificate pair the following command may be used: $ openssl req -new -x509 -days 365000 -nodes -keyout key.pem -out cert.pem Using short-living (in most web examples - 1 year) certificates is not advised as it will lead to complete cluster shutdown when certificate expires. 3.2.7 Incremental State Transfer parameters All parameters in this group are prefixed by 'ist.'. recv_addr Address to receive incremental state transfer at. Setting this parameter turns on incremental state transfer. IST will use SSL if SSL is configured as described above. No default. 4. GALERA ARBITRATOR Galera arbitrator found in this package is a small stateless daemon that can serve as a lightweight cluster member to * avoid split brain condition in a cluster with an otherwise even membership. * request consistent state snapshots for backup purposes. Example usage: ,---------. | garbd | `---------' ,---------. | ,---------. | clients | | | clients | `---------' | `---------' \ | / \ ,---. / (' `) ( WAN ) (. ,) / `---' \ / \ ,---------. ,---------. | node1 | | node2 | `---------' `---------' Data Center 1 Data Center 2 In this example, if one of the data centers loses WAN connection, the node that sees arbitrator (and therefore sees clients) will continue the operation. garbd accepts the same Galera options as the regular Galera node. Note that at the moment garbd needs to see all replication traffic (although it does not store it anywhere), so placing it in a location with poor connectivity to the rest of the cluster may lead to cluster performance degradation. Arbitrator failure does not affect cluster operation and a new instance can be reattached to cluster at any time. There can be several arbitrators in the cluster, although practicality of it is questionable. 5. WRITESET CACHE Starting with version 1.0 Galera stores writesets in a special cache. It's purpose is to improve control of Galera memory usage and offload writeset storage to disk. Galera cache has 3 types of stores: 1. A permanent in-memory store, where writesets are allocated by a default OS memory allocator. It can be useful for systems with spare RAM. It has a hard size limit. By default it is disabled (size set to 0). 2. A permanent ring-buffer file which is preallocated on disk during cache initialization. It is intended as the main writeset store. By default its size is 128Mb. 3. An on-demand page store, which allocates memory-mapped page files during runtime as necessary. Default page size is 128Mb, but it can be bigger if it needs to store a bigger writeset. The size of page store is limited by the free disk space. By default page files are deleted when not in use, but a limit can be set on the total size of the page files to keep. When all other stores are disabled, at least one page file is always present on disk. Allocation algorithm is as follows: all stores are tried in the above order. If a store does not have enough space to allocate the writeset, then the next store is tried. Page store should always succeed unless the writeset is bigger than the available disk space. By default Galera cache allocates files in the working directory of the process, but a dedicated location can be specified. For configuration parameters see GCache group above (p. 3.2.5). NOTE: Since all cache files are memory-mapped, the process may appear to use more memory than it actually does. 6. INCREMENTAL STATE TRANSFER (IST) Galera 2.x introduces a long awaited functionality: incremental state transfer. The idea is that if a) the joining node state UUID is the same as that of the group and b) all of the writesets that it missed can be found in the donor's Gcache then instead of whole state snapshot it will receive the missing writesets and catch up with the group by replaying them. For example: - local node state is 5a76ef62-30ec-11e1-0800-dba504cf2aab:197222 - group state is 5a76ef62-30ec-11e1-0800-dba504cf2aab:201913 - if writeset number 197223 is still in the donor's GCache, it will send writests 197223-201913 to joiner instead of the whole state. IST can dramatically speed up remerging node into cluster. It also non-blocking on the donor. The most important parameter for IST (besides 'ist.recv_addr') is GCache size on donor. The bigger it is, the more writesets can be stored in it and the bigger seqno gaps can be closed with IST. On the other hand, if GCache is much bigger than the state size, serving IST may be less efficient than sending state snapshot. 7. SPECIAL NOTES 7.1 DEADLOCK ON COMMIT In multi-master mode transaction commit operation may return a deadlock error. This is a consequence of writeset certification and is a fundamental property of Galera. If deadlock on commit cannot be tolerated by application, Galera can still be used on a condition that all write operations to a given table are performed on the same node. This still has an advantage over the "traditional" master-slave replication: write load can still be distributed between nodes and since replication is synchronous, failover is trivial. 7.2 "SPLIT-BRAIN" CONDITION Galera cluster is fully distributed and does not use any sort of centralized arbitrator, thus having no single point of failure. However, like any cluster of that kind it may fall to a dreaded "split-brain" condition where half of the cluster nodes suddenly disappear (e.g. due to network failure). In general case, having no information about the fate of disappeared nodes remaining nodes cannot continue to process requests and modify their states. While such situation is generally considered negligibly probable in a multi-node cluster (normally nodes fail one at a time), in 2-node cluster a single node failure can lead to this, thus making 3 nodes a minimum requirement for a highly-available cluster. Galera arbitrator (see above) can serve as an odd stateless cluster node to help avoid the possibility of an even cluster split. percona-xtradb-cluster-galera/scripts/packages/README-MySQL0000644000000000000000000001341412247075736023745 0ustar rootroot00000000000000Codership Oy http://www.codership.com DISCLAIMER THIS SOFTWARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL CODERSHIP OY BE HELD LIABLE TO ANY PARTY FOR ANY DAMAGES RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE. Trademark Information. MySQL and other trademarks are the property of their respective owners. Licensing Information. Please see COPYING file that came with this distribution. Source code can be found at http://www.codership.com/en/downloads/galera ABOUT THIS DOCUMENT This document briefly explains how to use Galera wsrep provider with MySQL RDBMS. For more information check http://www.codership.com/wiki/doku.php Using MySQL with GALERA v2.x CONTENTS: ========= 1. WHAT IS MYSQL/GALERA CLUSTER 2. MYSQL/GALERA NODE SETUP 3. CONNECTING TO CLUSTER 4. LIMITATIONS 1. WHAT IS MYSQL/GALERA CLUSTER MySQL/Galera cluster is a synchronous multi-master cluster solution for InnoDB engine based on wsrep API (https://launchpad.net/wsrep) which Galera is implementation of. It requires MySQL server patched to use this API (https://launchpad.net/codership-mysql). Node states are synchronized by replicating transaction changes at commit time. The cluster is virtually synchronous: this means that each node commits transactions in exactly the same order, although not necessarily at the same physical moment. (The latter is not that important as it may seem, since in most cases DBMS gives no guarantee on when the transaction is actually processed.) Built-in flow control keeps nodes within fraction of a second from each other, this is more than enough for most practical purposes. From the client perspective that means: * Truly highly available: no committed transaction is ever lost in case of a node crash. * Highly transparent: with few exceptions each node can be treated as a normal standalone MySQL/InnoDB server. * True multi-master: all cluster nodes can modify the same table concurrently. * Scalable even with WRITE-intensive applications. 2. MYSQL/GALERA NODE SETUP To setup MySQL/Galera node you will need to 1) Install wsrep-patched MySQL from https://launchpad.net/codership-mysql/0.7. Please see the documentation that comes with it about first time node setup. 2) Configure it to use Galera library as a wsrep provider. For that set wsrep_provider option to wsrep_provider=/usr/lib/galera/libgalera_smm.so (DEB systems) or wsrep_provider=/usr/lib64/galera/libgalera_smm.so (RPM systems). 3) Start MySQL server. 3. CONNECTING TO CLUSTER To join the cluster you will need to set global wsrep_cluster_address variable either in wsrep.cnf file or from the command line (the latter is recommended to avoid automatic connection to failed nodes). E.g. mysql> SET GLOBAL wsrep_cluster_address='gcomm://'; to bootstrap a new cluster, or mysql> SET GLOBAL wsrep_cluster_address='gcomm://:4567'; to join existing cluster. Upon connecting to initial node, the server will obtain the list of other nodes and connect to each of them. There is a number of options that can be passed to Galera with the wsrep_cluster_address string. See README for details. After connecting to cluster the new node will synchronize its state by receiving state snapshot form one of the peers. For the duration of this procedure both nodes may be unable to process client requests. Other nodes are unaffected by this. 4. SETTING GALERA PARAMETERS IN MYSQL To configure Galera parameteres set wsrep_provider_options variable either in my.cnf or in the command line: wsrep_provider_options="gcs.fc_limit = 128; gcs.fc_master_slave = yes" Certain parameteres can be changed in runtime: mysql> SET GLOBAL wsrep_provider_options="evs.send_window=16"; will only change the value of evs.send_window parameter. For the list of all Galera parameters see main README and http://www.codership.com/wiki. mysql> SHOW VARIABLES like 'wsrep_provider_options'; Will show all parameters and their current values. 5. LIMITATIONS 1) Currently replication works only with InnoDB storage engine. Any writes to tables of other types, including system (mysql.*) tables are not replicated. However, DDL statements are replicated in statement level, and changes to mysql.* tables will get replicated that way. So, you can safely issue: CREATE USER..., but issuing: INSERT INTO mysql.user..., will not be replicated. 2) DELETE operation is unsupported on tables without primary keys. Rows in tables without primary keys may appear in different order on different nodes. As a result SELECT...LIMIT... may return slightly different sets. 3) Unsupported queries: * LOCK/UNLOCK tables is not supported in multimaster configuration. * lock functions (GET_LOCK(), RELEASE_LOCK()... ) 4) Query log cannot be directed to table. If you enable query logging, you must forward the log to a file: log_output = FILE Use general_log and general_log_file to choose query logging and the log file name 5) Maximum allowed transaction size is defined by wsrep_max_ws_rows and wsrep_max_ws_size. Anything bigger (e.g. huge LOAD DATA) will be rejected. 6) Due to cluster level optimistic concurrency control, transaction issuing COMMIT may still be aborted at that stage. There can be two transactions writing to same rows and committing in separate cluster nodes, and only one of the them can successfully commit. The failing one will be aborted. For cluster level aborts, MySQL/galera cluster gives back deadlock error code (Error: 1213 SQLSTATE: 40001 (ER_LOCK_DEADLOCK)). 7) XA transactions can not be supported due to possible rollback on commit. percona-xtradb-cluster-galera/scripts/packages/empty0000644000000000000000000000000012247075736023166 0ustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/packages/freebsd/0000755000000000000000000000000012247075736023531 5ustar rootroot00000000000000percona-xtradb-cluster-galera/scripts/packages/freebsd.sh0000755000000000000000000000407712247075736024100 0ustar rootroot00000000000000#!/bin/bash -eu if [ $# -ne 1 ] then echo "Usage: $0 " exit 1 fi RELEASE=$1 # Absolute path of this script folder SCRIPT_ROOT=$(cd $(dirname $0); pwd -P) PBR="$SCRIPT_ROOT/pkg_top_dir" PBD="$SCRIPT_ROOT/../.." GALERA_LICENSE_DIR="$PBR/share/licenses/galera-$RELEASE" rm -rf "$PBR" mkdir -p "$PBR" install -d "$PBR/"{bin,lib/galera,share/doc/galera,etc/rc.d,libdata/ldconfig} install -m 555 "$PBD/garb/files/freebsd/garb.sh" "$PBR/etc/rc.d/garb" install -m 555 "$PBD/garb/garbd" "$PBR/bin/garbd" install -m 444 "$PBD/libgalera_smm.so" "$PBR/lib/galera/libgalera_smm.so" install -m 444 "$SCRIPT_ROOT/freebsd/galera-ldconfig" "$PBR/libdata/ldconfig/galera" install -m 444 "$PBD/scripts/packages/README" "$PBR/share/doc/galera/" install -m 444 "$PBD/scripts/packages/README-MySQL" "$PBR/share/doc/galera/" install -m 755 -d "$GALERA_LICENSE_DIR" install -m 444 "$PBD/LICENSE" "$GALERA_LICENSE_DIR/GPLv3" install -m 444 "$PBD/scripts/packages/freebsd/LICENSE" "$GALERA_LICENSE_DIR" install -m 444 "$PBD/scripts/packages/freebsd/catalog.mk" "$GALERA_LICENSE_DIR" install -m 644 "$SCRIPT_ROOT/freebsd/galera-"{plist,descr,comment,message} "$PBR" sed -e "s!%{SRCDIR}!$PBR!" -e "s!%{RELEASE}!$RELEASE!" -i "" "$PBR/galera-"{plist,descr,comment,message} \ "$GALERA_LICENSE_DIR/catalog.mk" for pkg in $(grep '^@comment DEPORIGIN:' "$PBR/galera-plist" | cut -d : -f 2); do pkgdep=$(/usr/sbin/pkg_info -q -O "$pkg") if [ -z "$pkgdep" ]; then echo "ERROR: failed to find dependency package '$pkg'" >&2 exit 1 fi sed -e "s!^@comment DEPORIGIN:$pkg!@pkgdep $pkgdep"$'\\\n&!' -i "" "$PBR/galera-plist" done /usr/sbin/pkg_create -c "$SCRIPT_ROOT/freebsd/galera-comment" \ -d "$SCRIPT_ROOT/freebsd/galera-descr" \ -m "$SCRIPT_ROOT/freebsd/galera-mtree" \ -D "$SCRIPT_ROOT/freebsd/galera-message" \ -f "$PBR/galera-plist" \ -v "galera-$1-$(uname -m).tbz" rm -rf "$PBR" exit 0 percona-xtradb-cluster-galera/scripts/packages/galera-common.inc0000644000000000000000000000203112247075736025327 0ustar rootroot00000000000000# This is Galera package description for ESP package manager %product Galera Replication Framework %copyright 2007-2011 by Codership Oy, All Rights Reserved %vendor Codership Oy %license COPYING %readme README %description Replication framework for transactional applications. Implements wsrep interface. %version ${GALERA_VER} %provides wsrep %provides galera %format deb %replaces galera %requires libc6 2.4 %requires libstdc++6 4.1.1 %requires libgcc1 4.1.1 %requires libssl0.9.8 %format rpm %requires glibc 2.4 %requires libstdc++ 4.1.2 %requires libgcc 4.1.2 %requires libssl.so.6 %format all $prefix=/usr %format !rpm $CONF_DEST=/etc/default $LIBS_DEST=${prefix}/lib/galera %format rpm $CONF_DEST=/etc/sysconfig %if x86_64 $LIBS_DEST=${prefix}/lib64/galera %else $LIBS_DEST=${prefix}/lib/galera %endif %format all $INIT_DEST=/etc/init.d $INCS_DEST=${prefix}/include/galera $SBIN_DEST=${prefix}/sbin $BINS_DEST=${prefix}/bin $DOCS_DEST=${prefix}/share/doc/galera # percona-xtradb-cluster-galera/scripts/packages/galera-dev.list0000644000000000000000000000261612247075736025030 0ustar rootroot00000000000000# This is Galera development package description for ESP package manager %include galera-common.inc d 755 root root $INCS_DEST - f 644 root root $INCS_DEST/gcs.h $BUILD_BASE/gcs/src/gcs.h f 644 root root $INCS_DEST/wsdb_api.h $BUILD_BASE/wsdb/src/wsdb_api.h f 644 root root $INCS_DEST/wsrep_api.h $BUILD_BASE/galera/src/wsrep_api.h d 755 root root $LIBS_DEST - f 755 root root $LIBS_DEST/libgalerautils.a $BUILD_BASE/galerautils/src/.libs/libgalerautils.a f 755 root root $LIBS_DEST/libgalerautils++.a $BUILD_BASE/galerautils/src/.libs/libgalerautils++.a %ifdef GCOMM #f 755 root root $LIBS_DEST/libgcomm.a $BUILD_BASE//gcomm/src/.libs/libgcomm.a %endif %ifdef VSBES f 755 root root $LIBS_DEST/libgcommcommonpp.a $BUILD_BASE/galeracomm/common/src/.libs/libgcommcommonpp.a f 755 root root $LIBS_DEST/libgcommtransportpp.a $BUILD_BASE/galeracomm/transport/src/.libs/libgcommtransportpp.a f 755 root root $LIBS_DEST/libgcommvspp.a $BUILD_BASE/galeracomm/vs/src/.libs/libgcommvspp.a %endif f 755 root root $LIBS_DEST/libgcs.a $BUILD_BASE/gcs/src/.libs/libgcs.a f 755 root root $LIBS_DEST/libwsdb.a $BUILD_BASE/wsdb/src/.libs/libwsdb.a f 755 root root $LIBS_DEST/libmmgalera.a $BUILD_BASE/galera/src/.libs/libmmgalera.a %format deb # Debian packages come with bad file ownership %postinstall < $LD_SO_CONF_D/galera.conf ldconfig $LIBS_DEST EOF_POSTINSTALL %preremove <. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 of the License or later. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to the # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston # MA 02110-1301 USA. %define name galera # use "rpmbuild --define 'version xxxx'" to define version %{!?version: %define version 2.x} %{!?release: %define release 1} %define copyright Copyright 2007-2013 Codership Oy. All rights reserved. Use is subject to license terms under GPLv2 license. %define libs %{_libdir}/%{name} %define docs /usr/share/doc/%{name} Name: %{name} Summary: Galera: a synchronous multi-master wsrep provider (replication engine) Group: System Environment/Libraries Version: %{version} Release: %{release} License: GPLv2 Source: http://www.codership.com/downloads/download-mysqlgalera/ URL: http://www.codership.com/ Packager: Codership Oy Vendor: Codership Oy Provides: %{name} wsrep Obsoletes: %{name} Requires: chkconfig #Requires: boost-program-options # BuildRequires: scons check boost-devel # This will be rm -rf BuildRoot: %{_tmppath}/%{name}-%{version} %description Galera is a fast synchronous multimaster wsrep provider (replication engine) for transactional databases and similar applications. For more information about wsrep API see http://launchpad.net/wsrep. For a description of Galera replication engine see http://www.codership.com. %{copyright} This software comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to modify and redistribute it under the GPLv2 license. %prep #%setup -T -a 0 -c -n galera-%{version} %build Build() { CFLAGS=${CFLAGS:-$RPM_OPT_FLAGS} CXXFLAGS=${CXXFLAGS:-$RPM_OPT_FLAGS} # We assume that Galera is built already by the top build.sh script } %install RBR=$RPM_BUILD_ROOT RBD=$RPM_BUILD_DIR # Clean up the BuildRoot first [ "$RBR" != "/" ] && [ -d $RBR ] && rm -rf $RBR; mkdir -p $RBR install -d $RBR%{_sysconfdir}/{init.d,sysconfig} install -m 644 $RBD/garb/files/garb.cnf $RBR%{_sysconfdir}/sysconfig/garb install -m 755 $RBD/garb/files/garb.sh $RBR%{_sysconfdir}/init.d/garb install -d $RBR%{_bindir} install -m 755 $RBD/garb/garbd $RBR%{_bindir}/garbd install -d $RBR%{libs} install -m 755 $RBD/libgalera_smm.so $RBR%{libs}/libgalera_smm.so install -d $RBR%{docs} install -m 644 $RBD/COPYING $RBR%{docs}/COPYING install -m 644 $RBD/scripts/packages/README $RBR%{docs}/README install -m 644 $RBD/scripts/packages/README-MySQL $RBR%{docs}/README-MySQL %pre %post %preun rm -f $(find %{libs} -type l) %files %defattr(-,root,root,0755) %config(noreplace,missingok) %{_sysconfdir}/sysconfig/garb %attr(0755,root,root) %{_sysconfdir}/init.d/garb %attr(0755,root,root) %dir %{_bindir} %attr(0755,root,root) %{_bindir}/garbd %attr(0755,root,root) %dir %{libs} %attr(0755,root,root) %{libs}/libgalera_smm.so %attr(0755,root,root) %dir %{docs} %doc %attr(0644,root,root) %{docs}/COPYING %doc %attr(0644,root,root) %{docs}/README %doc %attr(0644,root,root) %{docs}/README-MySQL %clean [ "$RPM_BUILD_ROOT" != "/" ] && [ -d $RPM_BUILD_ROOT ] && rm -rf $RPM_BUILD_ROOT; percona-xtradb-cluster-galera/scripts/packages/percona-xtradb-cluster-galera.spec0000644000000000000000000000740012247075736030615 0ustar rootroot00000000000000# Copyright (c) 2011, Percona Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to the # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston # MA 02110-1301 USA. %define src_dir percona-xtradb-cluster-galera %define docs /usr/share/doc/%{src_dir} %define rhelver %(rpm -qf --qf '%%{version}\\n' /etc/redhat-release | sed -e 's/^\\([0-9]*\\).*/\\1/g') %if "%rhelver" == "5" %define boost_req boost141-devel %define gcc_req gcc44-c++ %else %define boost_req boost-devel %define gcc_req gcc-c++ %endif %if %{undefined scons_args} %define scons_args %{nil} %endif %if %{undefined galera_revision} %define galera_revision %{revision} %endif %if %{undefined galera_mflags} %define galera_mflags 8 %endif %if %{undefined pxcg_revision} %define pxcg_revision %{revno} %endif %ifarch i686 %define scons_arch arch=i686 %else %define scons_arch %{nil} %endif %define redhatversion %(lsb_release -rs | awk -F. '{ print $1}') %define distribution rhel%{redhatversion} Name: Percona-XtraDB-Cluster-galera-2 Version: 2.8 Release: 1.%{pxcg_revision}.%{?distribution} Summary: Galera components of Percona XtraDB Cluster Group: Applications/Databases License: GPLv3 URL: http://www.percona.com/ Source0: percona-xtradb-cluster-galera.tar.gz BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) Prefix: %{_prefix} Provides: Percona-XtraDB-Cluster-galera-25 Obsoletes: Percona-XtraDB-Cluster-galera BuildRequires: scons check-devel glibc-devel %{gcc_req} openssl-devel %{boost_req} check-devel %description This package contains the Galera components required by Percona XtraDB Cluster. %prep %setup -q -n %{src_dir} %build %if "%rhelver" == "5" export CXX=g++44 %endif export GALERA_REV=%{galera_revision} export GALERA_VER=2.8 scons -j%{galera_mflags} revno=%{galera_revision} boost_pool=0 garb/garbd libgalera_smm.so %{scons_arch} %{scons_args} %install rm -rf $RPM_BUILD_ROOT mkdir -p "$RPM_BUILD_ROOT" install -d $RPM_BUILD_ROOT%{_sysconfdir}/{init.d,sysconfig} install -m 644 $RPM_BUILD_DIR/%{src_dir}/garb/files/garb.cnf \ $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/garb install -m 755 $RPM_BUILD_DIR/%{src_dir}/garb/files/garb.sh \ $RPM_BUILD_ROOT%{_sysconfdir}/init.d/garb install -d "$RPM_BUILD_ROOT/%{_bindir}" install -d "$RPM_BUILD_ROOT/%{_libdir}" install -m 755 "$RPM_BUILD_DIR/%{src_dir}/garb/garbd" \ "$RPM_BUILD_ROOT/%{_bindir}/" install -m 755 "$RPM_BUILD_DIR/%{src_dir}/libgalera_smm.so" \ "$RPM_BUILD_ROOT/%{_libdir}/" install -d $RPM_BUILD_ROOT%{docs} install -m 644 $RPM_BUILD_DIR/%{src_dir}/COPYING \ $RPM_BUILD_ROOT%{docs}/COPYING install -m 644 $RPM_BUILD_DIR/%{src_dir}/scripts/packages/README \ $RPM_BUILD_ROOT%{docs}/README install -m 644 $RPM_BUILD_DIR/%{src_dir}/scripts/packages/README-MySQL \ $RPM_BUILD_ROOT%{docs}/README-MySQL %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root,-) %config(noreplace,missingok) %{_sysconfdir}/sysconfig/garb %attr(0755,root,root) %{_sysconfdir}/init.d/garb %attr(0755,root,root) %{_bindir}/garbd %attr(0755,root,root) %{_libdir}/libgalera_smm.so %attr(0755,root,root) %dir %{docs} %doc %attr(0644,root,root) %{docs}/COPYING %doc %attr(0644,root,root) %{docs}/README %doc %attr(0644,root,root) %{docs}/README-MySQL %changelog percona-xtradb-cluster-galera/scripts/packages/rpm.sh0000755000000000000000000000250512247075736023256 0ustar rootroot00000000000000#!/bin/bash -eu if [ $# -ne 1 ] then echo "Usage: $0 " exit 1 fi set -x # Absolute path of this script folder SCRIPT_ROOT=$(cd $(dirname $0); pwd -P) THIS_DIR=$(pwd -P) RPM_TOP_DIR=$SCRIPT_ROOT/rpm_top_dir rm -rf $RPM_TOP_DIR mkdir -p $RPM_TOP_DIR/RPMS ln -s ../../../ $RPM_TOP_DIR/BUILD fast_cflags="-O3 -fno-omit-frame-pointer" uname -m | grep -q i686 && \ cpu_cflags="-mtune=i686" || cpu_cflags="-mtune=core2" RPM_OPT_FLAGS="$fast_cflags $cpu_cflags" GALERA_SPEC=$SCRIPT_ROOT/galera.spec RELEASE=${RELEASE:-"1"} if [ -r /etc/fedora-release ] then DISTRO_VERSION=fc$(rpm -qf --qf '%{version}\n' /etc/fedora-release) elif [ -r /etc/redhat-release ] then DISTRO_VERSION=rhel$(rpm -qf --qf '%{version}\n' /etc/redhat-release) elif [ -r /etc/SuSE-release ] then DISTRO_VERSION=sles$(rpm -qf --qf '%{version}\n' /etc/SuSE-release | cut -d. -f1) else DISTRO_VERSION= fi [ -n "$DISTRO_VERSION" ] && RELEASE=$RELEASE.$DISTRO_VERSION $(which rpmbuild) --clean --define "_topdir $RPM_TOP_DIR" \ --define "optflags $RPM_OPT_FLAGS" \ --define "version $1" \ --define "release $RELEASE" \ -bb --short-circuit -bi $GALERA_SPEC RPM_ARCH=$(uname -m | sed s/i686/i386/) mv $RPM_TOP_DIR/RPMS/$RPM_ARCH/galera-*.rpm ./ rm -rf $RPM_TOP_DIR exit 0 percona-xtradb-cluster-galera/scripts/packages/freebsd/LICENSE0000644000000000000000000000012112247075736024530 0ustar rootroot00000000000000This package has a single license: GPLv3 (GNU General Public License version 3). percona-xtradb-cluster-galera/scripts/packages/freebsd/catalog.mk0000644000000000000000000000032312247075736025472 0ustar rootroot00000000000000_LICENSE=GPLv3 _LICENSE_NAME=GNU General Public License version 3 _LICENSE_PERMS=dist-mirror dist-sell pkg-mirror pkg-sell auto-accept _LICENSE_GROUPS=FSF GPL OSI _LICENSE_DISTFILES=galera-%{RELEASE}-src.tar.gz percona-xtradb-cluster-galera/scripts/packages/freebsd/galera-comment0000644000000000000000000000010712247075736026345 0ustar rootroot00000000000000Galera: a synchronous multi-master wsrep provider (replication engine) percona-xtradb-cluster-galera/scripts/packages/freebsd/galera-descr0000644000000000000000000000046512247075736026012 0ustar rootroot00000000000000Galera is a fast synchronous multimaster wsrep provider (replication engine) for transactional databases and similar applications. For more information about wsrep API see http://launchpad.net/wsrep. For a description of Galera replication engine see http://www.codership.com. WWW: http://www.codership.com/ percona-xtradb-cluster-galera/scripts/packages/freebsd/galera-ldconfig0000644000000000000000000000002612247075736026470 0ustar rootroot00000000000000/usr/local/lib/galera percona-xtradb-cluster-galera/scripts/packages/freebsd/galera-message0000644000000000000000000000075712247075736026342 0ustar rootroot00000000000000************************************************************************ If you want to run Galera Arbitrator Daemon (garbd), remember to configure garb service in /etc/rc.conf, e.g.: garb_enable="YES" garb_galera_nodes="1.1.1.1:4567" garb_galera_group="wsrep_cluster_name" garb_galera_options="gmcast.listen_addr=tcp://2.2.2.2:4567" garb_log_file="/tmp/garb.log" To start garbd, use command: sudo service garb start ************************************************************************ percona-xtradb-cluster-galera/scripts/packages/freebsd/galera-mtree0000644000000000000000000004223012247075736026022 0ustar rootroot00000000000000# $FreeBSD: /tmp/pcvs/ports/Templates/BSD.local.dist,v 1.3 2010-11-12 20:57:14 pav Exp $ # # Please see the file src/etc/mtree/README before making changes to this file. # /set type=dir uname=root gname=wheel mode=0755 . bin .. etc devd .. man.d .. pam.d .. rc.d .. .. include X11 .. .. info .. lib X11 app-defaults .. fonts local .. .. .. .. libdata ldconfig .. ldconfig32 .. pkgconfig .. .. libexec .. man /set uname=man cat1 .. cat2 .. cat3 .. cat4 .. cat5 .. cat6 .. cat7 .. cat8 .. cat9 .. catl .. catn .. de.ISO8859-1 uname=root cat1 .. cat2 .. cat3 .. cat4 .. cat5 .. cat6 .. cat7 .. cat8 .. cat9 .. catl .. catn .. /set uname=root man1 .. man2 .. man3 .. man4 .. man5 .. man6 .. man7 .. man8 .. man9 .. manl .. mann .. .. en.ISO8859-1 /set uname=man cat1 .. cat1aout .. cat2 .. cat3 .. cat4 i386 .. .. cat5 .. cat6 .. cat7 .. cat8 i386 .. .. cat9 i386 .. .. catn .. .. ja uname=root cat1 .. cat2 .. cat3 .. cat4 .. cat5 .. cat6 .. cat7 .. cat8 .. cat9 .. catl .. catn .. /set uname=root man1 .. man2 .. man3 .. man4 .. man5 .. man6 .. man7 .. man8 .. man9 .. manl .. mann .. .. man1 .. man2 .. man3 .. man4 .. man5 .. man6 .. man7 .. man8 .. man9 .. manl .. mann .. ru.KOI8-R /set uname=man cat1 .. cat2 .. cat3 .. cat4 .. cat5 .. cat6 .. cat7 .. cat8 .. cat9 .. catl .. catn .. /set uname=root man1 .. man2 .. man3 .. man4 .. man5 .. man6 .. man7 .. man8 .. man9 .. manl .. mann .. .. .. sbin .. share aclocal .. dict .. doc ja .. .. emacs site-lisp .. .. examples .. java classes .. .. locale af LC_MESSAGES .. .. am LC_MESSAGES .. .. ar LC_MESSAGES .. .. az LC_MESSAGES .. .. be LC_MESSAGES .. .. bg LC_MESSAGES .. .. bn LC_MESSAGES .. .. br LC_MESSAGES .. .. bs LC_MESSAGES .. .. ca LC_MESSAGES .. .. cs LC_MESSAGES .. .. cy LC_MESSAGES .. .. da LC_MESSAGES .. .. de LC_MESSAGES .. .. de_AT LC_MESSAGES .. .. dk LC_MESSAGES .. .. ee LC_MESSAGES .. .. el LC_MESSAGES .. .. en LC_MESSAGES .. .. en_AU LC_MESSAGES .. .. en_CA LC_MESSAGES .. .. en_GB LC_MESSAGES .. .. eo LC_MESSAGES .. .. es LC_MESSAGES .. .. es_ES LC_MESSAGES .. .. es_MX LC_MESSAGES .. .. et LC_MESSAGES .. .. eu LC_MESSAGES .. .. fa LC_MESSAGES .. .. fa_IR LC_MESSAGES .. .. fi LC_MESSAGES .. .. fr LC_MESSAGES .. .. fr_FR LC_MESSAGES .. .. ga LC_MESSAGES .. .. gl LC_MESSAGES .. .. gu LC_MESSAGES .. .. he LC_MESSAGES .. .. hi LC_MESSAGES .. .. hr LC_MESSAGES .. .. hu LC_MESSAGES .. .. id LC_MESSAGES .. .. is LC_MESSAGES .. .. it LC_MESSAGES .. .. ja LC_MESSAGES .. .. ka LC_MESSAGES .. .. kn LC_MESSAGES .. .. ko LC_MESSAGES .. .. li LC_MESSAGES .. .. lt LC_MESSAGES .. .. lv LC_MESSAGES .. .. mk LC_MESSAGES .. .. ml LC_MESSAGES .. .. mn LC_MESSAGES .. .. ms LC_MESSAGES .. .. mt LC_MESSAGES .. .. nb LC_MESSAGES .. .. ne LC_MESSAGES .. .. nl LC_MESSAGES .. .. nn LC_MESSAGES .. .. no LC_MESSAGES .. .. or LC_MESSAGES .. .. pa LC_MESSAGES .. .. pl LC_MESSAGES .. .. pt LC_MESSAGES .. .. pt_BR LC_MESSAGES .. .. pt_PT LC_MESSAGES .. .. ro LC_MESSAGES .. .. ru LC_MESSAGES .. .. sk LC_MESSAGES .. .. sl LC_MESSAGES .. .. sq LC_MESSAGES .. .. sr LC_MESSAGES .. .. sr@Latn LC_MESSAGES .. .. sv LC_MESSAGES .. .. ta LC_MESSAGES .. .. tg LC_MESSAGES .. .. th LC_MESSAGES .. .. tk LC_MESSAGES .. .. tr LC_MESSAGES .. .. uk LC_MESSAGES .. .. uz LC_MESSAGES .. .. vi LC_MESSAGES .. .. wa LC_MESSAGES .. .. zh LC_MESSAGES .. .. zh_CN LC_MESSAGES .. .. zh_CN.GB2312 LC_MESSAGES .. .. zh_TW LC_MESSAGES .. .. zh_TW.Big5 LC_MESSAGES .. .. .. misc .. nls C .. af_ZA.ISO8859-1 .. af_ZA.ISO8859-15 .. af_ZA.UTF-8 .. am_ET.UTF-8 .. be_BY.CP1131 .. be_BY.CP1251 .. be_BY.ISO8859-5 .. be_BY.UTF-8 .. bg_BG.CP1251 .. bg_BG.UTF-8 .. ca_ES.ISO8859-1 .. ca_ES.ISO8859-15 .. ca_ES.UTF-8 .. cs_CZ.ISO8859-2 .. cs_CZ.UTF-8 .. da_DK.ISO8859-1 .. da_DK.ISO8859-15 .. da_DK.UTF-8 .. de_AT.ISO8859-1 .. de_AT.ISO8859-15 .. de_AT.UTF-8 .. de_CH.ISO8859-1 .. de_CH.ISO8859-15 .. de_CH.UTF-8 .. de_DE.ISO8859-1 .. de_DE.ISO8859-15 .. de_DE.UTF-8 .. el_GR.ISO8859-7 .. el_GR.UTF-8 .. en_AU.ISO8859-1 .. en_AU.ISO8859-15 .. en_AU.US-ASCII .. en_AU.UTF-8 .. en_CA.ISO8859-1 .. en_CA.ISO8859-15 .. en_CA.US-ASCII .. en_CA.UTF-8 .. en_GB.ISO8859-1 .. en_GB.ISO8859-15 .. en_GB.US-ASCII .. en_GB.UTF-8 .. en_IE.UTF-8 .. en_NZ.ISO8859-1 .. en_NZ.ISO8859-15 .. en_NZ.US-ASCII .. en_NZ.UTF-8 .. en_US.ISO8859-1 .. en_US.ISO8859-15 .. en_US.UTF-8 .. es_ES.ISO8859-1 .. es_ES.ISO8859-15 .. es_ES.UTF-8 .. et_EE.ISO8859-15 .. et_EE.UTF-8 .. fi_FI.ISO8859-1 .. fi_FI.ISO8859-15 .. fi_FI.UTF-8 .. fr_BE.ISO8859-1 .. fr_BE.ISO8859-15 .. fr_BE.UTF-8 .. fr_CA.ISO8859-1 .. fr_CA.ISO8859-15 .. fr_CA.UTF-8 .. fr_CH.ISO8859-1 .. fr_CH.ISO8859-15 .. fr_CH.UTF-8 .. fr_FR.ISO8859-1 .. fr_FR.ISO8859-15 .. fr_FR.UTF-8 .. he_IL.UTF-8 .. hi_IN.ISCII-DEV .. hr_HR.ISO8859-2 .. hr_HR.UTF-8 .. hu_HU.ISO8859-2 .. hu_HU.UTF-8 .. hy_AM.ARMSCII-8 .. hy_AM.UTF-8 .. is_IS.ISO8859-1 .. is_IS.ISO8859-15 .. is_IS.UTF-8 .. it_CH.ISO8859-1 .. it_CH.ISO8859-15 .. it_CH.UTF-8 .. it_IT.ISO8859-1 .. it_IT.ISO8859-15 .. it_IT.UTF-8 .. ja_JP.SJIS .. ja_JP.UTF-8 .. ja_JP.eucJP .. kk_KZ.PT154 .. kk_KZ.UTF-8 .. ko_KR.CP949 .. ko_KR.UTF-8 .. ko_KR.eucKR .. la_LN.ISO8859-1 .. la_LN.ISO8859-15 .. la_LN.ISO8859-2 .. la_LN.ISO8859-4 .. la_LN.US-ASCII .. lt_LT.ISO8859-13 .. lt_LT.ISO8859-4 .. lt_LT.UTF-8 .. nl_BE.ISO8859-1 .. nl_BE.ISO8859-15 .. nl_BE.UTF-8 .. nl_NL.ISO8859-1 .. nl_NL.ISO8859-15 .. nl_NL.UTF-8 .. no_NO.ISO8859-1 .. no_NO.ISO8859-15 .. no_NO.UTF-8 .. pl_PL.ISO8859-2 .. pl_PL.UTF-8 .. pt_BR.ISO8859-1 .. pt_BR.UTF-8 .. pt_PT.ISO8859-1 .. pt_PT.ISO8859-15 .. pt_PT.UTF-8 .. ro_RO.ISO8859-2 .. ro_RO.UTF-8 .. ru_RU.CP1251 .. ru_RU.CP866 .. ru_RU.ISO8859-5 .. ru_RU.KOI8-R .. ru_RU.UTF-8 .. sk_SK.ISO8859-2 .. sk_SK.UTF-8 .. sl_SI.ISO8859-2 .. sl_SI.UTF-8 .. sr_YU.ISO8859-2 .. sr_YU.ISO8859-5 .. sr_YU.UTF-8 .. sv_SE.ISO8859-1 .. sv_SE.ISO8859-15 .. sv_SE.UTF-8 .. tr_TR.ISO8859-9 .. tr_TR.UTF-8 .. uk_UA.ISO8859-5 .. uk_UA.KOI8-U .. uk_UA.UTF-8 .. zh_CN.GB18030 .. zh_CN.GB2312 .. zh_CN.GBK .. zh_CN.UTF-8 .. zh_CN.eucCN .. zh_HK.Big5HKSCS .. zh_HK.UTF-8 .. zh_TW.Big5 .. zh_TW.UTF-8 .. .. pixmaps .. sgml .. skel .. xml .. .. www .. .. percona-xtradb-cluster-galera/scripts/packages/freebsd/galera-plist0000644000000000000000000000243112247075736026040 0ustar rootroot00000000000000@comment PKG_FORMAT_REVISION:1.1 @name galera-%{RELEASE} @comment ORIGIN:databases/galera @cwd /usr/local @srcdir %{SRCDIR} @comment "=== dependencies ===" @comment "require /usr/local/lib/gcc48/libstdc++.so" @comment // @pkgdep gcc-4.8.2.s20130808 @comment DEPORIGIN:lang/gcc48 @comment // @pkgdep openssl-1.0.1_8 @comment DEPORIGIN:security/openssl @comment // @pkgdep libexecinfo-1.1_3 @comment DEPORIGIN:devel/libexecinfo @comment "=== preinstall stage ===" @exec echo "===> Linking /usr/local/bin/bash to /bin/bash" @exec [ -x /bin/bash ] && echo "Using existing /bin/bash." || ln -s ../usr/local/bin/bash /bin/bash @comment "=== file section ===" @owner root @group wheel @mode 0444 share/licenses/galera-%{RELEASE}/catalog.mk share/licenses/galera-%{RELEASE}/LICENSE share/licenses/galera-%{RELEASE}/GPLv3 @mode 0555 etc/rc.d/garb bin/garbd @mode 0444 lib/galera/libgalera_smm.so share/doc/galera/README share/doc/galera/README-MySQL libdata/ldconfig/galera @comment "=== postinstall stage ===" @exec /sbin/ldconfig -m /usr/local/lib/galera @comment "=== postremove stage ===" @dirrm share/licenses/galera-%{RELEASE} @dirrm share/doc/galera @comment // @unexec rm -f $(find /usr/local/lib/galera -type l)" @dirrm lib/galera @comment // @unexec ldconfig -R @unexec service ldconfig start >/dev/null percona-xtradb-cluster-galera/scripts/source/COPYING0000644000000000000000000004325412247075736022704 0ustar rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. percona-xtradb-cluster-galera/scripts/source/README0000644000000000000000000000046012247075736022521 0ustar rootroot00000000000000This is release 0.7 of Galera - Codership's implementation of wsrep interface (https://launchpad.net/wsrep) For details see http://www.codership.com. Build order: 1. libgalerautils 2. libgcomm 3. libgcs 4. libwsdb 5. libgalera Building unit tests requires check library (http://check.sourceforge.net) percona-xtradb-cluster-galera/scripts/source/build.sh0000755000000000000000000000354712247075736023310 0ustar rootroot00000000000000#!/bin/bash -eu # $Id: build.sh 1323 2009-11-22 23:48:22Z alex $ usage() { echo -e "Usage: build.sh [script options] [configure options]\n"\ "Script options:\n"\ " -h|--help this help message\n"\ " -i|--install install libraries system-wide\n" } INSTALL="no" CONFIGURE="no" LD_LIBRARY_PATH=${LD_LIBRARY_PATH:-""} CPPFLAGS=${CPPFLAGS:-""} LDFLAGS=${LDFLAGS:-""} if ccache -V > /dev/null 2>&1 then CC=${CC:-"gcc"} CXX=${CXX:-"g++"} echo "$CC" | grep "ccache" > /dev/null || CC="ccache $CC" echo "$CXX" | grep "ccache" > /dev/null || CXX="ccache $CXX" export CC CXX fi while test $# -gt 0 do case $1 in -i|--install) INSTALL="yes" shift ;; -h|--help) usage exit 1 ;; *) # what's left is the arguments for configure CONFIGURE="yes" break ;; esac done # Build process base directory build_base=$(cd $(dirname $0); pwd -P) # Updates build flags for the next stage build_flags() { local build_dir=$1 LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$build_dir/src/.libs CPPFLAGS="$CPPFLAGS -I$build_dir/src " LDFLAGS="$LDFLAGS -L$build_dir/src/.libs" } # Function to build single project build() { local build_dir=$1 shift echo "Building: $build_dir ($@)" pushd $build_dir export LD_LIBRARY_PATH export CPPFLAGS export LDFLAGS local SCRATCH=no if [ ! -s "Makefile" ]; then CONFIGURE=yes; fi if [ "$CONFIGURE" == "yes" ]; then rm -rf config.status; ./configure $@; SCRATCH=yes ; fi if [ "$SCRATCH" == "yes" ]; then make clean ; fi make || return 1 if [ "$INSTALL" == "yes" ] then make install || return 1 else build_flags $(pwd -P) || return 1 fi popd } build "libgalerautils*" $@ build "libgcomm*" $@ build "libgcs*" $@ build "libwsdb*" $@ build "libgalera*" $@ percona-xtradb-cluster-galera/scripts/source/package.sh0000755000000000000000000000443112247075736023575 0ustar rootroot00000000000000#!/bin/bash -eu # $Id: build.sh 1323 2009-11-22 23:48:22Z alex $ usage() { echo -e "Usage: build.sh [script options] [configure options] \n" \ "Script options:\n" \ " -h|--help this help message\n"\ " -r|--release release number to put in the tarball\n"\ " name: galera-source-XXX.tgz" } RELEASE="" CONFIGURE="no" LD_LIBRARY_PATH=${LD_LIBRARY_PATH:-""} CPPFLAGS=${CPPFLAGS:-""} LDFLAGS=${LDFLAGS:-""} while test $# -gt 0 do case $1 in -r|--release) RELEASE=$2 shift shift ;; -h|--help) usage exit 1 ;; *) # what's left is the arguments for configure break ;; esac done # Build process base directory BUILD_BASE=$(cd $(dirname $0)/../../; pwd -P) # Updates build flags for the next stage build_flags() { local build_dir=$1 LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$build_dir/src/.libs CPPFLAGS="$CPPFLAGS -I$build_dir/src " LDFLAGS="$LDFLAGS -L$build_dir/src/.libs" } # Function to build single project build() { local module=$1 shift local build_dir=$BUILD_BASE/$module echo "Building: $build_dir ($@)" pushd $build_dir export LD_LIBRARY_PATH export CPPFLAGS export LDFLAGS if [ ! -x "configure" ]; then ./bootstrap.sh; fi if [ ! -s "Makefile" ]; then ./configure $@; fi local src_base_name="lib${module}-" rm -rf "${src_base_name}"*.tar.gz make dist || return 1 build_flags $build_dir || return 1 local ret=$(ls "${src_base_name}"*.tar.gz) popd echo $build_dir/$ret } build_sources() { local module local srcs="" for module in "galerautils" "gcomm" "gcs" "wsdb" "galera" do src=$(build $module $@ | tail -n 1) || return 1 srcs="$srcs $src" done if [ -z "$RELEASE" ] then pushd "$BUILD_BASE" RELEASE="r$(svnversion | sed s/\:/,/g)" popd fi local dist_dir="galera-source-$RELEASE" rm -rf $dist_dir mkdir -p $dist_dir for src in $srcs do tar -C $dist_dir -xzf $src done cp "README" "COPYING" "build.sh" $dist_dir/ tar -czf $dist_dir.tgz $dist_dir # return absolute path for scripts echo $PWD/$dist_dir.tgz } pushd $(dirname $0) build_sources $@ percona-xtradb-cluster-galera/tests/bin/0000755000000000000000000000000012247075736020564 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/conf/0000755000000000000000000000000012247075736020741 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/r/0000755000000000000000000000000012247075736020255 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/0000755000000000000000000000000012247075736022357 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/run.sh0000755000000000000000000000263212247075736021162 0ustar rootroot00000000000000#!/bin/bash set -e # This script assumes that galera cluster is alredy installed and configured # This is location of this script. _HOME suffix preferred to _ROOT to avoid # confusion THIS_HOME=$(cd $(dirname $0); pwd -P) # Optional configuration file if test -n "$GALERA_TEST_CONFIG" then . "$GALERA_TEST_CONFIG" fi GALERA_TESTS_HOME=${GALERA_TESTS_HOME:-$THIS_HOME} GALERA_RESULTS_HOME=${GALERA_RESULTS_HOME:-$GALERA_TESTS_HOME/results} # Incoming cluster address (load balancer) export GALERA_CLUSTER_IP=${GALERA_CLUSTER_IP:-"127.0.0.1"} export GALERA_CLUSTER_PORT=${GALERA_CLUSTER_PORT:-3306} # List of tests to run GALERA_TESTS=${GALERA_TESTS:-"sqlgen dbt2 dots"} # This is needed for native load balancing and consistency checking export GALERA_NODES_IPS=${GALERA_NODE_IPS:?"GALERA_NODE_IPS not set"} export GALERA_NODES_PORTS=${GALERA_NODE_PORTS:?"GALERA_NODE_PORTS not set"} # Create a results directory for this run GALERA_DATE=$(date +%Y-%m-%d_%H:%M:%S) mkdir -p $GALERA_RESULTS_HOME/$GALERA_DATE declare TESTS_FAILED TESTS_FAILED=0 for TEST in $GALERA_TESTS do export GALERA_RESULT_DIR=$GALERA_RESULTS_HOME/$GALERA_DATE/$TEST mkdir -p $GALERA_RESULT_DIR echo -n "Running $TEST... " $GALERA_TESTS_HOME/test_$TEST/run.sh && echo "passed" \ || { TESTS_FAILED=$[ $TESTS_FAILED + 1 ]; echo "failed"; } done if [ $TESTS_FAILED != "0" ] then echo "Tests failed: $TESTS_FAILED" exit 1 fi # percona-xtradb-cluster-galera/tests/run_test_set.sh0000755000000000000000000000064212247075736023073 0ustar rootroot00000000000000#!/bin/sh # # This is a wrapper script for ./tap/run_test_set.pl # BASE_DIR=$(cd $(dirname $0); pwd -P) cd $BASE_DIR res=$? if test $res != 0 then echo "Failed to change directory to $BASE_DIR" exit 1 fi export TEST_BASE_DIR=$BASE_DIR . $BASE_DIR/conf/cluster.conf $BASE_DIR/tap/run_test_set.pl $@ res=$? if test $res != 0 then echo "Failed to run test set, exit code: $res" exit 1 fi exit 0 percona-xtradb-cluster-galera/tests/scripts/0000755000000000000000000000000012247075736021503 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/t/0000755000000000000000000000000012247075736020257 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/tap/0000755000000000000000000000000012247075736020600 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/test_causal/0000755000000000000000000000000012247075736022323 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/test_cppcheck/0000755000000000000000000000000012247075736022633 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/test_dbt2/0000755000000000000000000000000012247075736021706 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/test_dots/0000755000000000000000000000000012247075736022024 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/test_drupal/0000755000000000000000000000000012247075736022342 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/test_insert/0000755000000000000000000000000012247075736022357 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/test_memory/0000755000000000000000000000000012247075736022363 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/test_mtr/0000755000000000000000000000000012247075736021655 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/test_overhead/0000755000000000000000000000000012247075736022650 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/test_seesaw/0000755000000000000000000000000012247075736022342 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/test_sqlgen/0000755000000000000000000000000012247075736022344 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/test_startstop/0000755000000000000000000000000012247075736023116 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/test_stopcont/0000755000000000000000000000000012247075736022724 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/test_upgrade/0000755000000000000000000000000012247075736022502 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/conf/cluster.conf.tmpl0000644000000000000000000000305612247075736024250 0ustar rootroot00000000000000# # Cluster resources configuration. # # DBMS that will be used for tests export DBMS=${DBMS:-"MYSQL"} # DBMS superuser username and password for administrative purposes export DBMS_ROOT_USER=${DBMS_ROOT_USER:-"root"} export DBMS_ROOT_PSWD=${DBMS_ROOT_PSWD:-"rootpass"} # DBMS test use username and password export DBMS_TEST_USER=${DBMS_TEST_USER:-"test"} export DBMS_TEST_PSWD=${DBMS_TEST_PSWD:-"testpass"} # DBMS schema to use for tests export DBMS_TEST_SCHEMA=${DBMS_TEST_SCHEMA:-"test"} # Host for clients to connect to export DBMS_HOST=${DBMS_HOST:-"127.0.0.1"} # Port for MySQL specific tests export MYSQL_PORT=${MYSQL_PORT:-"3306"} # Port for PostgreSQL specific tests export PGSQL_PORT=${PGSQL_PORT:-"5432"} # Port for crossplatform tests case "$DBMS" in "MYSQL") export DBMS_PORT=${DBMS_PORT:-"$MYSQL_PORT"} ;; "PGSQL") export DBMS_PORT=${DBMS_PORT:-"$PGSQL_PORT"} ;; esac # How many concurrent clients to use export DBMS_CLIENTS=${DBMS_CLIENTS:-"16"} # Type of GCS backend export GCS_TYPE=${GCS_TYPE:-"gcomm"} case "$GCS_TYPE" in "gcomm") ;; "vsbes") if [ -z "$VSBES_ADDRESS" ]; then echo "VSBES_ADDRESS is not set"; exit 1; fi ;; *) echo "Urecognized GCS_TYPE: '$GCS_TYPE'" ; exit 1 ;; esac # Extra parameters passed to gcomm backend. # # export GCOMM_EXTRA_PARAMS=${GCOMM_EXTRA_PARAMS:-"gmcast.mcast_addr=239.192.0.11"} # default replication port export GCS_PORT=4567 # common part of my.cnf export COMMON_MY_CNF=$BASE_CONF/common_my.cnf # libglb.so location if not standard (/usr/lib|/usr/local/lib) #GLB_LIB= . $BASE_CONF/nodes.conf # end percona-xtradb-cluster-galera/tests/conf/dummy.conf0000644000000000000000000000007712247075736022747 0ustar rootroot00000000000000# Script arguments t/dummy.sh arg1 arg2 t/dummy.sh arg3 arg4 percona-xtradb-cluster-galera/tests/conf/galera_cert.pem0000644000000000000000000000217712247075736023723 0ustar rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDJDCCAo2gAwIBAgIJAL6/ZXC+Xx/xMA0GCSqGSIb3DQEBBQUAMGoxCzAJBgNV BAYTAkZJMRMwEQYDVQQIEwpTb21lLVN0YXRlMRUwEwYDVQQKEwxDb2RlcnNoaXAg T3kxDDAKBgNVBAsUA1ImRDEhMB8GCSqGSIb3DQEJARYSaW5mb0Bjb2RlcnNoaXAu Y29tMCAXDTExMDgxNTA5NTU1OFoYDzMwMTAxMjE2MDk1NTU4WjBqMQswCQYDVQQG EwJGSTETMBEGA1UECBMKU29tZS1TdGF0ZTEVMBMGA1UEChMMQ29kZXJzaGlwIE95 MQwwCgYDVQQLFANSJkQxITAfBgkqhkiG9w0BCQEWEmluZm9AY29kZXJzaGlwLmNv bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0LBFNowDw6d8njjguINZc2Xy 7puA0J8Jy6xCsnI/JtgxCMFI7ytvwm6VWfkpJd5aKiNhSV1Nr6Gmzqp8rzboZLWf Abptdnn8k2wE00YpXhVcrw0atcTEugOddBrS60m77Y2gLN9Dj00z49ZPBu4TW5rc QCX5E+PZXeQqZ6QqS1ECAwEAAaOBzzCBzDAdBgNVHQ4EFgQUXhvWGncIUO/lqGkO Rqdm5AEMsBQwgZwGA1UdIwSBlDCBkYAUXhvWGncIUO/lqGkORqdm5AEMsBShbqRs MGoxCzAJBgNVBAYTAkZJMRMwEQYDVQQIEwpTb21lLVN0YXRlMRUwEwYDVQQKEwxD b2RlcnNoaXAgT3kxDDAKBgNVBAsUA1ImRDEhMB8GCSqGSIb3DQEJARYSaW5mb0Bj b2RlcnNoaXAuY29tggkAvr9lcL5fH/EwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0B AQUFAAOBgQCIIp+/5+sa7o/VqAwk/SlctQOVJRrv5Ciy1qYVL6UcsTPFIE3w0ttP NUmD/+ck40U4NXhWaQPrUsT08QkoHqBe4LQbuURN/iscbX2THvawkKFxuHTES6p0 vntTDjqBuvVmrwy+ttOwa7hbUOTkE5VOEJYbx6Ejsjb7dNQCSLMcdg== -----END CERTIFICATE----- percona-xtradb-cluster-galera/tests/conf/galera_key.pem0000644000000000000000000000156712247075736023560 0ustar rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIICXQIBAAKBgQDQsEU2jAPDp3yeOOC4g1lzZfLum4DQnwnLrEKycj8m2DEIwUjv K2/CbpVZ+Skl3loqI2FJXU2voabOqnyvNuhktZ8Bum12efyTbATTRileFVyvDRq1 xMS6A510GtLrSbvtjaAs30OPTTPj1k8G7hNbmtxAJfkT49ld5CpnpCpLUQIDAQAB AoGATP/M+dxdgzJoG6UW3V2xgo+qO/nqZI9ZuOmdTmQzAu0f4oAWnhCj0tSkjKcP VKafFA8r1Tr08AmwF272RFv5EI0Vr76CyRUmw1Jq3gl4I5s6hqkYEnNiA3KuMcev ea+1briHm2ezLvC+YhQ02NP9hLkiipmN5Bi9Oh5EID0tg7ECQQDnuhOvbtqhPZxo 5jphwqwAYr+QtDadTLX8nKSj61wjyaB5owafA98CBvvcxg405MBFYauU9a2eWGDl dsxDrkF1AkEA5oxnlpOESqltIKIyfixEIkWfMxF6jf9OeiY1ctjZoprm/V83QsBq 60FfUwluhlG8pMriW4CU8S/c6WCMRuCq7QJBALtFYktRJ1dP12vfRCdlkGPvaimj us99A0ZXwabHuvshI5Op8Nvp4PxecIWHeku84rYvrN8BfYKOoiBP1dyMo1ECQQDc HyBkf1JnKt77soF9QGyJQGohC0Y1P5dBPyv3DJ63BykWr4uGm0s6dT87fBzf+Dii Kuf2r40Fv0owNTOaFLZ9AkACDW9483PlXdbmCxLwl5tqY0EExlwuRVVTga4ViNjp dMxjA9lS4sVvPKSkysq3RkCdkxuYtQcxl9NQOaW23KHn -----END RSA PRIVATE KEY----- percona-xtradb-cluster-galera/tests/conf/main.conf0000644000000000000000000000240312247075736022533 0ustar rootroot00000000000000#declare -r BASE_LOG="$TEST_BASE/log" declare -r BASE_OUT="$TEST_BASE/out" mkdir -p "$BASE_OUT" declare -r BASE_RUN="$TEST_BASE/run" mkdir -p "$BASE_RUN" declare -r BASE_CONF="$TEST_BASE/conf" . ${CLUSTER_CONF:-"$BASE_CONF/cluster.conf"} if [ "$(uname -s)" == "Darwin" ]; then GLB_PRELOAD=${DYLD_INSERT_LIBRARIES:-""} else GLB_PRELOAD=${LD_PRELOAD:-""} fi export GLB_PRELOAD # The code below tries to find available libglb.so and if found, export # necessary variables for client side load balancing GLB_LIB=${GLB_LIB:-""} if [ -z "$GLB_LIB" ] then if [ -r /usr/local/lib/libglb.so ] then GLB_LIB="/usr/local/lib/libglb.so" elif [ -r /usr/lib/libglb.so ] then GLB_LIB="/usr/lib/libglb.so" fi fi if [ -r "$GLB_LIB" ] then if [ -n "$GLB_PRELOAD" ] then export GLB_PRELOAD="$GLB_LIB:$GLB_PRELOAD" else export GLB_PRELOAD="$GLB_LIB" fi export GLB_BIND=$DBMS_HOST:$DBMS_PORT GLB_TARGETS="" for node in $NODE_LIST do target=${NODE_INCOMING_HOST[$node]}:${NODE_INCOMING_PORT[$node]} if [ $node -ne $NODE_MAX ] then GLB_TARGETS="$GLB_TARGETS$target," else GLB_TARGETS="$GLB_TARGETS$target" fi done export GLB_TARGETS fi percona-xtradb-cluster-galera/tests/conf/nodes.conf.tmpl0000644000000000000000000000357612247075736023706 0ustar rootroot00000000000000# # Nodes specific configuration # # Symbolic node ID - for output, for future use shoudl be unique declare -a NODE_ID # Location of the node. "local" - means this machine, anything else # is interpreted as SSH arguments declare -a NODE_LOCATION # Absolute path to where to unpack the distribution declare -a NODE_TEST_DIR # Address for incoming requests declare -a NODE_INCOMING_HOST declare -a NODE_INCOMING_PORT # Address for group communicaiton declare -a NODE_GCS_HOST declare -a NODE_GCS_PORT # Optional server configuration file to be copied to node declare -a NODE_MY_CNF declare -a NODE_PG_CNF # 1st node idx=0 NODE_ID[$idx]="home" NODE_INCOMING_HOST[$idx]=127.0.0.1 NODE_INCOMING_PORT[$idx]=$(( $DBMS_PORT - 1 )) NODE_GCS_HOST[$idx]=192.168.0.1 NODE_GCS_PORT[$idx]=$GCS_PORT NODE_LOCATION[$idx]="local" NODE_TEST_DIR[$idx]=/tmp/galera NODE_MY_CNF[$idx]="$TEST_BASE/conf/my.cnf" # 2nd node idx=$(( $idx + 1 )) NODE_ID[$idx]="bernhard" NODE_INCOMING_HOST[$idx]=192.168.0.3 NODE_INCOMING_PORT[$idx]=$DBMS_PORT NODE_GCS_HOST[$idx]=${NODE_INCOMING_HOST[$idx]} NODE_GCS_PORT[$idx]=$GCS_PORT NODE_LOCATION[$idx]="alex@${NODE_INCOMING_HOST[$idx]}" NODE_TEST_DIR[$idx]=/home/alex/codership/galera NODE_MY_CNF[$idx]="$TEST_BASE/conf/my.cnf" # 3rd node #idx=$(( $idx + 1 )) #NODE_ID[$idx]="bulldog" #NODE_INCOMING_HOST[$idx]=192.168.0.12 #NODE_INCOMING_PORT[$idx]=$DBMS_PORT #NODE_GCS_HOST[$idx]=${NODE_INCOMING_HOST[$idx]} #NODE_GCS_PORT[$idx]=$GCS_PORT #NODE_LOCATION[$idx]="alex@${NODE_INCOMING_HOST[$idx]}" #NODE_TEST_DIR[$idx]=/home/alex/codership/galera #NODE_MY_CNF[$idx]="$TEST_BASE/conf/my.cnf" declare -xr NODE_MAX=$idx declare -xr NODE_LIST=$(seq 0 $NODE_MAX) declare -xr NODE_ID declare -xr NODE_LOCATION declare -xr NODE_TEST_DIR declare -xr NODE_INCOMING_HOST declare -xr NODE_INCOMING_PORT declare -xr NODE_GCS_HOST declare -xr NODE_GCS_PORT declare -xr NODE_MY_CNF declare -xr NODE_PG_CNF # percona-xtradb-cluster-galera/tests/conf/sqlgen.conf0000644000000000000000000000017012247075736023077 0ustar rootroot00000000000000# Script test_sqlgen/run.sh --create 1 --rows 500 --duration 10 test_sqlgen/run.sh --create 1 --rows 1000 --duration 10 percona-xtradb-cluster-galera/tests/regressions/lp1003929/0000755000000000000000000000000012247075736023542 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/lp1013978/0000755000000000000000000000000012247075736023547 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/lp1019473/0000755000000000000000000000000012247075736023543 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/lp1026181/0000755000000000000000000000000012247075736023535 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/lp1055961/0000755000000000000000000000000012247075736023545 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/lp1073220/0000755000000000000000000000000012247075736023531 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/lp1089490/0000755000000000000000000000000012247075736023551 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/lp1100496/0000755000000000000000000000000012247075736023537 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/lp1130888/0000755000000000000000000000000012247075736023547 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/lp1179361/0000755000000000000000000000000012247075736023546 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/lp1184034/0000755000000000000000000000000012247075736023537 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/lp1206129/0000755000000000000000000000000012247075736023537 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/lp518749/0000755000000000000000000000000012247075736023474 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/lp587170/0000755000000000000000000000000012247075736023466 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/lp847353/0000755000000000000000000000000012247075736023470 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/lp861212/0000755000000000000000000000000012247075736023456 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/lp900816/0000755000000000000000000000000012247075736023462 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/lp909155/0000755000000000000000000000000012247075736023467 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/lp917416/0000755000000000000000000000000012247075736023466 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/lp922757/0000755000000000000000000000000012247075736023472 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/lp928150/0000755000000000000000000000000012247075736023463 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/lp930221/0000755000000000000000000000000012247075736023453 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/lp959512/0000755000000000000000000000000012247075736023471 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/lp963734/0000755000000000000000000000000012247075736023472 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/lp966950/0000755000000000000000000000000012247075736023475 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/lp967134/0000755000000000000000000000000012247075736023470 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/lp970714/0000755000000000000000000000000012247075736023466 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/lp994579/0000755000000000000000000000000012247075736023505 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/run-all.sh0000755000000000000000000000120412247075736024265 0ustar rootroot00000000000000#!/bin/bash tests="lp1100496 lp1179361 lp1184034 lp1089490 lp1073220 lp1003929 lp1013978 lp1026181 lp1055961 lp994579 lp970714 lp967134 lp966950 lp963734 lp959512 lp930221 lp928150 lp922757 lp909155 lp861212 lp847353 lp518749 t285 t281" # tests ignored # # lp900816 - does not work out of the box # lp917416 - hangs # for ii in $tests do echo "running test $ii" ( cd $ii && ./run.sh ) if test $? != 0 then echo "test $ii failed" exit 1 fi done percona-xtradb-cluster-galera/tests/regressions/t281/0000755000000000000000000000000012247075736023055 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/t285/0000755000000000000000000000000012247075736023061 5ustar rootroot00000000000000percona-xtradb-cluster-galera/tests/regressions/lp1003929/run.sh0000755000000000000000000000635512247075736024716 0ustar rootroot00000000000000#!/bin/bash -e ## # # lp:1003929 # https://bugs.launchpad.net/codership-mysql/+bug/1003929 # # BUG BACKGRPOUND: # # Table which ahs no primary key but has one or more unique keys # defined may cause a crash in slave configured for parallel applying. # This is due to the probölem that for such tables we generate only # full row hash as key value - the individual unique keys are not appended # in write set. # # If bug is present, slave appliers can easily conflict and cause crash. # # TEST SETUP: # - Two nodes are used in master slave mode. # - Slave is configured with 4 applier threads # - a table with no PK but one UK is created # - test load runs inserts/delete load for the table so that # inserted rows are different but unoque key values match # # SUCCESS CRITERIA # # If bug is present, slave will crash for DUPKEY error, PA control # allow two subsequent inserts to happen in parallel, althoug they # try to insert same unique key # declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/kill.sh . $SCRIPTS/misc.sh echo "##################################################################" echo "## regression test for lp:1003929" echo "##################################################################" echo "stopping cluster" $SCRIPTS/command.sh stop echo echo "starting node0, node1..." ../../scripts/command.sh start_node "-d -g gcomm://$(extra_params 0)" 0 ../../scripts/command.sh start_node "-d -g $(gcs_address 1) --slave_threads 4" 1 MYSQL="mysql --batch --silent --user=$DBMS_TEST_USER --password=$DBMS_TEST_PSWD -Dtest " declare -r port_0=$(( DBMS_PORT )) declare -r port_1=$(( DBMS_PORT + 1)) declare -r node_0="-h${NODE_INCOMING_HOST[0]} -P${NODE_INCOMING_PORT[0]}" declare -r node_1="-h${NODE_INCOMING_HOST[1]} -P${NODE_INCOMING_PORT[1]}" inserter() { local node="$@" for i in {1..10000}; do $MYSQL $node -e " DELETE FROM test.lp1003929; INSERT INTO test.lp1003929 VALUES('a',1,1); DELETE FROM test.lp1003929; INSERT INTO test.lp1003929 VALUES('a',1,2); " 2>&1 done } createdb() { $MYSQL $node_0 -e " DROP TABLE IF EXISTS test.lp1003929;" $MYSQL $node_0 -e ' CREATE TABLE test.lp1003929 ( a varchar(20), i INT, j INT, UNIQUE KEY(a,i) )' } ######################################################### # # Test begins here # ######################################################### threads=$($MYSQL $node_1 -e "SHOW VARIABLES LIKE 'wsrep_slave_threads'") echo "applier check: $threads" [ "$threads" = "wsrep_slave_threads 4" ] && echo "enough slaves" echo "Creating database..." createdb echo "Starting inserter..." inserter $node_0 & declare inserter_pid=$! echo "Waiting load to end ($inserter_pid)" wait $MYSQL $node_0 -e 'SHOW PROCESSLIST' echo $MYSQL $node_1 -e 'SHOW PROCESSLIST' [ "$?" != "0" ] && echo "failed!" && exit 1 $SCRIPTS/command.sh check if test $? != 0 then echo "Consistency check failed" exit 1 fi echo echo "Done!" echo ../../scripts/command.sh stop_node 0 ../../scripts/command.sh stop_node 1 exit 0 percona-xtradb-cluster-galera/tests/regressions/lp1013978/run.sh0000755000000000000000000002202712247075736024715 0ustar rootroot00000000000000#!/bin/bash ## # # lp:1013978 # https://bugs.launchpad.net/codership-mysql/+bug/1013978 # # BUG BACKGRPOUND: # # Foreign key constraint on non-unique index in parent table can cause # a crash in slave configured for parallel applying. # Current wsrep patch does not populate any key information for non-unique # keys and dependencies for parent row existence are not respected in PA # control. # # There are five scenarios how foreign key problems can surface: # A. FK has ON UPDATE CASCADE option # 1. Parent table has two rows with same fk key value # 2. One transaction issues update on one parent row, which triggers # corresponding update on depending child table row(s) # 3. Another transaction deletes the other parent table row. # This is now safe because child references have been changed to # point to the first parent row # => These WSs don't have mutual dependency and in slave side it may # happen that DELETE will be processed first, and it will fail for # FK violation # # B. shared reference to non-UK column in parent row with inserts # 1. one transaction does insert on parent table # 2. another transaction does insert on child table referencing the # inserted row # => These WSs don't have mutual dependency and in slave side it may # happen that child INSERT will be processed first, and it will fail for # FK violation # # C. shared reference to non-UK column in parent row with deletes # 1. one transaction deletes a row from child table # 2. another transaction deletes corresponding row in parent table # => These WSs don't have mutual dependency and in slave side it may # happen that parent delete will be processed first, and it will fail for # FK violation # # D. delete with FK on PK # * same as phase C, but with tables where FK constraint is on # primary key column # # E. shared reference to non-UK column in parent row with insert + delete # 1. one transaction first inserts a row in parent table # 2. second transaction inserts referencing row in child table # 3. third transaction deletes the row from child table # 4. fourths transaction deletes the row from parent table # => either insert or delete transactions can get applied in wrong order # in slave due to missing dependency # # If bug is present, slave appliers can easily conflict and cause crash. # # TEST SETUP: # - Two nodes are used in master slave mode. # - Slave is configured with 4 applier threads # - parent and child tables are creted and populated # - Test phases A, B and C will be run subsequently # # SUCCESS CRITERIA # # If bug is present, slave will crash for FK violation # declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/kill.sh . $SCRIPTS/misc.sh echo "##################################################################" echo "## regression test for lp:1013978" echo "##################################################################" echo "stopping cluster..." stop echo echo "starting node0, node1..." start_node "-d -g gcomm://$(extra_params 0)" 0 start_node "-d -g $(gcs_address 1) --slave_threads 4" 1 MYSQL="mysql --batch --silent --user=$DBMS_TEST_USER --password=$DBMS_TEST_PSWD --host=$DBMS_HOST test " declare -r port_0=${NODE_INCOMING_PORT[0]} declare -r port_1=${NODE_INCOMING_PORT[1]} ROUNDS=1000 SUCCESS=0 create_FK_parent_PK() { $MYSQL --port=$port_0 -e ' CREATE TABLE test.lp1013978p ( i int NOT NULL, j int DEFAULT NULL, PRIMARY KEY (i) ) ENGINE=InnoDB ' } create_FK_child_PK() { $MYSQL --port=$port_0 -e ' CREATE TABLE test.lp1013978c ( i int NOT NULL, f int DEFAULT NULL, PRIMARY KEY (i), FOREIGN KEY (i) REFERENCES test.lp1013978p (i) ) ENGINE=InnoDB ' } create_FK_parent_NON_UNIQ() { $MYSQL --port=$port_0 -e ' CREATE TABLE test.lp1013978p ( i int NOT NULL, j int DEFAULT NULL, PRIMARY KEY (i), KEY j (j) ) ENGINE=InnoDB ' } create_FK_child_NON_UNIQ() { $MYSQL --port=$port_0 -e ' CREATE TABLE test.lp1013978c ( i int NOT NULL, f int DEFAULT NULL, PRIMARY KEY (i), KEY fk (f), FOREIGN KEY (f) REFERENCES test.lp1013978p (j) ) ENGINE=InnoDB ' } create_FK_child_NON_UNIQ_ON_UPDATE_CASCADE() { $MYSQL --port=$port_0 -e ' CREATE TABLE test.lp1013978c ( i int NOT NULL, f int DEFAULT NULL, PRIMARY KEY (i), KEY fk (f), FOREIGN KEY (f) REFERENCES test.lp1013978p (j) ON UPDATE CASCADE ) ENGINE=InnoDB ' } create_DB_PK() { create_FK_parent_PK create_FK_child_PK } create_DB_NON_UNIQ() { create_FK_parent_NON_UNIQ create_FK_child_NON_UNIQ } create_DB_NON_UNIQ_ON_UPDATE_CASCADE() { create_FK_parent_NON_UNIQ create_FK_child_NON_UNIQ_ON_UPDATE_CASCADE } # # Test A procedures # A_cleanup() { $MYSQL --port=$port_0 -e " DROP TABLE IF EXISTS test.lp1013978c; " $MYSQL --port=$port_0 -e " DROP TABLE IF EXISTS test.lp1013978p; " } A_createdb() { A_cleanup create_DB_NON_UNIQ_ON_UPDATE_CASCADE $MYSQL --port=$port_0 -e " INSERT INTO test.lp1013978p VALUES (1,1); INSERT INTO test.lp1013978c VALUES (100,1); " } A_inserter() { local port=$1 for (( i=1; i<=$ROUNDS; i++ )); do $MYSQL --port=$port -e " INSERT INTO test.lp1013978p values (2, $i); UPDATE test.lp1013978p SET j=$(($i + 1)) where i=1; DELETE FROM test.lp1013978p WHERE i=2; " 2>&1 done } # # Test phase B procedures # B_cleanup() { A_cleanup } B_createdb() { B_cleanup create_DB_NON_UNIQ_ON_UPDATE_CASCADE } B_inserter() { local port=$1 for (( i=1; i<=$ROUNDS; i++ )); do $MYSQL --port=$port -e " INSERT INTO test.lp1013978p values ($i, $i); INSERT INTO test.lp1013978c values ($i, $i); " 2>&1 done } # # Test phase C procedures # C_cleanup() { A_cleanup } C_createdb() { # # we can re-use tables from test phase B # C_cleanup create_DB_NON_UNIQ_ON_UPDATE_CASCADE B_inserter $port_0 } C_deleter() { local port=$1 for (( i=1; i<=$ROUNDS; i++ )); do $MYSQL --port=$port -e " DELETE FROM test.lp1013978c WHERE i=$i; DELETE FROM test.lp1013978p WHERE i=$i; " 2>&1 done } # # Test phase D procedures, FK by PK # D_cleanup() { A_cleanup } D_createdb() { D_cleanup create_DB_PK # populate by phase B inserts B_inserter $port_0 } D_deleter() { C_deleter $port_0 } # # Test phase E procedures # E_cleanup() { A_cleanup } E_createdb() { E_cleanup create_DB_NON_UNIQ_ON_UPDATE_CASCADE } E_inserter() { local port=$1 for (( i=1; i<=$ROUNDS; i++ )); do $MYSQL --port=$port -e " INSERT INTO test.lp1013978p values ($i, $i); INSERT INTO test.lp1013978c values ($i, $i); DELETE FROM test.lp1013978c WHERE i=$i; DELETE FROM test.lp1013978p WHERE i=$i; " 2>&1 done } run_test() { phase=$1 create=$2 process=$3 cleanup=$4 echo "##################################################################" echo "## test phase $phase" echo "##################################################################" echo echo "Creating database..." eval $create echo "Starting test process..." eval $process $port_0 & pid=$! echo "Waiting load to end ($pid)" wait $MYSQL --port=$port_0 -e 'SHOW PROCESSLIST' echo $MYSQL --port=$port_1 -e 'SHOW PROCESSLIST' [ "$?" != "0" ] && echo "failed!" && exit 1 #$SCRIPTS/command.sh check echo "consistency checking..." check0=$(check_node 0) check1=$(check_node 1) cs0=$(echo $check0 | cut -d" " -f 11) cs1=$(echo $check1 | cut -d" " -f 11) echo "node 0: $cs0" echo "node 1: $cs1" if test "$cs0" != "$cs1" then echo "Consistency check failed" echo "$check0 $cs0" echo "$check1 $cs1" exit 1 fi if test $? != 0 then echo "Consistency check failed" exit 1 fi eval $cleanup echo echo "looks good so far..." echo } ######################################################### # # Test begins here # ######################################################### threads=$($MYSQL --port=$port_1 -e "SHOW VARIABLES LIKE 'wsrep_slave_threads'") echo "applier check: $threads" [ "$threads" = "wsrep_slave_threads 4" ] && echo "enough slaves" run_test A A_createdb A_inserter A_cleanup run_test B B_createdb B_inserter B_cleanup run_test C C_createdb C_deleter C_cleanup run_test D D_createdb D_deleter D_cleanup run_test E E_createdb E_inserter E_cleanup echo echo "Done!" echo stop_node 0 stop_node 1 exit $SUCCESS percona-xtradb-cluster-galera/tests/regressions/lp1019473/run.sh0000755000000000000000000000621612247075736024713 0ustar rootroot00000000000000#!/bin/bash ## # # lp:1019473 # https://bugs.launchpad.net/codership-mysql/+bug/1019473 # # BUG BACKGROUND: # # wsrep generates certification keys for tables with no PK nor UK, by # taking a MD5 digest over the whole row. This makes it possible to replicate # and control PA for key-less tables. Unfortunately this implementation # had a bug which caused such row key hashing to be non deterministic, if row # contains certain binary types (blob or text at least are vulnerable) # # TEST SETUP: # - Two nodes are used in master slave mode. # - Slave is configured with 4 applier threads # - parent and child tables are created and populated # - test load runs one connection, which issues a large detete for child # table and one row delete for parent table. We try to make the applying # of child table delete to last so long that parent table delete gets to # apply in parallel. # # SUCCESS CRITERIA # # If bug is present, slave will crash for not being able to delete a rowk # declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/kill.sh . $SCRIPTS/misc.sh echo "##################################################################" echo "## regression test for lp:1019473" echo "##################################################################" echo "stopping cluster" ../../scripts/command.sh stop echo echo "starting node0, node1..." ../../scripts/command.sh start "-d --slave_threads 4" declare -r port_0=${NODE_INCOMING_PORT[0]} declare -r port_1=${NODE_INCOMING_PORT[1]} MYSQL="mysql --batch --silent --user=$DBMS_TEST_USER --password=$DBMS_TEST_PSWD --host=$DBMS_HOST test " ROUNDS=1000 ROWS=5 USERS=3 echo "rounds=$ROUNDS" echo "rows=$ROWS" echo "users=$USERS" insert() { PORT=$1 for r in $(seq 1 $ROUNDS); do for i in $(seq 1 $ROWS); do $MYSQL --port=$PORT -e " DELETE FROM lp1019473 WHERE fid=$i AND uid=$i; INSERT INTO lp1019473 VALUES ($i,$i,'C-$1')"; done; done } createdb() { $MYSQL -P $port_0 -e "drop table if exists lp1019473;"; $MYSQL -P $port_0 -e "CREATE TABLE lp1019473 ( fid int(10) unsigned DEFAULT 0, uid int(10) unsigned DEFAULT 0, value text, KEY uid (uid), KEY fid (fid) ) ENGINE=InnoDB DEFAULT CHARSET=utf8" } cleandb() { PORT=$1 $MYSQL --port $PORT -e " DROP TABLE IF EXISTS test.lp1019473;" } ######################################################### # # Test begins here # ######################################################### threads=$($MYSQL --port=$port_1 -e "SHOW VARIABLES LIKE 'wsrep_slave_threads'") echo "applier check: $threads" [ "$threads" = "wsrep_slave_threads 4" ] || { echo "NOT ENOUGH SLAVES"; exit 1; } INITIAL_SIZE=`$MYSQL -P $port_0 -e "SHOW STATUS LIKE 'wsrep_cluster_size';" | cut -f 2` echo "Initial cluster size: $INITIAL_SIZE" createdb for u in $(seq 0 $USERS); do insert $port_0 & insert $port_1 & done wait ../../scripts/command.sh check cleandb $PORT_0 echo echo "Done!" echo ../../scripts/command.sh stop exit 0 percona-xtradb-cluster-galera/tests/regressions/lp1026181/run.sh0000755000000000000000000001143512247075736024704 0ustar rootroot00000000000000#!/bin/bash -ue ## # # lp:1026181 # https://bugs.launchpad.net/codership-mysql/+bug/1026181 # # BUG BACKGRPOUND: # # RSU method for DDL has been observed to cause intermittent hanging of # the client issueing the DDL. Original problem was that thread running # under TO isolation could not always abort victims. If there was a MDL # conflict with local DML processor it could happen that DDL query just kept # on waiting for the DML to end, and DML was waiting for commit monitor. # # Test scenarios: # A. DDL on same node as DML # # B. DDL on applier node # # # TEST SETUP: # - Cluster will be started # - sqlgen is run against one node in the cluster # - Connection to run the DDL is opened to one of the nodes # # SUCCESS CRITERIA # # If bug is present, DDL execution will hang # declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/kill.sh . $SCRIPTS/misc.sh declare sqlgen=$DIST_BASE/bin/sqlgen echo "##################################################################" echo "## regression test for lp1026181:" echo "##################################################################" echo "stopping cluster..." stop echo echo "starting node0, node1..." start_node "-d -g gcomm://$(extra_params 0)" 0 start_node "-d -g $(gcs_address 1) --slave_threads 4" 1 MYSQL="mysql --batch --silent --user=$DBMS_TEST_USER --password=$DBMS_TEST_PSWD test " declare -r host_0=${NODE_INCOMING_HOST[0]} declare -r host_1=${NODE_INCOMING_HOST[1]} declare -r port_0=${NODE_INCOMING_PORT[0]} declare -r port_1=${NODE_INCOMING_PORT[1]} MYSQL_0="$MYSQL --host=$host_0 --port=$port_0 -e" MYSQL_1="$MYSQL --host=$host_1 --port=$port_1 -e" $MYSQL_0 "SET GLOBAL wsrep_osu_method=TOI" 2>&1 $MYSQL_1 "SET GLOBAL wsrep_osu_method=TOI" 2>&1 echo -n "Populating test database... " $sqlgen --user=root --password=rootpass --host=$host_0 --port=$port_0 \ --create=1 --tables=4 --rows=20000 --users=1 --duration=0 > /dev/null echo "done" ROUNDS=50 SUCCESS=0 dml() { local host=$1 local port=$2 local users=$3 $sqlgen --user=root --password=rootpass --host=$host --port=$port \ --create=0 --users=$users --duration=600 > /dev/null 2>&1 & echo $! } alter() { local host=$1 local port=$2 local mysql_="$MYSQL --host=$host --port=$port -e" echo "running DDL for $host:$port..." for (( i=1; i<=$ROUNDS; i++ )); do echo "DDL round: $i" $mysql_ 'CREATE INDEX keyx ON test.comm00(x)' 2>&1 || : sleep 0.1 # this reduces the number of desync collisions $mysql_ 'DROP INDEX keyx ON test.comm00' 2>&1 || : sleep 0.1 done echo "... DDL over" } consistency_check() { #$SCRIPTS/command.sh check echo "consistency checking..." check0=$(check_node 0) check1=$(check_node 1) cs0=$(echo $check0 | cut -d" " -f 11) cs1=$(echo $check1 | cut -d" " -f 11) echo "node 0: $cs0" echo "node 1: $cs1" if test "$cs0" != "$cs1" then echo "Consistency check failed" echo "$check0 $cs0" echo "$check1 $cs1" exit 1 fi if test $? != 0 then echo "Consistency check failed" exit 1 fi echo echo "looks good so far..." echo } run_test() { local phase=$1 local dml_host=${NODE_INCOMING_HOST[$2]} local ddl_host=${NODE_INCOMING_HOST[$3]} local dml_port=${NODE_INCOMING_PORT[$2]} local ddl_port=${NODE_INCOMING_PORT[$3]} local users=$4 echo "##################################################################" echo "## test phase $phase" echo "##################################################################" echo echo "Starting sqlgen..." dml_pid=$(dml $dml_host $dml_port $users) sleep 3 $MYSQL --host=$ddl_host --port=$ddl_port -e " SET GLOBAL wsrep_osu_method=RSU " 2>&1 alter $ddl_host $ddl_port & alter_pid=$! echo "Waiting alter load to end (alter: $alter_pid, dml: $dml_pid)" wait $alter_pid echo "stopping dml load ($dml_pid)" kill $dml_pid echo "Waiting remaining load to end..." wait $MYSQL_0 'SHOW PROCESSLIST' && \ echo && \ $MYSQL_1 'SHOW PROCESSLIST' [ "$?" != "0" ] && echo "failed!" && exit 1 || : # eval $cleanup # what is this supposed to mean? } ######################################################### # # Test begins here # ######################################################### run_test A 0 1 1 run_test B 0 0 1 consistency_check run_test C 0 1 4 run_test D 0 0 4 consistency_check echo echo "Done!" echo # Automatic cleanup at the end of the test - bad parctice: who knows what # you may want to check afterwards? #stop_node 0 #stop_node 1 exit $SUCCESS percona-xtradb-cluster-galera/tests/regressions/lp1055961/run.sh0000755000000000000000000001052612247075736024714 0ustar rootroot00000000000000#!/bin/bash ## # # lp:lp1055961 # https://bugs.launchpad.net/codership-mysql/+bug/lp1055961 # # BUG BACKGRPOUND: # # autocommit query retrying was effective only for statements which were # really BF aborted. Autocommit statements, which tried to commit but # failed in certification, were never retried, they were immune to # wsrep_retry_autocommit setting. # # # TEST SETUP: # - two nodes to be started, wsrep_retry_autocommit set to 100 # - sqlgen is run against one node in the cluster # - sending autocommit updates on one sqlgen table to the other node # and checking that deadlock error should never come as result # # SUCCESS CRITERIA # # If bug is present, deadlock error will be observed # declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/kill.sh . $SCRIPTS/misc.sh declare sqlgen=$DIST_BASE/bin/sqlgen echo "##################################################################" echo "## regression test for lp1055961:" echo "##################################################################" echo "stopping cluster..." stop echo #echo "starting nodes..." #start echo "starting node0, node1..." start_node "-d -g gcomm://$(extra_params 0)" 0 start_node "-d -g $(gcs_address 1)" 1 MYSQL="mysql --batch --silent --user=$DBMS_TEST_USER --password=$DBMS_TEST_PSWD --host=$DBMS_HOST test " declare -r port_0=${NODE_INCOMING_PORT[0]} declare -r port_1=${NODE_INCOMING_PORT[1]} $MYSQL --port=$port_0 -e " SET GLOBAL wsrep_retry_autocommit=100 " 2>&1 ROUNDS=10 SUCCESS=0 sqlgen_load() { local port=$1 echo "running sqlgen..." $sqlgen --user=root --password=rootpass --host=0 --port=$port --create=1 --tables=1 --rows=20 --users=1 --selects=0 --inserts=0 --updates=100 --ac-frac=100 --duration=600 > /dev/null } deadlock_test() { local port=$1 echo "running AC updates for $port..." for (( i=1; i<=$ROUNDS; i++ )); do $MYSQL --port=$port -e " UPDATE test.comm00 SET z=99 WHERE p=0; " 2>&1 ret=$? if [ "$ret" != "0" ]; then echo "DEADLOCK ERROR"; SUCCESS=1; fi done echo "... Deadlock test over" } non_effective_update() { local port=$1 echo "running AC updates for $port..." for (( i=1; i<=$ROUNDS; i++ )); do $MYSQL --port=$port -e " UPDATE test.comm00 SET z=z WHERE p=0; " 2>&1 ret=$? if [ "$ret" != "0" ]; then echo "DEADLOCK ERROR FOR NON-EFFECTIVE UPDATE"; exit_code=1; fi done echo "... Non effective update test over" } run_test() { echo "##################################################################" echo "## test phase deadlock" echo "##################################################################" echo echo "Starting sqlgen..." sqlgen_load $port_1 & echo "sqlgen $sqlgen_pid" sleep 3 deadlock_test $port_0 echo "##################################################################" echo "## test phase non effective updates" echo "##################################################################" echo non_effective_update $port_0 echo echo "stopping sqlgen load ($sqlgen_pid)" kill $(pidof sqlgen) echo "Waiting remaining load to end..." wait $(pidof sqlgen) $MYSQL --port=$port_0 -e 'SHOW PROCESSLIST' echo $MYSQL --port=$port_1 -e 'SHOW PROCESSLIST' [ "$?" != "0" ] && echo "failed!" && exit 1 #$SCRIPTS/command.sh check echo "consistency checking..." check0=$(check_node 0) check1=$(check_node 1) cs0=$(echo $check0 | cut -d" " -f 11) cs1=$(echo $check1 | cut -d" " -f 11) echo "node 0: $cs0" echo "node 1: $cs1" if test "$cs0" != "$cs1" then echo "Consistency check failed" echo "$check0 $cs0" echo "$check1 $cs1" exit 1 fi if test $? != 0 then echo "Consistency check failed" exit 1 fi eval $cleanup echo echo "looks good so far..." echo } ######################################################### # # Test begins here # ######################################################### run_test echo echo "Done!" echo stop_node 0 stop_node 1 exit $SUCCESS percona-xtradb-cluster-galera/tests/regressions/lp1073220/run.sh0000755000000000000000000000272412247075736024701 0ustar rootroot00000000000000#!/bin/bash -e # # lp:861212 # https://bugs.launchpad.net/codership-mysql/+bug/861212 # # TEST SETUP # # This test starts two nodes and runs mixed DML/DDL load described in # # https://bugs.launchpad.net/codership-mysql/+bug/861212/comments/2 # # for some time. # # PARAMETERS # # Number of test rounds ROUNDS=${ROUNDS:-"200"} # Duration of single iteration DURATION=${DURATION:-"3"} # # declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/kill.sh . $SCRIPTS/misc.sh echo "##################################################################" echo "## regression test for lp:861212" echo "##################################################################" echo "restarting cluster to clean state" restart echo "starting load for $DURATION" seconds SQLGEN=${SQLGEN:-"$DIST_BASE/bin/sqlgen"} round=0 while test $round -lt $ROUNDS do LD_PRELOAD=$GLB_PRELOAD \ DYLD_INSERT_LIBRARIES=$GLB_PRELOAD \ DYLD_FORCE_FLAT_NAMESPACE=1 \ $SQLGEN --user $DBMS_TEST_USER --pswd $DBMS_TEST_PSWD --host $DBMS_HOST \ --port $DBMS_PORT --users $DBMS_CLIENTS --duration $DURATION \ --stat-interval 30 --rows 1000 --ac-frac 10 --rollbacks 0.1 \ --alters 1 echo "checking consistency" check || (sleep 5 && check) round=$(($round + 1)) done echo "stopping cluster" stop percona-xtradb-cluster-galera/tests/regressions/lp1089490/run.sh0000755000000000000000000001027712247075736024723 0ustar rootroot00000000000000#!/bin/bash ## # # lp:1089490 # https://bugs.launchpad.net/codership-mysql/+bug/1089490 # # BUG BACKGRPOUND: # # Foreign keys can cause slave crash if following conditions are met: # 1. foreign key constraint has been defined with CASCADE ON DELETE option # 2. foreign key constraint has mixed NULL options in referencing columns # (i.e. client column defined with NOT NULL option and parent column # with NULL option) # 3. slave is configured with multiple slave threads (wsrep_slave_threads > 1) # 4. work load has conflicting DELETE operations for parent and child table # # TEST SETUP: # - Two nodes are used in master slave mode. # - Slave is configured with 4 applier threads # - parent and child tables are created and populated # - test load runs two separate connections, where other session deletes # rows from parent table and other sessions delete rows from child table, # one by one # Due to the cascade on delete, option, the delete from parent table will issue # delete for the child table, and this cascaded delete may conflict with the # direct delete on child table # # SUCCESS CRITERIA # # If bug is present, slave will crash for not being able to delete a row from # child table # declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/kill.sh . $SCRIPTS/misc.sh echo "##################################################################" echo "## regression test for lp:1089490" echo "##################################################################" echo "stopping cluster" ../../scripts/command.sh stop echo echo "starting node0, node1..." ../../scripts/command.sh start "-d --slave_threads 4" MYSQL="mysql --batch --silent --user=$DBMS_TEST_USER --password=$DBMS_TEST_PSWD --host=$DBMS_HOST test " declare -r port_0=${NODE_INCOMING_PORT[0]} declare -r port_1=${NODE_INCOMING_PORT[1]} inserter() { local port=$1 for i in {1..1000}; do $MYSQL --port=$port -e " INSERT INTO test.lp1089490_parent(pk, j) VALUES($i, $i); INSERT INTO test.lp1089490_child(i,fk) VALUES ($i, $i); " 2>&1 done } delete_parent() { local port=$1 for i in {1..1000}; do $MYSQL --port=$port -e " DELETE FROM test.lp1089490_parent WHERE pk=$i; " 2>&1 done } delete_child() { local port=$1 for i in {1..1000}; do $MYSQL --port=$port -e " DELETE FROM test.lp1089490_child WHERE i=$i; " 2>&1 done } createdb() { $MYSQL --port=$port_0 -e "reset master;" $MYSQL --port=$port_1 -e "reset master;" $MYSQL --port=$port_0 -e " DROP TABLE IF EXISTS test.lp1089490_child;" $MYSQL --port=$port_0 -e ' DROP TABLE IF EXISTS test.lp1089490_parent;' $MYSQL --port=$port_0 -e ' CREATE TABLE test.lp1089490_parent ( pk INT PRIMARY KEY AUTO_INCREMENT, j int )' $MYSQL --port=$port_0 -e " CREATE TABLE test.lp1089490_child ( i INT PRIMARY KEY AUTO_INCREMENT, fk int, CONSTRAINT FOREIGN KEY (fk) REFERENCES test.lp1089490_parent(pk) ON DELETE CASCADE )" } ######################################################### # # Test begins here # ######################################################### threads=$($MYSQL --port=$port_1 -e "SHOW VARIABLES LIKE 'wsrep_slave_threads'") echo "applier check: $threads" [ "$threads" = "wsrep_slave_threads 4" ] || { echo "NOT ENOUGH SLAVES"; exit 1; } echo "Creating database..." createdb for i in {1..20}; do echo echo "### round $i ###" echo "populating database..." inserter $port_0 echo "starting delete for parent table" delete_parent $port_0 & declare parent_pid=$! echo "starting delete for child table" delete_child $port_0 & declare child_pid=$! echo "waiting load to end ($parent_pid $child_pid)" wait done $MYSQL --port=$port_0 -e 'SHOW PROCESSLIST' echo $MYSQL --port=$port_1 -e 'SHOW PROCESSLIST' [ "$?" != "0" ] && echo "failed!" && exit 1 $SCRIPTS/command.sh check echo echo "Done!" echo ../../scripts/command.sh stop exit 0 percona-xtradb-cluster-galera/tests/regressions/lp1100496/run.sh0000755000000000000000000001065012247075736024704 0ustar rootroot00000000000000#!/bin/bash ## # # lp:1100496 # https://bugs.launchpad.net/codership-mysql/+bug/1100496 # # BUG BACKGROUND: # # Foreign keys can cause slave crash if following conditions are met: # 1. foreign key column in child table and referenced column in parent # table have mixed types of CHAR and VARCHAR # 2. slave uses parallel applying # 3. work load contains concurrent direct deletes on parent table and deletes # on child table # Crash happens because, wsrep appends shared parent key (through FK processing) # and exclusive key (due to direct parent row delete) in different formats. # => certification does not detect the conflict and lets write sets to apply # in parallel. # # TEST SETUP: # - Two nodes are used in master slave mode. # - Slave is configured with 4 applier threads # - parent and child tables are created and populated # - test load runs one connection, which issues a large detete for child # table and one row delete for parent table. We try to make the applying # of child table delete to last so long that parent table delete gets to # apply in parallel. # # SUCCESS CRITERIA # # If bug is present, slave will crash for not being able to delete a row from # child table # declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/kill.sh . $SCRIPTS/misc.sh echo "##################################################################" echo "## regression test for lp:1100496" echo "##################################################################" echo "stopping cluster" ../../scripts/command.sh stop echo echo "starting node0, node1..." ../../scripts/command.sh start "-d --slave_threads 4" declare -r port_0=${NODE_INCOMING_PORT[0]} declare -r port_1=${NODE_INCOMING_PORT[1]} MYSQL="mysql --batch --silent --user=$DBMS_TEST_USER --password=$DBMS_TEST_PSWD --host=$DBMS_HOST --port=$port_0 test " populate() { local rows=$1 $MYSQL -e "INSERT INTO test.lp1100496_parent(pk) VALUES('Parent');" for ((i=1; i<$rows; i++)); do $MYSQL -e "$I1K" 2>&1 done } run_deletes() { $MYSQL -e "DELETE FROM test.lp1100496_child; DELETE FROM test.lp1100496_parent;" } check_cluster() { local original_size=$1 size=`$MYSQL -e "SHOW STATUS LIKE 'wsrep_cluster_size';" | cut -f 2` if [ "$size" != "$original_size" ]; then echo "cluster broken; $size $original_size" exit 1 fi } cleandb() { $MYSQL --port=$port_0 -e "reset master;" $MYSQL --port=$port_1 -e "reset master;" $MYSQL -e " DROP TABLE IF EXISTS test.lp1100496_child;" $MYSQL -e ' DROP TABLE IF EXISTS test.lp1100496_parent;' } createdb() { local charset=$1 cleandb $MYSQL --port=$port_0 -e " CREATE TABLE test.lp1100496_parent ( pk VARCHAR(30) PRIMARY KEY ) CHARACTER SET = $charset ENGINE=innodb" $MYSQL --port=$port_0 -e " CREATE TABLE test.lp1100496_child ( i INT PRIMARY KEY AUTO_INCREMENT, fk CHAR(20), CONSTRAINT FOREIGN KEY (fk) REFERENCES test.lp1100496_parent(pk) ) CHARACTER SET = $charset, ENGINE=innodb" } run_test() { local charset=$1 echo echo "## Testing character set: $charset" echo createdb $charset for i in 10 20 30 40 50 60; do echo echo "populating database, ${i}K rows..." populate $i echo "testing..." run_deletes check_cluster $INITIAL_SIZE done echo echo "## Consistency checks" echo $MYSQL --port=$port_0 -e 'SHOW PROCESSLIST' echo $MYSQL --port=$port_1 -e 'SHOW PROCESSLIST' [ "$?" != "0" ] && echo "failed!" && exit 1 wait_sync $NODE_LIST $SCRIPTS/command.sh check } ######################################################### # # Test begins here # ######################################################### threads=$($MYSQL --port=$port_1 -e "SHOW VARIABLES LIKE 'wsrep_slave_threads'") echo "applier check: $threads" [ "$threads" = "wsrep_slave_threads 4" ] || { echo "NOT ENOUGH SLAVES"; exit 1; } INITIAL_SIZE=`$MYSQL -e "SHOW STATUS LIKE 'wsrep_cluster_size';" | cut -f 2` echo "Initial cluster size: $INITIAL_SIZE" I1K="INSERT INTO test.lp1100496_child(fk) VALUES ('Parent')" for ((i=1; i<1000; i++)); do I1K="$I1K,('Parent')" done run_test latin1 run_test utf8 echo echo "Done!" echo ../../scripts/command.sh stop exit 0 percona-xtradb-cluster-galera/tests/regressions/lp1130888/run.sh0000755000000000000000000001011312247075736024706 0ustar rootroot00000000000000#!/bin/bash ## # # lp:1130888 # https://bugs.launchpad.net/codership-mysql/+bug/1130888 # # BUG BACKGROUND: # # wsrep appends shared key for FK parent references, when child table # is beign modified. This FK parent reference requires a read in parent table, # and essentially new shared lock on paretn table row. Now this extra lock can # enter in deadlock, and bad news is that existing code is note prepared to see # deadlocks at this point. This unresolved deadlock left the server in hanging # state. # # TEST SETUP: # - Two nodes are used in multi-master mode # - child/parent tables are created with cascading delete option # - node1 will get transactions modifying parent table # - node2 will get transactions modifying child table # # SUCCESS CRITERIA # # If bug is present, one node will hang # declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/kill.sh . $SCRIPTS/misc.sh echo "##################################################################" echo "## regression test for lp:1130888" echo "##################################################################" echo "stopping cluster" ../../scripts/command.sh stop echo echo "starting node0, node1..." ../../scripts/command.sh start "-d --slave_threads 4" declare -r port_0=${NODE_INCOMING_PORT[0]} declare -r port_1=${NODE_INCOMING_PORT[1]} MYSQL="mysql --batch --silent --user=$DBMS_TEST_USER --password=$DBMS_TEST_PSWD --host=$DBMS_HOST test " ROUNDS=1000 ROWS=5 echo "rounds=$ROUNDS" echo "rows=$ROWS" insert() { k PORT=$1 for j in $(seq 1 $ROWS); do $MYSQL --port=$PORT -e "INSERT INTO TRANSACTION(ID) VALUES ('$j')"; $MYSQL --port=$PORT -e "INSERT INTO TRANSACTIONLOG(IDTRANSACTION,IDMESSAGE) VALUES ('$j','$j')"; done } trxlog() { PORT=$1 for i in $(seq 1 $ROUNDS); do for j in $(seq 1 $ROWS); do $MYSQL --port=$PORT -e "DELETE FROM TRANSACTIONLOG WHERE IDTRANSACTION = '$j'; INSERT INTO TRANSACTIONLOG(IDTRANSACTION,IDMESSAGE) VALUES ('$j','$j')"; done done } trx() { PORT=$1 for i in $(seq 1 $ROUNDS); do for j in $(seq 1 $ROWS); do $MYSQL --port=$PORT -e "DELETE FROM TRANSACTION WHERE ID = '$j'; INSERT INTO TRANSACTION(ID) VALUES ('$j'); INSERT INTO TRANSACTIONLOG(IDTRANSACTION,IDMESSAGE) VALUES ('$j','$j')"; done done } createdb() { PORT=$1 $MYSQL --port=$PORT -e "CREATE TABLE IF NOT EXISTS TRANSACTION ( ID varchar(25) NOT NULL, MARCA datetime DEFAULT NULL, TIMEOUT int(11) DEFAULT NULL, DESCRIPCION varchar(255) DEFAULT NULL, PRIMARY KEY (ID) ) ENGINE=InnoDB DEFAULT CHARSET=latin1"; $MYSQL --port=$PORT -e "CREATE TABLE IF NOT EXISTS TRANSACTIONLOG ( IDTRANSACTION varchar(25) NOT NULL, IDMESSAGE varchar(25) NOT NULL, TIPO int(11) DEFAULT NULL, FECHA datetime DEFAULT NULL, PRIMARY KEY (IDTRANSACTION,IDMESSAGE), UNIQUE KEY IX_EBITRANSACTIONLOG (IDMESSAGE), CONSTRAINT FK_EBITRANSACTIONLOG_EBITRAN FOREIGN KEY (IDTRANSACTION) REFERENCES TRANSACTION (ID) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=latin1"; $MYSQL --port=$PORT -e "TRUNCATE TRANSACTIONLOG"; $MYSQL --port=$PORT -e "TRUNCATE TRANSACTION"; } cleandb() { PORT=$1 $MYSQL --port=$PORT -e " DROP TABLE IF EXISTS test.TRANSACTION; DROP TABLE IF EXISTS test.TRANSACTIONLOG" } ######################################################### # # Test begins here # ######################################################### threads=$($MYSQL --port=$port_1 -e "SHOW VARIABLES LIKE 'wsrep_slave_threads'") echo "applier check: $threads" [ "$threads" = "wsrep_slave_threads 4" ] || { echo "NOT ENOUGH SLAVES"; exit 1; } INITIAL_SIZE=`$MYSQL -P $port_0 -e "SHOW STATUS LIKE 'wsrep_cluster_size';" | cut -f 2` echo "Initial cluster size: $INITIAL_SIZE" createdb insert $port_0 trxlog $port_0 & trx $port_1 & wait ../../scripts/command.sh check cleandb $port_0 echo echo "Done!" echo ../../scripts/command.sh stop exit 0 percona-xtradb-cluster-galera/tests/regressions/lp1179361/run.sh0000755000000000000000000000636112247075736024717 0ustar rootroot00000000000000#!/bin/bash -ue ## # # lp:1179361 # https://bugs.launchpad.net/codership-mysql/+bug/ 1179361 # # BUG BACKGROUND: # # # TEST SETUP: # - N nodes are used in master-slave mode # - master gets INSERTs and DELETEs from two concurrent threads # # SUCCESS CRITERIA # # In case of success all nodes should stay alive # # If bug is present, one node will hang # declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/kill.sh . $SCRIPTS/misc.sh echo "##################################################################" echo "## regression test for lp:1179361" echo "##################################################################" echo "Restarting cluster" ../../scripts/command.sh restart "--slave_threads 16" MYSQL="mysql --batch --skip-column-names --silent --user=$DBMS_TEST_USER --password=$DBMS_TEST_PSWD" MYSQL0="$MYSQL --host=${NODE_INCOMING_HOST[0]} --port=${NODE_INCOMING_PORT[0]}" TABLE="test.lp1179361" cat << EOF | $MYSQL0 DROP TABLE IF EXISTS $TABLE; CREATE TABLE $TABLE ( obj_type varchar(16) NOT NULL DEFAULT '' COMMENT 'obj_type', obj_id varchar(20) NOT NULL COMMENT 'obj_type編號', tag varchar(255) NOT NULL, msno bigint(20) NOT NULL DEFAULT '0' COMMENT 'user id', terr_id int(11) NOT NULL COMMENT '地區編號', PRIMARY KEY (obj_type,obj_id,msno,tag), KEY obj_id (obj_id), KEY tag (tag), KEY msno (msno) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPRESSED; EOF ROUNDS=100000 echo "Rounds: $ROUNDS" # Checking that all nodes are alive check_nodes() { for N in $NODE_LIST do MYSQLN="$MYSQL --host=${NODE_INCOMING_HOST[$N]} --port=${NODE_INCOMING_PORT[$N]}" if ! $MYSQLN -e 'SELECT COUNT(*) FROM '$TABLE > /dev/null then echo "Node $N is dead, test failed" >&2 return 1 fi done } thread_insert() { local obj_type=0 local tag="'中文測試'" local terr_id=0 for i in $(seq 1 $ROUNDS) do local obj_id=$(( $RANDOM % 100 + 1 )) local msno=$(( $RANDOM % 100 + 1 )) echo "INSERT INTO $TABLE (obj_type, obj_id, tag, msno, terr_id)" \ "VALUES ($obj_type, $obj_id, $tag, $msno, $terr_id)" \ "ON DUPLICATE KEY UPDATE obj_type = obj_type;" if [ $(( $i % 1000 )) -eq 0 ] then echo "Insert thread: inserted $i" >&2 check_nodes || return 1 fi done } thread_delete() { for i in $(seq 1 $ROUNDS) do local count=`$MYSQL0 -e 'SELECT COUNT(*) FROM '$TABLE` if [ $count -gt 2000 ] then local limit=$(( $count / 2 )) $MYSQL0 -e \ "DELETE FROM $TABLE ORDER BY obj_type, obj_id, tag, msno LIMIT $limit;" echo "Delete thread: deleted $limit" >&2 fi sleep 0.5 done } thread_delete & delete_pid=$! trap "kill $delete_pid >/dev/null 2>&1 || true" TERM EXIT thread_insert | $MYSQL0 FAILED=${PIPESTATUS[0]} echo "Terminating delete thread..." kill $delete_pid && wait %% > /dev/null 2>&1 || : [ $FAILED -eq 0 ] && check_nodes || exit 1 check || (sleep 2; check) || (sleep 3; check) || exit 1 exit 0 percona-xtradb-cluster-galera/tests/regressions/lp1184034/run.sh0000755000000000000000000000141612247075736024704 0ustar rootroot00000000000000#!/bin/bash -ue # # https://bugs.launchpad.net/galera/+bug/1184034 # # BUG BACKGROUND # # Running # # nmap -sT -p # # causes uncaught exception in gcomm asio code # declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/kill.sh . $SCRIPTS/misc.sh echo "##################################################################" echo "## regression test for lp:1184034" echo "##################################################################" echo "Restarting cluster" ../../scripts/command.sh restart for ii in `seq 1 100` do nmap -sT -p ${NODE_GCS_PORT[0]} ${NODE_GCS_HOST[0]} done check_node 0 percona-xtradb-cluster-galera/tests/regressions/lp1206129/run.sh0000755000000000000000000000367512247075736024715 0ustar rootroot00000000000000#!/bin/bash -e # # lp:1206129 # # Verify that LOAD DATA INFILE is executed and data is replicated # over cluster. # # This test assumes that if the LOAD DATA would be processed by single # 500k row commit, it would take several seconds process that on slaves. # On the other hand it is assumed that if LOAD DATA splitting works as # expected, Galera flow control limits master load rate and final 10k # batches are committed nearly synchronously. # declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/kill.sh . $SCRIPTS/misc.sh echo "##################################################################" echo "## regression test for lp:1089490" echo "##################################################################" echo "restarting cluster" ../../scripts/command.sh restart MYSQL="mysql --user=$DBMS_TEST_USER --password=$DBMS_TEST_PSWD --host=${NODE_INCOMING_HOST[0]} --port=${NODE_INCOMING_PORT[0]} -Dtest" rm -f /tmp/lp1206129.dat $MYSQL -e "DROP TABLE IF EXISTS lp1206129; DROP PROCEDURE IF EXISTS populate; CREATE TABLE lp1206129 (a INT PRIMARY KEY); DELIMITER ;; CREATE PROCEDURE populate() BEGIN DECLARE i INT DEFAULT 0; WHILE i < 500000 DO INSERT INTO lp1206129 VALUES (i); SET i = i + 1; END WHILE; END;; DELIMITER ; BEGIN; CALL populate(); SELECT * FROM lp1206129 INTO OUTFILE '/tmp/lp1206129.dat'; COMMIT; SELECT SLEEP(0); TRUNCATE lp1206129; SELECT SLEEP(0); LOAD DATA INFILE '/tmp/lp1206129.dat' INTO TABLE lp1206129;" rm /tmp/lp1206129.dat sleep 2 ../../scripts/command.sh check $MYSQL -e "DROP TABLE lp1206129; DROP PROCEDURE populate;"percona-xtradb-cluster-galera/tests/regressions/lp518749/mysqlfs.sql0000644000000000000000000000623612247075736025722 0ustar rootroot00000000000000-- MySQL dump 10.11 -- -- Host: vs1 Database: -- ------------------------------------------------------ -- Server version 5.1.42-debug /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; /*!40103 SET TIME_ZONE='+00:00' */; /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -- -- Current Database: `mysqlfs` -- DROP DATABASE IF EXISTS mysqlfs; CREATE DATABASE /*!32312 IF NOT EXISTS*/ `mysqlfs` /*!40100 DEFAULT CHARACTER SET latin1 */; USE `mysqlfs`; -- -- Table structure for table `data_blocks` -- SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `data_blocks` ( `inode` bigint(20) NOT NULL, `seq` int(10) unsigned NOT NULL, `data` blob, PRIMARY KEY (`inode`,`seq`) ) ENGINE=InnoDB; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `data_blocks` -- INSERT INTO `data_blocks` VALUES (3,0,'1\n2\n3\n4\n'); -- -- Table structure for table `inodes` -- SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `inodes` ( `inode` bigint(20) NOT NULL, `inuse` int(11) NOT NULL DEFAULT '0', `deleted` tinyint(4) NOT NULL DEFAULT '0', `mode` int(11) NOT NULL DEFAULT '0', `uid` int(10) unsigned NOT NULL DEFAULT '0', `gid` int(10) unsigned NOT NULL DEFAULT '0', `atime` int(10) unsigned NOT NULL DEFAULT '0', `mtime` int(10) unsigned NOT NULL DEFAULT '0', `ctime` int(10) unsigned NOT NULL DEFAULT '0', `size` bigint(20) NOT NULL DEFAULT '0', PRIMARY KEY (`inode`), KEY `inode` (`inode`,`inuse`,`deleted`) ) ENGINE=InnoDB; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `inodes` -- INSERT INTO `inodes` VALUES (1,0,0,16877,0,0,1265561793,1265561793,1265561793,0); INSERT INTO `inodes` VALUES (3,0,0,33188,1000,1000,1265561852,1265561852,1265561852,8); /*!50003 SET @SAVE_SQL_MODE=@@SQL_MODE*/; DELIMITER ;; /*!50003 SET SESSION SQL_MODE="" */;; /*!50003 CREATE */ /*!50017 DEFINER=`root`@`localhost` */ /*!50003 TRIGGER `drop_data` AFTER DELETE ON `inodes` FOR EACH ROW BEGIN DELETE FROM data_blocks WHERE inode=OLD.inode; END */;; DELIMITER ; /*!50003 SET SESSION SQL_MODE=@SAVE_SQL_MODE*/; -- -- Table structure for table `tree` -- SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `tree` ( `inode` int(10) unsigned NOT NULL, `parent` int(10) unsigned DEFAULT NULL, `name` varchar(255) NOT NULL, UNIQUE KEY `name` (`name`,`parent`), KEY `inode` (`inode`), KEY `parent` (`parent`) ) ENGINE=InnoDB; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `tree` -- INSERT INTO `tree` VALUES (1,NULL,'/'); INSERT INTO `tree` VALUES (3,1,'foo'); UPDATE inodes SET inuse = inuse + 1 WHERE inode=3; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; percona-xtradb-cluster-galera/tests/regressions/lp518749/run.sh0000755000000000000000000000136112247075736024640 0ustar rootroot00000000000000#!/bin/bash -eu declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/kill.sh . $SCRIPTS/misc.sh echo "regression test for lp:518749" echo "restarting cluster" $SCRIPTS/command.sh restart mysql --user=$DBMS_ROOT_USER --password=$DBMS_ROOT_PSWD --host=${NODE_INCOMING_HOST[0]} --port=${NODE_INCOMING_PORT[0]} < mysqlfs.sql; check; ret=$?; if test $ret != 0 then echo "checksum failed"; exit 1; fi # Cleanup mysql --user=$DBMS_ROOT_USER --password=$DBMS_ROOT_PSWD --host=${NODE_INCOMING_HOST[0]} --port=${NODE_INCOMING_PORT[0]} -e "DROP DATABASE mysqlfs"; $SCRIPTS/command.sh stoppercona-xtradb-cluster-galera/tests/regressions/lp587170/run.sh0000755000000000000000000000202412247075736024627 0ustar rootroot00000000000000#!/bin/bash -e # # lp:587170 # # Verify that ALTER TABLE ... AUTO_INCREMENT produces consistent results. # declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/kill.sh . $SCRIPTS/misc.sh echo "##################################################################" echo "## regression test for lp:1089490" echo "##################################################################" echo "restarting cluster" ../../scripts/command.sh restart MYSQL="mysql --user=$DBMS_TEST_USER --password=$DBMS_TEST_PSWD --host=${NODE_INCOMING_HOST[0]} --port=${NODE_INCOMING_PORT[0]} -Dtest" $MYSQL -e "DROP TABLE IF EXISTS lp587170; CREATE TABLE lp587170 (i INT); INSERT INTO lp587170 VALUES (1),(2),(3),(4),(5); ALTER TABLE lp587170 ADD id INT NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST;" ../../scripts/command.sh check $MYSQL -e "DROP TABLE lp587170" percona-xtradb-cluster-galera/tests/regressions/lp847353/run.sh0000755000000000000000000000541012247075736024633 0ustar rootroot00000000000000#!/bin/bash # lp:847353 # https://bugs.launchpad.net/codership-mysql/+bug/847353 # # BUG BACKGRPOUND: # # FTWRL needs to pause replicator from committing. FTWRL will take global read # lock and this can cause a deadlock between applying trasnactions and FTWRL # execution. When FTWRL has called wsrep->pause(), all earlier applying tasks # must be able to complete. # # TEST SETUP: # # This test starts two nodes to play with. # It uses sqlgen to run load against both nodes and one process running # FTWRL agains one node only. # # TEST PROCESSES: # # Test creates two sqlgen processes # sqlgen: regular sqlgen load. First sqlgen process will create # the tables, latyter one just sends DML load in. # FTWRL: FTWRL keeps on sending FLUSTE TABLES WITH READ LOCK # statements agains other cluster node # # SUCCESS CRITERIA # # If bug is present, either one node may hang # declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/kill.sh . $SCRIPTS/misc.sh echo "##################################################################" echo "## regression test for lp:847353" echo "##################################################################" echo "stopping cluster" ../../scripts/command.sh stop echo echo "starting node0, node1..." ../../scripts/command.sh start_node "-d -g gcomm://$(extra_params 0)" 0 ../../scripts/command.sh start_node "-d -g $(gcs_address 1)" 1 # Start load SQLGEN=${SQLGEN:-"$DIST_BASE/bin/sqlgen"} LD_PRELOAD=$GLB_PRELOAD \ DYLD_INSERT_LIBRARIES=$GLB_PRELOAD \ DYLD_FORCE_FLAT_NAMESPACE=1 \ $SQLGEN --user $DBMS_TEST_USER --pswd $DBMS_TEST_PSWD --host ${NODE_INCOMING_HOST[0]} \ --port ${NODE_INCOMING_PORT[0]} --users 1 --duration 300 \ --stat-interval 99999999 --sess-min 999999 --sess-max 999999 \ --rollbacks 0.1 \ >/dev/null 2>$BASE_RUN/lp.err & declare -r sqlgen1_pid=$! sleep 0.2 # this connects strictly to one node so no libglb preloading $SQLGEN --user $DBMS_TEST_USER --pswd $DBMS_TEST_PSWD --host ${NODE_INCOMING_HOST[1]} \ --port ${NODE_INCOMING_PORT[1]} --users 1 --duration 300 \ --stat-interval 99999999 --sess-min 999999 --sess-max 999999 \ --rollbacks 0.1 --create 0 \ >/dev/null 2>$BASE_RUN/lp.err & declare -r sqlgen2_pid=$! for i in {1..150}; do mysql --show-warnings --user=$DBMS_TEST_USER --password=$DBMS_TEST_PSWD --host=${NODE_INCOMING_HOST[1]} --port=${NODE_INCOMING_PORT[1]} test -e 'FLUSH TABLES WITH READ LOCK'; sleep 0.5; done echo "waiting sqlgens ($sqlgen1_pid $sqlgen2_pid) to complete" wait echo "Done!" ../../scripts/command.sh stop_node 0 ../../scripts/command.sh stop_node 1 exit percona-xtradb-cluster-galera/tests/regressions/lp861212/run.sh0000755000000000000000000000246612247075736024631 0ustar rootroot00000000000000#!/bin/bash -e # # lp:861212 # https://bugs.launchpad.net/codership-mysql/+bug/861212 # # TEST SETUP # # This test starts two nodes and runs mixed DML/DDL load described in # # https://bugs.launchpad.net/codership-mysql/+bug/861212/comments/2 # # for some time. # # PARAMETERS # # Duration of test run DURATION=${DURATION:-"600"} # # declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/kill.sh . $SCRIPTS/misc.sh echo "##################################################################" echo "## regression test for lp:861212" echo "##################################################################" echo "restarting cluster to clean state" restart echo "starting load for $DURATION" seconds SQLGEN=${SQLGEN:-"$DIST_BASE/bin/sqlgen"} LD_PRELOAD=$GLB_PRELOAD \ DYLD_INSERT_LIBRARIES=$GLB_PRELOAD \ DYLD_FORCE_FLAT_NAMESPACE=1 \ $SQLGEN --user $DBMS_TEST_USER --pswd $DBMS_TEST_PSWD --host $DBMS_HOST \ --port $DBMS_PORT --users $DBMS_CLIENTS --duration $DURATION \ --stat-interval 30 --rows 1000 --ac-frac 10 --rollbacks 0.1 \ --alters 0.001 echo "checking consistency" check || (sleep 5 && check) echo "stopping cluster" stop percona-xtradb-cluster-galera/tests/regressions/lp900816/run.sh0000755000000000000000000000103112247075736024620 0ustar rootroot00000000000000#!/bin/sh -uex PORT=3306 HOST=127.0.0.1 USER=root PSWD=rootpass DB=test TABLE=nopk TRIES=1000 MYSQL="mysql -u$USER -p$PSWD -h$HOST -P$PORT $DB" CREATE=" DROP TABLE IF EXISTS $DB.$TABLE; CREATE TABLE $DB.$TABLE (i INT, j INT);" INSERT="INSERT INTO $DB.$TABLE VALUES (1, 0),(2,0);" UPDATE="UPDATE $DB.$TABLE SET j=j+1;" DELETE="DELETE FROM $DB.$TABLE;" echo $CREATE | $MYSQL for i in $(seq 1 $TRIES) do echo $INSERT | $MYSQL echo $UPDATE | $MYSQL >> update.log 2>&1 & echo $DELETE | $MYSQL done echo "$i tries passed" percona-xtradb-cluster-galera/tests/regressions/lp909155/run.sh0000755000000000000000000001024312247075736024632 0ustar rootroot00000000000000#!/bin/bash ## # # lp:909155 # https://bugs.launchpad.net/codership-mysql/+bug/909155 # # BUG BACKGRPOUND: # # under certain situations, DROP TABLE processing may be postponed to happen # in background thread. This can be fatal for galera processing, as DROP # needs to be processed under total order isolation and has strict requirements # for when the processing begins and ends. # # TEST SETUP: # # This test starts two nodes to play with. # It creates one table with AC primary key: test.t # # TEST PROCESSES: # # Test creates one db dropper process and two inserter process: # inserter: keeps on insertting rows in the test table # the two inserters target separate cluster nodes # This way the dropper will conflict both with local state # and slave inserts # dropper: dropper process runs drop table; create table sequences # for the test table. When table is dropped, local inserts # keep on failing until the table is re-created again. # There is small sleep after CREATE # # SUCCESS CRITERIA # # If bug is present, either one node can crash. When DROP processing happens in # background, the drop can happen in unpredictable time, and slave applier # may fail to apply an insert => this will crash the slave # It is a good idea to check mysql logs as well. Backgrounded DROP will cause # the following CREATE TABLE to fail. galera apply processor tolerates failures # for DDL, and this type of error does not cause crash. However, there is # slave error in the log. This latter symptom is much more probable than # actual crash. # declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/kill.sh . $SCRIPTS/misc.sh echo "##################################################################" echo "## regression test for lp:909155" echo "##################################################################" echo "stopping cluster" ../../scripts/command.sh stop echo echo "starting node0, node1..." ../../scripts/command.sh start_node "-d -g gcomm://$(extra_params 0)" 0 ../../scripts/command.sh start_node "-d -g $(gcs_address 1)" 1 MYSQL="mysql --batch --silent --user=$DBMS_TEST_USER --password=$DBMS_TEST_PSWD --host=$DBMS_HOST test " #declare -r port_0=$(( DBMS_PORT )) #declare -r port_1=$(( DBMS_PORT + 1)) declare -r port_0=${NODE_INCOMING_PORT[0]} declare -r port_1=${NODE_INCOMING_PORT[1]} inserter() { local port=$1 for i in {1..10000}; do $MYSQL --port=$port -e "INSERT INTO test.t(j) VALUES($i)" 2>&1 | grep -v "ERROR 1146"; done } createdb() { $MYSQL --port=$port_0 -e 'DROP TABLE IF EXISTS test.t; CREATE TABLE test.t(i INT PRIMARY KEY AUTO_INCREMENT, j int)' } dropper() { for i in {1..200}; do createdb; sleep 0.2; done } echo "Creating database..." createdb echo echo "### Phase 1, DML & DDL => node0 ###" echo echo "starting inserter for port $port_0" inserter $port_0 & declare inserter1_pid=$! echo "starting dropper" dropper & declare dropper_pid=$! echo "waiting phase 1 load to end ($dropper_pid $inserter1_pid)" wait $MYSQL --port=$port_0 -e 'SHOW PROCESSLIST' echo $MYSQL --port=$port_1 -e 'SHOW PROCESSLIST' echo echo "### Phase 2, DML => node1 DDL => node0 ###" echo echo "starting inserter for port $port_1" inserter $port_1 & declare inserter2_pid=$! echo "starting dropper" dropper & dropper_pid=$! echo "waiting phase 2 load to end ($dropper_pid $inserter2_pid)" wait $MYSQL --port=$port_0 -e 'SHOW PROCESSLIST' echo $MYSQL --port=$port_1 -e 'SHOW PROCESSLIST' echo echo "### Phase 3, DML => node0, node1 DDL => node0 ###" echo echo "starting inserter for port $port_0" inserter $port_0 & inserter1_pid=$! echo "starting inserter for port $port_1" inserter $port_1 & inserter2_pid=$! echo "starting dropper" dropper & dropper_pid=$! echo "waiting phase 3 load to end ($dropper_pid $inserter1_pid $inserter2_pid)" wait $MYSQL --port=$port_0 -e 'SHOW PROCESSLIST' echo $MYSQL --port=$port_1 -e 'SHOW PROCESSLIST' echo echo "Done!" echo ../../scripts/command.sh stop_node 0 ../../scripts/command.sh stop_node 1 exit percona-xtradb-cluster-galera/tests/regressions/lp917416/run.sh0000755000000000000000000001126612247075736024637 0ustar rootroot00000000000000#!/bin/bash ## # # lp:917416 # https://bugs.launchpad.net/codership-mysql/+bug/917416 # # BUG BACKGROUND: # # BF processing can kill local transactins doing only reads. One situation # where this happens very probably is when DDL is run under TO isolation # and some local transaction tries to read from mysql database. # Native MySQL uses thr locks to serialize such access and reads are not # aborted, so we have a transaparency issue here. And there are use cases # especially related to SP's and functions where proc table is consulted # for available routines SELECT ROUTINE_TYPE FROM INFORMATION_SCHEMA.ROUTINES # WHERE SPECIFIC_NAME = '' etc... # # TEST SETUP: # # This test starts two nodes to play with # # # TEST PROCESSES: # # Test creates one DDL process and one mysql schema reader process: # reader: keeps on reading mysql.user table # the two inserters target separate cluster nodes # This way the dropper will conflict both with local state # and slave inserts # DDL: DDL process creates and drops one user to cause locking # needs for mysql.user table # # SUCCESS CRITERIA # # If bug is present, the select process will encounter many deadlocks # due to BF aborting # declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/kill.sh . $SCRIPTS/misc.sh echo "##################################################################" echo "## regression test for lp:917416" echo "##################################################################" echo "stopping cluster" ../../scripts/command.sh stop echo echo "starting node0, node1..." ../../scripts/command.sh start_node "-d -g gcomm://$(extra_params 0)" 0 ../../scripts/command.sh start_node "-d -g $(gcs_address 1)" 1 MYSQL="mysql --batch --silent --user=$DBMS_TEST_USER --password=$DBMS_TEST_PSWD --host=$DBMS_HOST test " declare -r port_0=${NODE_INCOMING_PORT[0]} declare -r port_1=${NODE_INCOMING_PORT[1]} declare -r ROUNDS=10000 declare -r ERROR_LIMIT=10 declare errors=0 declare success=0 ####################### # Processes ######################## send_sql() { local port=$1 local type=$2 local rounds=$3 local sleep=$4 local SQL=$5 for (( i=1; i<$rounds; i++ )); do $MYSQL --port=$port -e "$SQL" 2>&1 > /tmp/lp917416.out ret=$? [ $ret -ne 0 ] && \ echo "SELECT failed ($ret $i) " && \ cat /tmp/lp917416.out && \ success=1 && \ errors=$(( $errors + 1 )) [ $errors -gt $ERROR_LIMIT ] && echo "$type STOPPED" && exit 1 sleep $sleep done } reader_m() { local port=$1 send_sql $port "SELECT-M" $ROUNDS 0 \ 'select user,host from mysql.user limit 1' echo "mysql reader complete `date`" } ddl_m() { send_sql $port_0 "DDL-M" $ROUNDS 0 \ 'CREATE USER seppo; DROP USER seppo; FLUSH PRIVILEGES' echo "mysql DDL complete `date`" } # # InnODB reader and DDL # reader_i() { local port=$1 send_sql $port "SELECT-I" $ROUNDS 0 'select i from test.t917416 limit 1' echo "innodb reader complete `date`" } ddl_i() { send_sql $port_0 "DDL-I" $(( $ROUNDS / 20 )) 0.1 \ 'ALTER TABLE t917416 ADD COLUMN (j int); ALTER TABLE t917416 DROP COLUMN j' echo "innodb DDL complete `date`" } ####################### # Main, init ######################## echo "Creating database..." $MYSQL --port=$port_0 -e 'DROP USER seppo; FLUSH PRIVILEGES' 2>&1 > /dev/null $MYSQL --port=$port_0 -e 'DROP TABLE IF EXISTS test.t917416' $MYSQL --port=$port_0 -e 'CREATE TABLE test.t917416(i int) engine=innodb' ####################### # Phase 1 ######################## echo echo "### Phase 1, SELECT & DDL => node0 ###" echo echo "starting reader for port $port_0" reader_m $port_0 & declare reader_pid=$! echo "starting ddl" ddl_m & declare ddl_pid=$! echo "waiting phase 1 load to end (PIDs: $ddl_pid $reader_pid)" wait echo echo "Processlist now:" $MYSQL --port=$port_0 -e 'SHOW PROCESSLIST' echo ####################### # Phase 2 ######################## echo echo "### Phase 2, InnoDB SELECT & ALTER => node0 ###" echo errors=0 echo "starting reader for port $port_0" reader_i $port_0 & reader_pid=$! echo "starting ddl" ddl_i & ddl_pid=$! echo "waiting phase 2 load to end (PIDs: $ddl_pid $reader_pid)" wait echo echo "Processlist now:" $MYSQL --port=$port_0 -e 'SHOW PROCESSLIST' echo ####################### # Cleanup ######################## $MYSQL --port=$port_0 -e 'DROP TABLE t917416' ; ../../scripts/command.sh stop_node 0 ../../scripts/command.sh stop_node 1 exit $successpercona-xtradb-cluster-galera/tests/regressions/lp922757/run.sh0000755000000000000000000001332012247075736024634 0ustar rootroot00000000000000#!/bin/bash ## # # lp:922757 # https://bugs.launchpad.net/codership-mysql/+bug/922757 # # BUG BACKGROUND: # # Foreign key constrainst were taken in consideration so that both parent # and child key were populated in cert index, i.e. they were recorded as # write access for both tables. # This caused major performance issues due to unnecessary cert failures. # A general use case has it that one parent table is referenced from many # child tables. Transactions updating such separate child rows had conflciting # write sets in the parent reference and caused a big number of certification # aborts. # # TEST SETUP: # # This test starts two nodes to play with. # A child table - parent table pair is created and populated with rows # so that we have only one parent row and several child rows referencing # the parent. # # # TEST PROCESSES: # # Test creates several update processes updating separate child table rows # updater: keeps on updating same child table with altering values # # SUCCESS CRITERIA # # If bug is present, the update process will encounter many deadlocks # due to cert failures # declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/kill.sh . $SCRIPTS/misc.sh echo "##################################################################" echo "## regression test for lp:922757" echo "##################################################################" echo "stopping cluster" ../../scripts/command.sh stop #echo "stopping node0, node1..." #../../scripts/command.sh stop_node 0 #../../scripts/command.sh stop_node 1 echo echo "starting node0, node1..." ../../scripts/command.sh start_node "-d -g gcomm://$(extra_params 0)" 0 ../../scripts/command.sh start_node "-d -g $(gcs_address 1)" 1 MYSQL="mysql --batch --silent --user=$DBMS_TEST_USER --password=$DBMS_TEST_PSWD -Dtest " declare -r host_0=${NODE_INCOMING_HOST[0]} declare -r port_0=${NODE_INCOMING_PORT[0]} declare -r host_1=${NODE_INCOMING_HOST[1]} declare -r port_1=${NODE_INCOMING_PORT[1]} declare -r ROUNDS=1000 declare -r ERROR_LIMIT=10 declare success=0 declare err_cnt=0 declare ok_cnt=0 ####################### # Processes ######################## updater() { local key=$1 local host=$2 local port=$3 local errors=0 echo "updater, key: $key, port: $port starting" for (( i=1; i<$ROUNDS; i++ )); do fk=$(( $i % 2 )) $MYSQL --host=$host --port=$port -e "UPDATE test.lp922757child set fk=$fk WHERE pk=$key" 2>&1 > /tmp/lp922757.out ret=$? if (( $ret != 0 )); then echo "UPDATE failed, ret=$ret key=$key round=$i " cat /tmp/lp922757.out success=1 errors=$(( $errors + 1 )) err_cnt=$(( $err_cnt + 1 )) else ok_cnt=$(( $ok_cnt + 1 )) fi [ $errors -gt $ERROR_LIMIT ] && echo "update STOPPED, key=$key" && exit 1 done echo "updater, key: $key, port: $port ending" } ####################### # Main, init ######################## echo "Creating database..." $MYSQL --host=$host_0 --port=$port_0 -e 'DROP TABLE IF EXISTS test.lp922757child' $MYSQL --host=$host_0 --port=$port_0 -e 'DROP TABLE IF EXISTS test.lp922757parent' $MYSQL --host=$host_0 --port=$port_0 -e 'CREATE TABLE test.lp922757parent(pk int primary key) engine=innodb' $MYSQL --host=$host_0 --port=$port_0 -e 'CREATE TABLE test.lp922757child(pk int primary key, fk int, v int, foreign key (fk) references test.lp922757parent(pk)) engine=innodb' $MYSQL --host=$host_0 --port=$port_0 -e 'INSERT INTO test.lp922757parent(pk) VALUES (0)' $MYSQL --host=$host_0 --port=$port_0 -e 'INSERT INTO test.lp922757parent(pk) VALUES (1)' $MYSQL --host=$host_0 --port=$port_0 -e 'INSERT INTO test.lp922757child(pk,fk,v) VALUES (1,1,0)' $MYSQL --host=$host_0 --port=$port_0 -e 'INSERT INTO test.lp922757child(pk,fk,v) VALUES (2,1,0)' $MYSQL --host=$host_0 --port=$port_0 -e 'INSERT INTO test.lp922757child(pk,fk,v) VALUES (3,1,0)' $MYSQL --host=$host_0 --port=$port_0 -e 'INSERT INTO test.lp922757child(pk,fk,v) VALUES (4,1,0)' $MYSQL --host=$host_0 --port=$port_0 -e 'INSERT INTO test.lp922757child(pk,fk,v) VALUES (5,1,0)' $MYSQL --host=$host_0 --port=$port_0 -e 'INSERT INTO test.lp922757child(pk,fk,v) VALUES (6,1,0)' $MYSQL --host=$host_0 --port=$port_0 -e 'INSERT INTO test.lp922757child(pk,fk,v) VALUES (7,1,0)' $MYSQL --host=$host_0 --port=$port_0 -e 'INSERT INTO test.lp922757child(pk,fk,v) VALUES (8,1,0)' $MYSQL --host=$host_0 --port=$port_0 -e 'INSERT INTO test.lp922757child(pk,fk,v) VALUES (9,1,0)' $MYSQL --host=$host_0 --port=$port_0 -e 'INSERT INTO test.lp922757child(pk,fk,v) VALUES (10,1,0)' ####################### # Phase 1 ######################## echo echo "### Phase 1, launch updaters" echo updater 1 $host_0 $port_0 & declare p1=$! updater 2 $host_1 $port_1 & declare p2=$! updater 3 $host_0 $port_0 & declare p3=$! updater 4 $host_1 $port_1 & declare p4=$! updater 5 $host_0 $port_0 & declare p5=$! updater 6 $host_1 $port_1 & declare p6=$! updater 7 $host_0 $port_0 & declare p7=$! updater 8 $host_1 $port_1 & declare p8=$! updater 9 $host_0 $port_0 & declare p9=$! updater 10 $host_1 $port_1 & declare p10=$! echo "waiting load to end (PIDs $p1 $p2 $p3 $p4 $p5 $p6 $p7 $p8 $p9 $p10)" wait echo echo "total failed : $err_cnt" echo "total succeeded: $ok_cnt" echo echo echo "Processlist now:" $MYSQL --host=$host_0 --port=$port_0 -e 'SHOW PROCESSLIST' echo ####################### # Cleanup ######################## $MYSQL --host=$host_0 --port=$port_0 -e 'DROP TABLE test.lp922757child' $MYSQL --host=$host_0 --port=$port_0 -e 'DROP TABLE test.lp922757parent' ../../scripts/command.sh stop_node 0 ../../scripts/command.sh stop_node 1 exit $successpercona-xtradb-cluster-galera/tests/regressions/lp928150/run.sh0000755000000000000000000000231312247075736024625 0ustar rootroot00000000000000#!/bin/bash # # lp:928150 # https://bugs.launchpad.net/codership-mysql/+bug/928150 # # TEST SETUP # # This test starts the cluster and runs 100% autocommit load. # # PARAMETERS # # Duration of test run DURATION=${DURATION:-"600"} # # set -e declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/kill.sh . $SCRIPTS/misc.sh echo "##################################################################" echo "## regression test for lp:928150" echo "##################################################################" echo "restarting cluster to clean state" restart echo "starting load for $DURATION" seconds SQLGEN=${SQLGEN:-"$DIST_BASE/bin/sqlgen"} LD_PRELOAD=$GLB_PRELOAD \ DYLD_INSERT_LIBRARIES=$GLB_PRELOAD \ DYLD_FORCE_FLAT_NAMESPACE=1 \ $SQLGEN --user $DBMS_TEST_USER --pswd $DBMS_TEST_PSWD --host $DBMS_HOST \ --port $DBMS_PORT --users $DBMS_CLIENTS --duration $DURATION \ --stat-interval 30 --sess-min 999999 --sess-max 999999 \ --rollbacks 0.1 --ac-frac 100 echo "checking consistency" check echo "stopping cluster" stop percona-xtradb-cluster-galera/tests/regressions/lp930221/run.sh0000755000000000000000000000342312247075736024620 0ustar rootroot00000000000000#!/bin/bash # # lp:930221 # https://bugs.launchpad.net/codership-mysql/+bug/930221 # # TEST # # This test starts two nodes, runs sqlgen against the first and # FTWRL against the second. Finally consistency is checked. # # TEST OUTCOME # # If bug is present, test will hang. # # PARAMETERS # # How many rounds to run FTWRL ROUNDS=${ROUNDS:-10000} # declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/kill.sh . $SCRIPTS/misc.sh echo "##################################################################" echo "## regression test for lp:930221" echo "##################################################################" echo "starting two nodes" stop start_node "-d -g gcomm://$(extra_params 0)" 0 start_node "-d -g $(gcs_address 1)" 1 echo "starting sqlgen load for $DURATION" seconds SQLGEN=${SQLGEN:-"$DIST_BASE/bin/sqlgen"} $SQLGEN --user $DBMS_TEST_USER --pswd $DBMS_TEST_PSWD \ --host ${NODE_INCOMING_HOST[0]} \ --port ${NODE_INCOMING_PORT[0]} \ --users $DBMS_CLIENTS --duration 99999999 \ --stat-interval 30 --sess-min 999999 --sess-max 999999 \ --rows 1000 --ac-frac 100 & sqlgen_pid=$! trap "kill $sqlgen_pid" EXIT echo "waiting 10 seconds for sqlgen to start)" sleep 10 echo "starting FTWRL load" cnt=$ROUNDS while test $cnt != 0 do mysql -u$DBMS_ROOT_USER -p$DBMS_ROOT_PSWD \ --host ${NODE_INCOMING_HOST[1]} --port ${NODE_INCOMING_PORT[1]} \ -e "FLUSH TABLES WITH READ LOCK" cnt=$(($cnt - 1)) done kill $sqlgen_pid || true echo "checking consistency" check echo "stopping cluster" stop percona-xtradb-cluster-galera/tests/regressions/lp959512/run.sh0000755000000000000000000000337312247075736024642 0ustar rootroot00000000000000#!/bin/bash ## # lp:959512 # https://bugs.launchpad.net/codership-mysql/+bug/959512 # # Description: # # Running the statements described below caused slave nodes to crash. # The reson was that multi statement transaction produced empty write # set, which caused IO cache not to be reset at transaction cleanup. # Following INSERT INTO foo failed then to replicate because IO cache # remained in read mode, following slave crashes on final UPDATE foo # because row to be updated could not be found. # ## set -e declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" # . $SCRIPTS/jobs.sh # . $SCRIPTS/action.sh # . $SCRIPTS/kill.sh # . $SCRIPTS/misc.sh # restart the cluster $SCRIPTS/command.sh restart STMTS="DROP TABLE IF EXISTS variable; DROP TABLE IF EXISTS foo; CREATE TABLE variable ( name varchar(128) NOT NULL DEFAULT '' COMMENT 'The name of the variable.', value longblob NOT NULL COMMENT 'The value of the variable.', PRIMARY KEY (name) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Named variable/value pairs created by Drupal core or any...'; CREATE TABLE foo (a int); INSERT INTO variable (name, value) VALUES ('menu_expanded', 'a:0:{}'); START TRANSACTION; SELECT 1 AS expression FROM variable variable WHERE ( (name = 'menu_expanded') ) FOR UPDATE; UPDATE variable SET value='a:0:{}' WHERE ( (name = 'menu_expanded') ); COMMIT; INSERT INTO foo VALUES (1); UPDATE foo SET a = 2 WHERE a = 1;" MYSQL="mysql --batch --silent -uroot -prootpass -h${NODE_INCOMING_HOST[0]} -P${NODE_INCOMING_PORT[0]}" $MYSQL -Dtest -e "$STMTS" $SCRIPTS/command.sh check if test $? != 0 then echo "test failed" exit 1 fi $SCRIPTS/command.sh stop percona-xtradb-cluster-galera/tests/regressions/lp963734/run.sh0000755000000000000000000000423712247075736024643 0ustar rootroot00000000000000#!/bin/bash # # Regression test for lp:963734 # https://bugs.launchpad.net/codership-mysql/+bug/963734 # # TEST DESCRIPTION: # # This test runs create table, table rename, insert into in single # session. If alter table rename is missing table keys for both table names, # slave will eventually crash on insert because replicator allows parallel # applying of insert when alter is still running. # # Table renaming is tested with both ALTER TABLE ... RENAME and # RENAME TABLE ... TO. # set -e declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" TABLE_BASENAME="lp963734" ALTER_STMTS="DROP TABLE IF EXISTS ${TABLE_BASENAME}_a, ${TABLE_BASENAME}_b; CREATE TABLE ${TABLE_BASENAME}_a (a int); ALTER TABLE ${TABLE_BASENAME}_a RENAME ${TABLE_BASENAME}_b; INSERT INTO ${TABLE_BASENAME}_b VALUES (1); DROP TABLE ${TABLE_BASENAME}_b;" RENAME_STMTS="DROP TABLE IF EXISTS ${TABLE_BASENAME}_a, ${TABLE_BASENAME}_b; CREATE TABLE ${TABLE_BASENAME}_a (a int); RENAME TABLE ${TABLE_BASENAME}_a TO ${TABLE_BASENAME}_b; INSERT INTO ${TABLE_BASENAME}_b VALUES (1); DROP TABLE ${TABLE_BASENAME}_b;" ROUNDS=${ROUNDS:-1000} cnt=$ROUNDS $SCRIPTS/command.sh restart while test $cnt != 0 do mysql -u$DBMS_TEST_USER -p$DBMS_TEST_PSWD \ -h${NODE_INCOMING_HOST[0]} \ -P${NODE_INCOMING_PORT[0]} \ -Dtest \ -e "$ALTER_STMTS" cnt=$(($cnt - 1)) done while test $cnt != 0 do mysql -u$DBMS_TEST_USER -p$DBMS_TEST_PSWD \ -h${NODE_INCOMING_HOST[0]} \ -P${NODE_INCOMING_PORT[0]} \ -Dtest \ -e "$RENAME_STMTS" cnt=$(($cnt - 1)) done $SCRIPTS/command.sh check ret=$? if test $ret != 0 then echo "Test failed" exit 1 fi node_cnt=0 for node in $NODE_LIST do node_cnt=$(($node_cnt + 1)) done for node in $NODE_LIST do cs=$($SCRIPTS/command.sh cluster_status $node) if test $cs != "Primary:$node_cnt" then echo "invalid cluster status for $node: $cs" echo "test failed" exit 1 fi done $SCRIPTS/command.sh stop percona-xtradb-cluster-galera/tests/regressions/lp966950/run.sh0000755000000000000000000000234412247075736024643 0ustar rootroot00000000000000#!/bin/bash # # DESCRIPTION: # # Regression test to reproduce lp:966950 # See https://bugs.launchpad.net/codership-mysql/+bug/966950 # set -e declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" $SCRIPTS/command.sh restart MYSQL="mysql -u$DBMS_TEST_USER -p$DBMS_TEST_PSWD -h${NODE_INCOMING_HOST[0]} -P${NODE_INCOMING_PORT[0]} -Dtest" TABLE_PREFIX="lp966950_" PARENT_TABLE="${TABLE_PREFIX}parent" CHILD_TABLE="${TABLE_PREFIX}child" $MYSQL -e "DROP TABLE IF EXISTS $CHILD_TABLE, $PARENT_TABLE; CREATE TABLE $PARENT_TABLE ( a INT PRIMARY KEY, b INT, INDEX idx_b(b) ) ENGINE=InnoDB; CREATE TABLE $CHILD_TABLE ( c INT, d INT, INDEX idx_d(d), FOREIGN KEY (d) REFERENCES $PARENT_TABLE(b) ON DELETE CASCADE ) ENGINE=InnoDB; INSERT INTO $PARENT_TABLE VALUES (1, 1); INSERT INTO $CHILD_TABLE VALUES (101, 1);" $SCRIPTS/command.sh check if test $? != 0 then echo "Consistency check failed" exit 1 fi percona-xtradb-cluster-galera/tests/regressions/lp967134/run.sh0000755000000000000000000000354112247075736024636 0ustar rootroot00000000000000#!/bin/bash # # DESCRIPTION: # # Test for lp:967134 # https://bugs.launchpad.net/codership-mysql/+bug/967134 # # This test script may not be able to crash nodes or generate inconsistency. # However, some error messages may be generated in error log if the # fix is not effective. # set -e declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" $SCRIPTS/command.sh restart MYSQL="mysql -u$DBMS_TEST_USER -p$DBMS_TEST_PSWD -h${NODE_INCOMING_HOST[0]} -P${NODE_INCOMING_PORT[0]} -Dtest" TABLE_PREFIX="" PARENT_TABLE="${TABLE_PREFIX}parent" CHILD_TABLE="${TABLE_PREFIX}child" rounds=10 while test $rounds != 0 do PARENT_TABLE_CREATE="CREATE TABLE $PARENT_TABLE (a INT PRIMARY KEY" echo "-- generating $((100 - $rounds)) columns for parent" for ii in `seq 0 $((100 - $rounds))` do PARENT_TABLE_CREATE="$PARENT_TABLE_CREATE, a$ii INT DEFAULT $ii" done PARENT_TABLE_CREATE="$PARENT_TABLE_CREATE) ENGINE=InnoDB" # echo $PARENT_TABLE_CREATE $MYSQL -e "DROP TABLE IF EXISTS $CHILD_TABLE, $PARENT_TABLE; $PARENT_TABLE_CREATE; CREATE TABLE $CHILD_TABLE ( c INT, d INT, INDEX idx_c(c), FOREIGN KEY (c) REFERENCES $PARENT_TABLE(a) ON DELETE CASCADE ) ENGINE=InnoDB;" ROWS=${ROWS:-1000} ii=0 echo "-- Filling parent table" while test $ii != $ROWS do $MYSQL -e "INSERT INTO $PARENT_TABLE (a) VALUES ($ii)" ii=$(($ii + 1)) done echo "-- Filling child table" ii=0 while test $ii != $ROWS do $MYSQL -e "INSERT INTO $CHILD_TABLE VALUES (1, 1)" ii=$(($ii + 1)) done rounds=$(($rounds - 1)) done $SCRIPTS/command.sh checkpercona-xtradb-cluster-galera/tests/regressions/lp970714/lp.sql0000644000000000000000000021570012247075736024627 0ustar rootroot00000000000000LOCK TABLE test.lp WRITE; INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); INSERT INTO test.lp(who) VALUES ('loader'); UNLOCK TABLES;percona-xtradb-cluster-galera/tests/regressions/lp970714/run.sh0000755000000000000000000001044112247075736024631 0ustar rootroot00000000000000#!/bin/bash # # lp:970714 # https://bugs.launchpad.net/codership-mysql/+bug/970714 # # BACKGROUND: # For locking session support we can say that: # 1. they cannot be supported in multi-master case # 2. they muist be supported in master-slave case (for tansparency) # # Detecting MS and MM use cases is difficult from within a node, so we # must detect locking sessions conflicts between appliers and # local locking sessions and abort the locking sessions. # # TEST # # This test starts two nodes and runs two client sessions against each node: # applier: this load is simple autoinc insert load which will cause conflicts # in the other node's applying # loader: this load runs a locking session with a big number of inserts within # # The idea is that in the second node, loader will run a locking session and # applier load from the first node will conflict with the locking session. # mysqld should be able to abort loader transaction and break the locking # session # # TEST OUTCOME # # If bug is present, second node will either crash or hang due to unresolved # conflicts, applier will be blocked # # PARAMETERS # # How many rounds to run load ROUNDS=${ROUNDS:-100} # declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/kill.sh . $SCRIPTS/misc.sh applier() { for (( i=1; i<$ROUNDS; i++ )); do mysql -u$DBMS_ROOT_USER -p$DBMS_ROOT_PSWD \ --host ${NODE_INCOMING_HOST[0]} --port ${NODE_INCOMING_PORT[0]} \ -e "INSERT INTO test.lp(who) VALUES ('applier')" [ $? != 0 ] && echo "applier aborted: $? at $cnt" done } loader() { for (( i=1; i<$ROUNDS; i++ )); do mysql -u$DBMS_ROOT_USER -p$DBMS_ROOT_PSWD \ --host ${NODE_INCOMING_HOST[1]} --port ${NODE_INCOMING_PORT[1]} \ < ./lp.sql [ $? != 0 ] && echo "load aborted: $? at $cnt" done } run_test() { echo "starting loader" loader& loader_pid=$! echo "starting applier" applier& applier_pid=$! echo "waiting for loader ($loader_pid) and applier ($applier_pid)" wait trap "kill $applier_pid" EXIT trap "kill $loader_pid" EXIT echo "checking consistency" check } echo "##################################################################" echo "## regression test for lp:970714" echo "##################################################################" echo "starting two nodes" stop start_node "-d -g gcomm://$(extra_params 0)" 0 start_node "-d -g $(gcs_address 1)" 1 echo "creating database" mysql -u$DBMS_ROOT_USER -p$DBMS_ROOT_PSWD \ --host ${NODE_INCOMING_HOST[0]} --port ${NODE_INCOMING_PORT[0]} \ -e "DROP TABLE IF EXISTS test.lp" mysql -u$DBMS_ROOT_USER -p$DBMS_ROOT_PSWD \ --host ${NODE_INCOMING_HOST[0]} --port ${NODE_INCOMING_PORT[0]} \ -e "CREATE TABLE test.lp(pk INT PRIMARY KEY AUTO_INCREMENT, who VARCHAR(10))" [ $? != "0" ] && echo "table create failed" && exit 1 echo "##" echo "## wsrep_convert_LOCK_to_trx=1" echo "##" echo "setting wsrep_convert_LOCK_to_trx=1" mysql -u$DBMS_ROOT_USER -p$DBMS_ROOT_PSWD \ --host ${NODE_INCOMING_HOST[0]} --port ${NODE_INCOMING_PORT[0]} \ -e "SET GLOBAL wsrep_convert_LOCK_to_trx=1" [ $? != "0" ] && echo "SET failed" && exit 1 mysql -u$DBMS_ROOT_USER -p$DBMS_ROOT_PSWD \ --host ${NODE_INCOMING_HOST[1]} --port ${NODE_INCOMING_PORT[1]} \ -e "SET GLOBAL wsrep_convert_LOCK_to_trx=1" [ $? != "0" ] && echo "SET failed" && exit 1 run_test echo "##" echo "## wsrep_convert_LOCK_to_trx=0" echo "##" echo "setting wsrep_convert_LOCK_to_trx=0" mysql -u$DBMS_ROOT_USER -p$DBMS_ROOT_PSWD \ --host ${NODE_INCOMING_HOST[0]} --port ${NODE_INCOMING_PORT[0]} \ -e "SET GLOBAL wsrep_convert_LOCK_to_trx=0" [ $? != "0" ] && echo "SET failed" && exit 1 mysql -u$DBMS_ROOT_USER -p$DBMS_ROOT_PSWD \ --host ${NODE_INCOMING_HOST[1]} --port ${NODE_INCOMING_PORT[1]} \ -e "SET GLOBAL wsrep_convert_LOCK_to_trx=0" [ $? != "0" ] && echo "SET failed" && exit 1 run_test echo "stopping cluster" stop percona-xtradb-cluster-galera/tests/regressions/lp994579/run.sh0000755000000000000000000001404512247075736024654 0ustar rootroot00000000000000#!/bin/bash ## # # lp:994579 # https://bugs.launchpad.net/codership-mysql/+bug/994579 # # BUG BACKGRPOUND: # # Foreign keys are not yet safe with parallel applying. This is due to # a bug in populating of shared keys. # If bug is present, slave appliers can easily conflict and cause crash. # # TEST SETUP: # - Two nodes are used in master slave mode. # - Slave is configured with 4 applier threads # - parent and child tables are created # - test load runs inserts for parent and child table, each parent # insert is followed by corresponding child insert # # # SUCCESS CRITERIA # # If bug is present, slave will crash for FK constraint failure. This is # because slave applied inserts in wrong order - child row was added before # corresponding parent row. # declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/kill.sh . $SCRIPTS/misc.sh echo "##################################################################" echo "## regression test for lp:994579" echo "##################################################################" echo "stopping cluster" ../../scripts/command.sh stop #echo "stopping node0, node1..." #../../scripts/command.sh stop_node 0 #../../scripts/command.sh stop_node 1 echo echo "starting node0, node1..." ../../scripts/command.sh start_node "-d -g gcomm://$(extra_params 0)" 0 ../../scripts/command.sh start_node "-d -g $(gcs_address 1) --slave_threads 4" 1 MYSQL="mysql --batch --silent --user=$DBMS_TEST_USER --password=$DBMS_TEST_PSWD --host=$DBMS_HOST test " declare -r port_0=${NODE_INCOMING_PORT[0]} declare -r port_1=${NODE_INCOMING_PORT[1]} inserter_int_trx() { local port=$1 for i in {1..10000}; do $MYSQL --port=$port -e " BEGIN; INSERT INTO test.lp994579_parent(pk, j) VALUES($i, $i); INSERT INTO test.lp994579_child(i,fk) VALUES ($i, $i); COMMIT;" 2>&1 done } inserter_int_ac() { local port=$1 for i in {1..10000}; do $MYSQL --port=$port -e " INSERT INTO test.lp994579_parent(pk, j) VALUES($i, $i); INSERT INTO test.lp994579_child(i,fk) VALUES ($i, $i); " 2>&1 done } createdb_int() { $MYSQL --port=$port_0 -e " DROP TABLE IF EXISTS test.lp994579_child;" $MYSQL --port=$port_0 -e ' DROP TABLE IF EXISTS test.lp994579_parent;' $MYSQL --port=$port_0 -e ' CREATE TABLE test.lp994579_parent ( pk INT PRIMARY KEY AUTO_INCREMENT, j int )' $MYSQL --port=$port_0 -e " CREATE TABLE test.lp994579_child ( i INT PRIMARY KEY AUTO_INCREMENT, fk int, CONSTRAINT FOREIGN KEY (fk) REFERENCES test.lp994579_parent(pk) )" } createdb_varchar() { local charset=$1 $MYSQL --port=$port_0 -e " DROP TABLE IF EXISTS test.lp994579_child;" $MYSQL --port=$port_0 -e ' DROP TABLE IF EXISTS test.lp994579_parent;' $MYSQL --port=$port_0 -e " CREATE TABLE test.lp994579_parent ( pk VARCHAR(20) PRIMARY KEY, j int ) CHARACTER SET '$charset' " $MYSQL --port=$port_0 -e " CREATE TABLE test.lp994579_child ( i INT PRIMARY KEY AUTO_INCREMENT, fk VARCHAR(20), CONSTRAINT FOREIGN KEY (fk) REFERENCES test.lp994579_parent(pk) ) CHARACTER SET '$charset'" } inserter_varchar_ac() { local port=$1 for i in {1..10000}; do $MYSQL --port=$port -e " INSERT INTO test.lp994579_parent(pk, j) VALUES('key-$i', $i); INSERT INTO test.lp994579_child(i,fk) VALUES ($i, 'key-$i'); " 2>&1 done } ######################################################### # # Test begins here # ######################################################### threads=$($MYSQL --port=$port_1 -e "SHOW VARIABLES LIKE 'wsrep_slave_threads'") echo "applier check: $threads" [ "$threads" = "wsrep_slave_threads 4" ] && echo "enough slaves" echo "Creating database..." createdb_int echo echo echo "#############################################" echo "### Phase 1, FK by int column, autocommit ###" echo echo "starting inserter for int keys, autocommit" inserter_int_ac $port_0 & declare inserter1_pid=$! echo "waiting phase 1 load to end ($inserter1_pid)" wait $MYSQL --port=$port_0 -e 'SHOW PROCESSLIST' echo $MYSQL --port=$port_1 -e 'SHOW PROCESSLIST' [ "$?" != "0" ] && echo "failed!" && exit 1 createdb_int echo echo echo "###############################################" echo "### Phase 2, FK by int column, transactions ###" echo echo "starting inserter for int keys, transactions" inserter_int_trx $port_0 & declare inserter2_pid=$! echo "waiting phase 2 load to end ($inserter2_pid)" wait $MYSQL --port=$port_0 -e 'SHOW PROCESSLIST' echo $MYSQL --port=$port_1 -e 'SHOW PROCESSLIST' [ "$?" != "0" ] && echo "failed!" && exit 1 echo echo echo "#############################################" echo "### Phase 3, FK by varchar column, latin1 ###" echo createdb_varchar latin1 echo "starting inserter for VARCHAR keys, autocommit" inserter_varchar_ac $port_0 & declare inserter3_pid=$! echo "waiting phase 3 load to end ($inserter3_pid)" wait $MYSQL --port=$port_0 -e 'SHOW PROCESSLIST' echo $MYSQL --port=$port_1 -e 'SHOW PROCESSLIST' [ "$?" != "0" ] && echo "failed!" && exit 1 echo echo echo "###########################################" echo "### Phase 4, FK by varchar column, utf8 ###" echo createdb_varchar utf8 echo "starting inserter for VARCHAR keys, autocommit" inserter_varchar_ac $port_0 & declare inserter4_pid=$! echo "waiting phase 4 load to end ($inserter4_pid)" wait $MYSQL --port=$port_0 -e 'SHOW PROCESSLIST' [ "$?" != "0" ] && echo "failed!" && exit 1 echo $MYSQL --port=$port_1 -e 'SHOW PROCESSLIST' [ "$?" != "0" ] && echo "failed!" && exit 1 $SCRIPTS/command.sh check if test $? != 0 then echo "Consistency check failed" exit 1 fi echo echo "Done!" echo ../../scripts/command.sh stop_node 0 ../../scripts/command.sh stop_node 1 exit 0 percona-xtradb-cluster-galera/tests/regressions/t281/run.sh0000755000000000000000000000261512247075736024224 0ustar rootroot00000000000000#!/bin/bash -eu declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/signal.sh . $SCRIPTS/misc.sh function get_status { cmd=$(echo "show status like '$2'") echo $(mysql -s -s -u$DBMS_TEST_USER -p$DBMS_TEST_PSWD -h${NODE_INCOMING_HOST[$1]} -P${NODE_INCOMING_PORT[$1]} -e "$cmd" | awk '{print $2;}'); } echo "regression test for #281" echo "restarting cluster" $SCRIPTS/command.sh restart mysql -s -s -u$DBMS_TEST_USER -p$DBMS_TEST_PSWD \ -h${NODE_INCOMING_HOST[1]} -P${NODE_INCOMING_PORT[1]} test \ -e "DROP TABLE IF EXISTS t281"; signal_node STOP 1 pause 20 1 signal_node CONT 1 pause 10 1 status=$(get_status 1 "wsrep_cluster_status"); ready=$(get_status 1 "wsrep_ready"); echo $status $ready if test $status=="Primary" && test $ready=="ON" then mysql -s -s -u$DBMS_TEST_USER -p$DBMS_TEST_PSWD \ -h${NODE_INCOMING_HOST[1]} -P${NODE_INCOMING_PORT[1]} test \ -e "CREATE TABLE t281 (a int)"; if test $? != 0; then echo "Failed to execute command"; exit 1; fi else echo "Wrong state"; exit 1; fi mysql -s -s -u$DBMS_TEST_USER -p$DBMS_TEST_PSWD \ -h${NODE_INCOMING_HOST[1]} -P${NODE_INCOMING_PORT[1]} test \ -e "DROP TABLE IF EXISTS t281"; $SCRIPTS/command.sh stop exit 0; percona-xtradb-cluster-galera/tests/regressions/t285/run.sh0000755000000000000000000000256312247075736024232 0ustar rootroot00000000000000#!/bin/bash -eu declare -r DIST_BASE=$(cd $(dirname $0)/../..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/signal.sh . $SCRIPTS/misc.sh echo "regression test for #285" echo "restarting cluster" $SCRIPTS/command.sh restart mysql -s -s -u$DBMS_TEST_USER -p$DBMS_TEST_PSWD \ -h${NODE_INCOMING_HOST[0]} -P${NODE_INCOMING_PORT[0]} test \ -e "DROP TABLE IF EXISTS t285"; mysql -s -s -u$DBMS_TEST_USER -p$DBMS_TEST_PSWD \ -h${NODE_INCOMING_HOST[0]} -P${NODE_INCOMING_PORT[0]} test \ -e "CREATE TABLE t285 (a INT PRIMARY KEY)"; stmt=$( echo "BEGIN;"; for i in `seq 1 $((1 << 16))` do echo "INSERT INTO t285 VALUES ($i);"; done echo "COMMIT;"; ) echo $stmt | mysql -s -s -u$DBMS_TEST_USER -p$DBMS_TEST_PSWD \ -h${NODE_INCOMING_HOST[0]} -P${NODE_INCOMING_PORT[0]} test if test $? != 0 then echo "query failed"; exit 1; else mysql -s -s -u$DBMS_TEST_USER -p$DBMS_TEST_PSWD \ -h${NODE_INCOMING_HOST[0]} -P${NODE_INCOMING_PORT[0]} test \ -e "DROP TABLE IF EXISTS t285"; if test $? != 0 then echo "cleanup failed, check server status"; exit 1; fi fi $SCRIPTS/command.sh check if test $? != 0 then echo "checksum failed" exit 1 fi $SCRIPTS/command.sh stop exit 0;percona-xtradb-cluster-galera/tests/scripts/README0000644000000000000000000000337412247075736022372 0ustar rootroot00000000000000THIS DIRECTORY This directory contains a library of cluster manipulation scripts. The main idea is facilitation of parallel operations and minimizaiton of code duplicaiton. Executable file is command.sh. It sources the configuration and the rest of the scripts which implement different funcitons: jobs.sh - the main file which defines generic command execution framework. It implements parallel and per-node command execution routines and enables unified logging behaviour. Other scripts just have to define the actual command functions following certain conventions, so far there is only one: node index is appended to the list of command line parameters, then everything is passed to the function. install.sh - implements a cluster-wide 'install' command which takes a name of the distribution file as the first argument remove.sh - implements a cluster-wide "remove" command. action.sh - usual start/stop/restart/check commands, both cluster-wide and per node. kill.sh - a per-node kill -9 command Assumed convention: cluster-wide commands are just commnads, per-node commands have _node suffix. E.g. ./command.sh start starts all nodes, ./command.sh stop_node 1 stops only node number 1. Numbering is 0-based. It is intended that each command should implement its own help. SPECIAL FILES AND DIRECTORIES: ../conf directory contains configuration files. Each command creates at least 4 files named _. where is: out - standard output of the command err - standard error of the command pid - pid of the porcess executing command ret - return code of the command "out" files are placed into $BASE_OUT directory, other files are placed in $BASE_RUN. The reason to separate is unclear. percona-xtradb-cluster-galera/tests/scripts/action.sh0000644000000000000000000001527612247075736023327 0ustar rootroot00000000000000# Helper to get status variable value cluster_status() { local node=$1 case "$DBMS" in "MYSQL") local res=$(mysql -u$DBMS_ROOT_USER -p$DBMS_ROOT_PSWD \ -h${NODE_INCOMING_HOST[$node]} -P${NODE_INCOMING_PORT[$node]} \ --skip-column-names -ss \ -e "SET wsrep_on=0; SHOW STATUS WHERE Variable_name LIKE 'wsrep_cluster_status' OR Variable_name LIKE 'wsrep_cluster_size'" 2>/dev/null) echo -n $res | awk '{ print $4 ":" $2; }' ;; "PGSQL"|*) return -1 esac } mysql_query() { local node=$1 local query=$2 mysql -u$DBMS_ROOT_USER -p$DBMS_ROOT_PSWD \ -h${NODE_INCOMING_HOST[$node]} -P${NODE_INCOMING_PORT[$node]} \ --skip-column-names -ss -e "$query" 2>/dev/null } wait_node_state() { local node=$1 local state=$2 while true do local res="-1" case "$DBMS" in "MYSQL") res=$(mysql_query $node "SHOW STATUS LIKE 'wsrep_local_state'" \ | awk '{ print $2 }') ;; "PGSQL"|*) return -1 esac if [ "$res" = "$state" ]; then break; fi sleep 1 done } # # Routines to start|stop|check cluster nodes # action_cmd() { local cmd=$1 local node=${@:$#} local nargs=$(( $# - 2 )) # minus cmd and node local args="${@:2:$nargs}" # arguments range from 2 to n-1 local dir="${NODE_TEST_DIR[$node]}" case "$DBMS" in "MYSQL") echo -n "MYSQL_PORT=${NODE_INCOMING_PORT[$node]} "\ "\"$dir/mysql-galera\" $args $cmd" ;; "PGSQL"|*) return -1 ;; esac } # By convention node index is the last in the arguments list. # So we prepend command to the argument list otherwise it'll go after node # index here. start_cmd() { action_cmd "start" "$@" } stop_cmd() { action_cmd "stop" "$@" } restart_cmd() { action_cmd "restart" "$@" } check_cmd() { action_cmd "check" "$@" } dump_cmd() { action_cmd "dump" "$@" } action() { start_jobs "$@" wait_jobs } stop() { action "stop_cmd" "$@" } dump() { action "dump_cmd" "$@" } check() { cmd="check_cmd" ! action "$cmd" "$@" # ! - to ignore possible connection error local -r prefix="$BASE_OUT/$cmd" local node local prev="" local fail="" for node in $NODE_LIST do local node_id="${NODE_ID[$node]}" local out="${prefix}_${node_id}.out" chk=$(cat "$out") # no need to check if file exists: # should be created even if command fails # echo "$node_id: ${chk%% -}" if [ -n "$chk" ] # skip 0-length checksum: the node was down then echo "$chk" | sed s/-/${node_id}/ if [ -z "$prev" ] then prev="$chk" else if [ "$prev" != "$chk" ] then fail="yes" fi fi fi done if [ -z "$fail" ] && [ -n "$prev" ]; then return 0; fi echo "Checksum failed." # for node in $NODE_LIST # do # local node_id="${NODE_ID[$node]}" # echo -n "$node_id: " # cat "${prefix}_$node_id.out" # done return 1 } # Query each node with causal reads on to make sure that slave # queue has been fully processed. # Arguments: list of nodes wait_sync() { for node in "$@" do mysql_query "$node" "set wsrep_causal_reads=1; select 0;" 1>/dev/null done } start_node() { node_job "start_cmd" "$@" } stop_node() { node_job "stop_cmd" "$@" } restart_node() { node_job "restart_cmd" "$@" } dump_node() { node_job "dump_cmd" "$@" } # unlike bulk check this one returns error when the node could not be checked check_node() { local cmd="check_cmd" node_job "$cmd" "$@" local node_id="${NODE_ID[$1]}" cat "${BASE_OUT}/${cmd}_${node_id}.out" | sed s/-/${node_id}/ return $(cat $BASE_RUN/check_cmd_$node_id.ret) } extra_params() { local node=$1 local extra_params [ -z "$GCOMM_EXTRA_PARAMS" ] && extra_params="?" || extra_params="?${GCOMM_EXTRA_PARAMS}&" # echo "${extra_params}gmcast.listen_addr=tcp://${NODE_GCS_HOST[$node]}:${NODE_GCS_PORT[$node]}" echo "${extra_params}gmcast.listen_addr=tcp://0.0.0.0:${NODE_GCS_PORT[$node]}" } # return GCS address at which node N should connect to group gcs_address() { local node=$1 case "$GCS_TYPE" in "gcomm") local peer=$(( $node - 1 )) # select previous node as connection peer # local peer=0 # use the first node as a connection handle if [ $peer -lt 0 ]; then peer=$NODE_MAX; fi # rollover echo "'gcomm://${NODE_GCS_HOST[$peer]}:${NODE_GCS_PORT[$peer]}$(extra_params $node)'" ;; "vsbes") echo "'vsbes://$VSBES_ADDRESS'" ;; *) return 1 ;; esac } # start/restart nodes in group mode. _cluster_up() { local -r cmd=$1 shift SECONDS=0 # for wait_jobs for node in $NODE_LIST do echo "Starting ${NODE_ID[$node]}" if [ $node -eq 0 ] then # must make sure 1st node completely operational case "$GCS_TYPE" in # "gcomm") $cmd "-g 'gcomm://:${NODE_GCS_PORT[$node]}$(extra_params $node)'" "$@" 0 ;; "gcomm") $cmd "-g $(gcs_address $node) --mysql-opt --wsrep-new-cluster" "$@" 0 ;; "vsbes") $cmd "-g 'vsbes://$VSBES_ADDRESS'" "$@" 0 ;; esac else $cmd "-g $(gcs_address $node)" "$@" $node & fi done wait_jobs } # start/restart nodes in group mode. bootstrap() { SECONDS=0 # for wait_jobs local cnt=0 for node in $NODE_LIST do echo "Starting ${NODE_ID[$node]}" start_node "-g $(gcs_address $node)" "$@" $node & cnt=$(($cnt + 1)) done # TODO: Poll until all have reached non-prim for node in 0 # only one node is sufficient do while true do st=$(cluster_status $node) if test "x$st" = "xnon-Primary:$cnt" then break; fi sleep 1 done done # TODO: Figure out how to do this in DBMS indepent way case "$DBMS" in "MYSQL") mysql -u$DBMS_ROOT_USER -p$DBMS_ROOT_PSWD \ -h${NODE_INCOMING_HOST[0]} \ -P${NODE_INCOMING_PORT[0]} \ -e "SET GLOBAL wsrep_provider_options='pc.bootstrap=1'" ;; "PGSQL"|*) return -1 ;; esac # Jobs will finish when nodes reach primary wait_jobs } start() { _cluster_up start_node "$@" } restart() { stop _cluster_up start_node "$@" } percona-xtradb-cluster-galera/tests/scripts/command.sh0000755000000000000000000000137712247075736023470 0ustar rootroot00000000000000#!/bin/bash -eu help() { echo "Usage: $0 [args]" echo "Cluster commands: install, remove, start, stop, check" echo "Node commands: start_node, stop_node, restart_node, check_node," echo " kill_node" echo "Command help: $0 help" } if [ $# -eq 0 ]; then help >&2; exit 1; fi declare -r DIST_BASE=$(cd $(dirname $0)/..; pwd -P) declare -r DIST_SCRIPTS="$DIST_BASE/scripts" # later create config.sh to read config from command line options declare -r TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . "$TEST_BASE/conf/main.conf" . $DIST_SCRIPTS/jobs.sh . $DIST_SCRIPTS/install.sh . $DIST_SCRIPTS/remove.sh . $DIST_SCRIPTS/action.sh . $DIST_SCRIPTS/kill.sh . $DIST_SCRIPTS/signal.sh command=$1 shift $command "$@" percona-xtradb-cluster-galera/tests/scripts/install.sh0000644000000000000000000000330512247075736023506 0ustar rootroot00000000000000# # Routines to install distribution on nodes # untar_cmd() { local node=${@:$#} local path="${NODE_TEST_DIR[$node]}" local hst=$(hostname -s) echo -n "mkdir -p \"$path\" && tar --strip 1 -C \"$path\" -xzf - && > \"$path/mysql/var/mysqld.err\" && exit 0" } copy_config() { local -r node=$1 local cnf local cnf_dir local key_src="$BASE_CONF/galera_key.pem" local key_dst local cert_src="$BASE_CONF/galera_cert.pem" local cert_dst case $DBMS in MYSQL) common_cnf="$COMMON_MY_CNF" cnf_src="${NODE_MY_CNF[$node]}" cnf_dst="${NODE_TEST_DIR[$node]}/mysql/etc/my.cnf" key_dst="${NODE_TEST_DIR[$node]}/mysql/var/galera_key.pem" cert_dst="${NODE_TEST_DIR[$node]}/mysql/var/galera_cert.pem" ;; PGSQL|*) echo "Unsupported DBMS: '$DBMS'" >&2 return 1 ;; esac if [ -n "$common_cnf" ] || [ -n "$cnf_src" ] then if [ "${NODE_LOCATION[$node]}" = "local" ] then ([ -n "$common_cnf" ] && cat "$common_cnf" && \ [ -n "$cnf_src" ] && cat "$cnf_src") > "$cnf_dst" cat "$key_src" > "$key_dst" cat "$cert_src" > "$cert_dst" else local remote="${NODE_LOCATION[$node]}" ([ -n "$common_cnf" ] && cat "$common_cnf" && \ [ -n "$cnf_src" ] && cat "$cnf_src") | \ ssh "$remote" "cat > $cnf_dst" cat "$key_src" | ssh "$remote" "cat > $key_dst" cat "$cert_src" | ssh "$remote" "cat > $cert_dst" fi fi } install_node() { local dist=$1 node_job untar_cmd "$@" } install() { local dist=$1 start_jobs untar_cmd $dist wait_jobs } percona-xtradb-cluster-galera/tests/scripts/jobs.sh0000644000000000000000000000520612247075736022777 0ustar rootroot00000000000000# # Routines pertaining to parallel execution # # xxxx_job() functions assume node index as last parameter # # Logging must happen on different layers. # e.g. logging of stderr must happen on the same level as recording of return # code. While stdout log should record only the output of command. #_local_job() #{ # local cmd="$1" # eval "$($@)" # eval $cmd #} #_ssh_job() #{ # local node=${@:$#} # last argument # local cmd="$($@)" # local cmd="$1" # # ssh -ax ${NODE_LOCATION[$node]} "$cmd" #} _date() { echo -n $(date +'%y%m%d %T.%N' | cut -c 1-19) } virtual_job() { local node=${@:$#} # last argument local out="$BASE_OUT/${1}_${NODE_ID[$node]}.out" local cmd="$($@)" if [ "${NODE_LOCATION[$node]}" = "local" ] then # local_job "$cmd" 1>"$out" eval "$cmd" 1>"$out" else # ssh_job "$cmd" 1>"$out" ssh -ax ${NODE_LOCATION[$node]} "$cmd" 1>"$out" fi } # This function tries to add a bit of polymorphism by treating untar cmd # specially. The speciality comes from that it not only excutes command, # but also transfers data # # Usage: node_job cmd opt1 opt2 ... optN node # node_job() { local cmd=$1 shift local node=${@:$#} # last argument local node_id="${NODE_ID[$node]}" local prefix="$BASE_RUN/${cmd}_${node_id}" local rcode=0 local start=$SECONDS case $cmd in "untar_cmd") local dist="$1" shift cat "$dist" | virtual_job "$cmd" "$@" 2>"$prefix.err" && \ copy_config $node 2>"$prefix.err" || rcode=$? ;; *) virtual_job "$cmd" "$@" 2>"$prefix.err" || rcode=$? ;; esac echo $rcode > "$prefix.ret" echo -n "$(_date) Job '$cmd' on '$node_id'" if [ $rcode -eq 0 ] then echo " complete in $(($SECONDS - $start)) seconds, " else echo " failed with code: $rcode, " echo "FAILED COMMAND: $($cmd $@)" echo "REASON: $(cat "$prefix.err")" fi return $rcode } start_jobs() { SECONDS=0 local node for node in $NODE_LIST do local node_id="${NODE_ID[$node]}" local prefix="$BASE_RUN/${1}_$node_id" node_job "$@" $node & echo $! > "$prefix.pid" echo "$(_date) Job '$1' on '$node_id' started" done echo "All jobs started" } wait_jobs() { local err=0 local node for node in $NODE_LIST do wait %% 2>$BASE_RUN/wait.err || err=$?; # 127 - no more jobs if [ $err -eq 127 ]; then err=0; break; fi if [ $err -gt 128 ]; then err=0; fi # ignore signals done echo "$(_date) All jobs complete in $SECONDS seconds" return $err } percona-xtradb-cluster-galera/tests/scripts/kill.sh0000644000000000000000000000062312247075736022773 0ustar rootroot00000000000000# # Ungracefully kills the process # kill_cmd() { local node=${@:$#} local dir="${NODE_TEST_DIR[$node]}" case $DBMS in "MYSQL") echo -n "kill -9 \$(cat $dir/mysql/var/mysqld.pid)" ;; "PGSQL"|*) echo "Not supported" >&2 return 1 ;; esac } kill_node() { node_job kill_cmd "$@" } kill_all() { start_jobs kill_cmd wait_jobs } percona-xtradb-cluster-galera/tests/scripts/misc.sh0000644000000000000000000000111712247075736022772 0ustar rootroot00000000000000# # Miscellaneous functions # # Sleeps variable amount of seconds (by default 1-10) pause() #min_sleep #var_sleep { local min_sleep=${1:-"1"} local var_sleep=${2:-"10"} local p=$(( $RANDOM % var_sleep + min_sleep )) echo "Sleeping for $p sec." sleep $p } # Pauses given processes (load) to perform consistency check consistency_check() #pids { local ret=0 local pids="$@" kill -STOP $pids sleep 1 check || (sleep 2; check) || (sleep 3; check) || ret=$? kill -CONT $pids # processes will receive SIGHUP in case of script exit return $ret } percona-xtradb-cluster-galera/tests/scripts/remove.sh0000644000000000000000000000034112247075736023332 0ustar rootroot00000000000000# # Routines to remove test distribution from nodes # remove_cmd() { local node=${@:$#} local dirn="${NODE_TEST_DIR[$node]}" echo -n 'rm -rf '"$dirn"'/*' } remove() { start_jobs remove_cmd wait_jobs } percona-xtradb-cluster-galera/tests/scripts/signal.sh0000644000000000000000000000056012247075736023315 0ustar rootroot00000000000000# # Sends signal to process # signal_cmd() { local sig=$1 local node=$2 local dir="${NODE_TEST_DIR[$node]}" echo $sig $node case $DBMS in "MYSQL") echo -n "kill -$sig \$(cat $dir/mysql/var/mysqld.pid)" ;; "PGSQL"|*) echo "Not supported" >&2 return 1 ;; esac } signal_node() { node_job signal_cmd "$@" } percona-xtradb-cluster-galera/tests/t/dummy.sh0000755000000000000000000000116512247075736021754 0ustar rootroot00000000000000#!/bin/bash BASE_DIR=$(cd $(dirname $0); pwd -P) . $BASE_DIR/../tap/tap-functions REPORT_DIR="$TEST_REPORT_DIR/dummy" if ! test -d $REPORT_DIR then mkdir $REPORT_DIR fi DUMMY_LOG=$REPORT_DIR/dummy.log echo "args: " $@ >> $DUMMY_LOG echo "pwd: " `pwd` >> $DUMMY_LOG echo "output: " $REPORT_DIR >> $DUMMY_LOG echo "nodes :" $CLUSTER_NODES >> $DUMMY_LOG echo "n_nodes: " $CLUSTER_N_NODES >> $DUMMY_LOG plan_tests 7 ok 0 "first" okx test 1 -eq 1 is 1 1 "third" isnt 1 2 "fourth" skip ok "maybe not ok but skip" TODO="not implemented yet" ok 0 "test not implemented yet" unset TODO diag "some message" ok 0 "final"percona-xtradb-cluster-galera/tests/tap/README0000644000000000000000000000027712247075736021466 0ustar rootroot00000000000000 Required packages to run run_test_set.pl - TAP::Parser perl module + + Ubuntu package: libtap-parser-perl + CentOS: ? + Directly from CPAN: http://search.cpan.org/dist/TAP-Parser/percona-xtradb-cluster-galera/tests/tap/run_test_set.pl0000755000000000000000000000647412247075736023671 0ustar rootroot00000000000000#!/usr/bin/perl -w # # Initially shamelessly stolen from # http://testanything.org/wiki/index.php/TAP::Parser_Cookbook # # Name: run_test_set.pl # Purpose: A test scheduler and runner # # Usage: # # run_test_set.pl [-q|-v] []* [" [test args]"]* # # Options: # -q Quiet mode (default) # -v Verbose mode, prints report output also to STDOUT # use strict; use warnings; use POSIX qw/strftime/; use TAP::Parser qw/all/; use TAP::Parser::Aggregator qw/all/; sub usage { printf STDERR "Usage: $0 \n"; } my $out_file; my $quiet = 1; sub printlog { if ($quiet == 0) { print @_; } printf $out_file @_; } sub read_tests { my @res = (); my $file = shift; open IN, "<", $file or die "Could not open file " . $file . ": " . $! . "\n"; while () { my $line; chomp($line = $_); unless ($line =~ /^#/) { push @res, $line; } } close IN; return @res; } # Find out if script is run from correct working directory my $cwd; chomp($cwd = `pwd`); my $self = $cwd . "/scripts/run_test_set.pl"; die "run_test_set.pl must be run from test root" unless system(("test", "-f", $self)) == 0; # Export some helpful environment variables # Count number of nodes for test scripts my @nvec = split(' ', $ENV{CLUSTER_NODES}); $ENV{CLUSTER_N_NODES} = scalar(@nvec); # Report directory and test result log definitions # Report dir is r/yymmdd.i where yy is year, mm month, dd day and # i running index, starting from 0. my $fmt_str = "%y%m%d"; my $i = 0; my $report_dir = $cwd . "/r/" . strftime($fmt_str, localtime); while (-d $report_dir . "." . $i) { $i++; } $report_dir = $report_dir . "." . $i; mkdir($report_dir, 0777) or die "Could not create report dir " . $report_dir . ": " . $! . "\n"; my $result_log = $report_dir . "/result.log"; # Open result log open $out_file, ">>", $result_log or die "Cannot open outfile. $!\n"; printlog sprintf("\n---\nReport %s\n---\n", strftime("%y%m%d-%H:%M", localtime)); # Scan given arguments for test definitions my @args = @ARGV; while ($args[0] =~ /^-(\w)/) { my $opt = $1; # print $opt . "\n"; if ($opt eq "q") { $quiet = 1; } elsif ($opt eq "v") { $quiet = 0; } else { usage; die $0 . ": Unknown option: -" . $opt . "\n"; } shift @args; } my @tests = (); if (scalar(@args) == 0) { printlog "No tests to be run\n"; exit 0; } foreach my $arg (@args) { if ($arg =~ /.conf/) { push @tests, read_tests($arg); } else { push(@tests, $arg); } } foreach my $test (@tests) { printlog "\n---\nRunning test: " . $test . "\n---\n"; my @line = split(' ', $test); my $file = $line[0]; shift(@line); $ENV{TEST_REPORT_DIR} = $report_dir; my $parser = TAP::Parser->new( { source => $file, test_args => \@line } ); while ( my $result = $parser->next ) { printlog $result->as_string . "\n"; } my $aggregate = TAP::Parser::Aggregator->new; $aggregate->add( 'testcases', $parser ); printlog sprintf("\nPassed: %s\nFailed: %s\n", scalar $aggregate->passed, scalar $aggregate->failed); } percona-xtradb-cluster-galera/tests/tap/tap-functions0000755000000000000000000001652112247075736023325 0ustar rootroot00000000000000#!/bin/bash # # Taken from: http://svn.solucorp.qc.ca/repos/solucorp/JTap/ # _version='1.01' _plan_set=0 _no_plan=0 _skip_all=0 _test_died=0 _expected_tests=0 _executed_tests=0 _failed_tests=0 TODO= usage(){ cat <<'USAGE' tap-functions: A TAP-producing BASH library PLAN: plan_no_plan plan_skip_all [REASON] plan_tests NB_TESTS TEST: ok RESULT [NAME] okx COMMAND is RESULT EXPECTED [NAME] isnt RESULT EXPECTED [NAME] like RESULT PATTERN [NAME] unlike RESULT PATTERN [NAME] pass [NAME] fail [NAME] SKIP: skip [CONDITION] [REASON] [NB_TESTS=1] skip $feature_not_present "feature not present" 2 || { is $a "a" is $b "b" } TODO: Specify TODO mode by setting $TODO: TODO="not implemented yet" ok $result "some not implemented test" unset TODO OTHER: diag MSG EXAMPLE: #!/bin/bash . tap-functions plan_tests 7 me=$USER is $USER $me "I am myself" like $HOME $me "My home is mine" like "`id`" $me "My id matches myself" /bin/ls $HOME 1>&2 ok $? "/bin/ls $HOME" # Same thing using okx shortcut okx /bin/ls $HOME [[ "`id -u`" != "0" ]] i_am_not_root=$? skip $i_am_not_root "Must be root" || { okx ls /root } TODO="figure out how to become root..." okx [ "$HOME" == "/root" ] unset TODO USAGE exit } # # Commented out option parsing, it messes things if commandline switches # are given for the script that sources this file # #opt= #set_u= #while getopts ":sx" opt ; do # case $_opt in # u) set_u=1 ;; # *) usage ;; # esac #done #shift $(( OPTIND - 1 )) # Don't allow uninitialized variables if requested #[[ -n "$set_u" ]] && set -u #unset opt set_u # Used to call _cleanup on shell exit trap _exit EXIT plan_no_plan(){ (( _plan_set != 0 )) && "You tried to plan twice!" _plan_set=1 _no_plan=1 return 0 } plan_skip_all(){ local reason=${1:-''} (( _plan_set != 0 )) && _die "You tried to plan twice!" _print_plan 0 "Skip $reason" _skip_all=1 _plan_set=1 _exit 0 return 0 } plan_tests(){ local tests=${1:?} (( _plan_set != 0 )) && _die "You tried to plan twice!" (( tests == 0 )) && _die "You said to run 0 tests! You've got to run something." _print_plan $tests _expected_tests=$tests _plan_set=1 return $tests } _print_plan(){ local tests=${1:?} local directive=${2:-''} echo -n "1..$tests" [[ -n "$directive" ]] && echo -n " # $directive" echo } pass(){ local name=$1 ok 0 "$name" } fail(){ local name=$1 ok 1 "$name" } # This is the workhorse method that actually # prints the tests result. ok(){ local result=${1:?} local name=${2:-''} (( _plan_set == 0 )) && _die "You tried to run a test without a plan! Gotta have a plan." _executed_tests=$(( $_executed_tests + 1 )) if [[ -n "$name" ]] ; then if _matches "$name" "^[0-9]+$" ; then diag " You named your test '$name'. You shouldn't use numbers for your test names." diag " Very confusing." fi fi if (( result != 0 )) ; then echo -n "not " _failed_tests=$(( _failed_tests + 1 )) fi echo -n "ok $_executed_tests" if [[ -n "$name" ]] ; then local ename=${name//\#/\\#} echo -n " - $ename" fi if [[ -n "$TODO" ]] ; then echo -n " # TODO $TODO" ; if (( result != 0 )) ; then _failed_tests=$(( _failed_tests - 1 )) fi fi echo if (( result != 0 )) ; then local file='tap-functions' local func= local line= local i=0 local bt=$(caller $i) while _matches "$bt" "tap-functions$" ; do i=$(( $i + 1 )) bt=$(caller $i) done local backtrace= eval $(caller $i | (read line func file ; echo "backtrace=\"$file:$func() at line $line.\"")) local t= [[ -n "$TODO" ]] && t="(TODO) " if [[ -n "$name" ]] ; then diag " Failed ${t}test '$name'" diag " in $backtrace" else diag " Failed ${t}test in $backtrace" fi fi return $result } okx(){ local command="$@" local line= diag "Output of '$command':" $command | while read line ; do diag "$line" done ok ${PIPESTATUS[0]} "$command" } _equals(){ local result=${1:?} local expected=${2:?} if [[ "$result" == "$expected" ]] ; then return 0 else return 1 fi } # Thanks to Aaron Kangas for the patch to allow regexp matching # under bash < 3. _bash_major_version=${BASH_VERSION%%.*} _matches(){ local result=${1:?} local pattern=${2:?} if [[ -z "$result" || -z "$pattern" ]] ; then return 1 else if (( _bash_major_version >= 3 )) ; then [[ "$result" =~ "$pattern" ]] else echo "$result" | egrep -q "$pattern" fi fi } _is_diag(){ local result=${1:?} local expected=${2:?} diag " got: '$result'" diag " expected: '$expected'" } is(){ local result=${1:?} local expected=${2:?} local name=${3:-''} _equals "$result" "$expected" (( $? == 0 )) ok $? "$name" local r=$? (( r != 0 )) && _is_diag "$result" "$expected" return $r } isnt(){ local result=${1:?} local expected=${2:?} local name=${3:-''} _equals "$result" "$expected" (( $? != 0 )) ok $? "$name" local r=$? (( r != 0 )) && _is_diag "$result" "$expected" return $r } like(){ local result=${1:?} local pattern=${2:?} local name=${3:-''} _matches "$result" "$pattern" (( $? == 0 )) ok $? "$name" local r=$? (( r != 0 )) && diag " '$result' doesn't match '$pattern'" return $r } unlike(){ local result=${1:?} local pattern=${2:?} local name=${3:-''} _matches "$result" "$pattern" (( $? != 0 )) ok $? "$name" local r=$? (( r != 0 )) && diag " '$result' matches '$pattern'" return $r } skip(){ local condition=${1:?} local reason=${2:-''} local n=${3:-1} if (( condition == 0 )) ; then local i= for (( i=0 ; i<$n ; i++ )) ; do _executed_tests=$(( _executed_tests + 1 )) echo "ok $_executed_tests # skip: $reason" done return 0 else return fi } diag(){ local msg=${1:?} if [[ -n "$msg" ]] ; then echo "# $msg" fi return 1 } _die(){ local reason=${1:-''} echo "$reason" >&2 _test_died=1 _exit 255 } BAIL_OUT(){ local reason=${1:-''} echo "Bail out! $reason" >&2 _exit 255 } _cleanup(){ local rc=0 if (( _plan_set == 0 )) ; then diag "Looks like your test died before it could output anything." return $rc fi if (( _test_died != 0 )) ; then diag "Looks like your test died just after $_executed_tests." return $rc fi if (( _skip_all == 0 && _no_plan != 0 )) ; then _print_plan $_executed_tests fi local s= if (( _no_plan == 0 && _expected_tests < _executed_tests )) ; then s= ; (( _expected_tests > 1 )) && s=s local extra=$(( _executed_tests - _expected_tests )) diag "Looks like you planned $_expected_tests test$s but ran $extra extra." rc=-1 ; fi if (( _no_plan == 0 && _expected_tests > _executed_tests )) ; then s= ; (( _expected_tests > 1 )) && s=s diag "Looks like you planned $_expected_tests test$s but only ran $_executed_tests." fi if (( _failed_tests > 0 )) ; then s= ; (( _failed_tests > 1 )) && s=s diag "Looks like you failed $_failed_tests test$s of $_executed_tests." fi return $rc } _exit_status(){ if (( _no_plan != 0 || _plan_set == 0 )) ; then return $_failed_tests fi if (( _expected_tests < _executed_tests )) ; then return $(( _executed_tests - _expected_tests )) fi return $(( _failed_tests + ( _expected_tests - _executed_tests ))) } _exit(){ local rc=${1:-''} if [[ -z "$rc" ]] ; then _exit_status rc=$? fi _cleanup local alt_rc=$? (( alt_rc != 0 )) && rc=$alt_rc trap - EXIT exit $rc } percona-xtradb-cluster-galera/tests/test_causal/SConstruct0000644000000000000000000000040012247075736024347 0ustar rootroot00000000000000Program('causal.cpp', CXXFLAGS='-O2 -std=c++0x', CPPFLAGS='-I../../galerautils/src -I/usr/include/mysql++ -I/usr/include/mysql', LIBS=['mysqlpp', 'boost_program_options', 'galerautils++'], LIBPATH=['../../galerautils/src']) percona-xtradb-cluster-galera/tests/test_causal/causal.cpp0000644000000000000000000002730712247075736024310 0ustar rootroot00000000000000// // Copyright (C) 2012 Codership Oy // // // Small utility program to test cluster causality. // // For commandling options, run with --help. // // Build requirements: // * C++11 capable compiler // * gu_utils.hpp from galerautils // * libmysql++ (libmysql++-dev on Ubuntu) // * Boost program options // // Example build command with g++ 4.6: // // g++ -std=c++0x -g -O3 -Wextra -Wall -I../../galerautils/src // -I/usr/include/mysql++ -I/usr/include/mysql causal.cpp // -lboost_program_options -lmysqlpp -L../../galerautils/src -lgalerautils++ // -o causal // #include "gu_utils.hpp" #include #include #include #include #include #include #include #include #include #include namespace po = boost::program_options; namespace causal { // Commandline parsing and configuration class Config { public: Config(int argc, char* argv[]) : db_ ("test"), read_hosts_ (), write_host_ ("localhost"), user_ ("test"), password_ ("testpass"), transactional_(false), duration_ (10), readers_ (1), disable_causal_reads_(false), compact_ (false), write_delay_ (0) { po::options_description other("Other options"); other.add_options() ("help,h", "Show help message") ("dry-run", "Print config and exit"); std::string read_hosts_str("localhost"); po::options_description config("Configuration"); config.add_options() ("db", po::value(&db_), "Database") ("read-host", po::value(&read_hosts_str), "Read host (:)") ("write-host", po::value(&write_host_), "Write host (:)") ("user" , po::value(&user_), "User") ("password", po::value(&password_), "Password") ("transactional", po::value(&transactional_), "Transactional") ("duration", po::value(&duration_), "Test duration in seconds") ("readers", po::value(&readers_), "Number of reader threads") ("disable-causal-reads", "Disable causal reads for reader connections") ("compact", po::value(&compact_), "Print output in one line") ("write-delay", po::value(&write_delay_), "Delay between write operations in milliseconds"); po::options_description opts; opts.add(config).add(other); po::variables_map vm; store(po::command_line_parser(argc, argv).options(opts).run(), vm); notify(vm); if (vm.count("disable-causal-reads")) { disable_causal_reads_ = true; } if (vm.count("help")) { std::cerr << "\nUsage: " << argv[0] << "\n" << opts << std::endl; exit(EXIT_SUCCESS); } if (vm.count("dry-run")) { std::cerr << "Config: " << "db : " << db_ << "\n" << "read-host : " << read_hosts_str << "\n" << "write-host : " << write_host_ << "\n" << "user : " << user_ << "\n" << "password : " << password_ << "\n" << "transactional : " << transactional_ << "\n" << "duration : " << duration_ << "\n" << "readers : " << readers_ << "\n" << "disable-causal-reads: " << disable_causal_reads_ << "\n" << "compact : " << compact_ << std::endl; exit(EXIT_SUCCESS); } read_hosts_ = gu::strsplit(read_hosts_str, ','); } const char* db() const { return db_.c_str(); } const char* read_host(size_t idx) const { return read_hosts_[idx % read_hosts_.size()].c_str(); } const char* write_host() const { return write_host_.c_str(); } const char* user() const { return user_.c_str(); } const char* password() const { return password_.c_str(); } bool transactional() const { return transactional_; } time_t duration() const { return duration_; } size_t readers() const { return readers_; } bool disable_causal_reads() const { return disable_causal_reads_; } bool compact() const { return compact_; } time_t write_delay() const { return write_delay_; } private: std::string db_; std::vector read_hosts_; std::string write_host_; std::string user_; std::string password_; bool transactional_; time_t duration_; size_t readers_; bool disable_causal_reads_; bool compact_; time_t write_delay_; }; // Global state class Global { public: static std::atomic_llong violations_; static std::atomic_llong value_; static std::atomic_llong written_value_; static std::atomic_llong reads_; static std::atomic_llong failures_; }; std::atomic_llong Global::violations_(0); std::atomic_llong Global::value_(0); std::atomic_llong Global::written_value_(0); std::atomic_llong Global::reads_(0); std::atomic_llong Global::failures_(0); // Reader class class Reader { public: Reader(const Config& config, size_t idx) : config_(config), conn_(config_.db(), config_.read_host(idx), config_.user(), config_.password()) { if (config.disable_causal_reads() == false) { (void)conn_.query("SET wsrep_causal_reads=1").execute(); } } ~Reader() { conn_.disconnect(); } Reader(const Reader&) = delete; void operator=(const Reader&) = delete; long long value() { long long ret(-1); if (config_.transactional()) { (void)conn_.query("START TRANSACTION").execute(); } mysqlpp::StoreQueryResult result( conn_.query("SELECT value FROM causal_test").store()); if (result.num_rows()) { ret = gu::from_string(result[0]["value"].c_str()); } else { throw std::runtime_error("select didn't result any value"); } if (config_.transactional()) { (void)conn_.query("COMMIT").execute(); } return ret; } private: const Config& config_; mysqlpp::Connection conn_; }; // Writer class class Writer { public: Writer(const Config& config) : config_(config), conn_(config_.db(), config_.write_host(), config_.user(), config_.password()) { } ~Writer() { conn_.disconnect(); } Writer(const Writer&) = delete; void operator=(const Writer&) = delete; void store_value(long long val) { std::ostringstream os; os << "UPDATE causal_test SET value = " << val; mysqlpp::Query query(conn_.query(os.str())); mysqlpp::SimpleResult result(query.execute()); if (!result) { throw std::runtime_error("failed to store value"); } } private: const Config& config_; mysqlpp::Connection conn_; }; void init(const char* db, const char* server, const char* user, const char* password) { mysqlpp::Connection conn(db, server, user, password); (void)conn.query("DROP TABLE IF EXISTS causal_test").execute(); (void)conn.query( "CREATE TABLE causal_test (value BIGINT PRIMARY KEY)") .execute(); std::ostringstream os; os << "INSERT INTO causal_test VALUES (" << Global::value_.fetch_add(1LL) << ")"; (void)conn.query(os.str()).execute(); conn.disconnect(); } } void writer_func(std::shared_ptr w, const causal::Config& config) { std::chrono::system_clock::time_point until( std::chrono::system_clock::now() + std::chrono::seconds(config.duration())); while (std::chrono::system_clock::now() < until) { long long val(causal::Global::value_.load()); w->store_value(val); causal::Global::written_value_.store(val); ++causal::Global::value_; std::this_thread::sleep_for(std::chrono::milliseconds(config.write_delay())); } } void reader_func(std::shared_ptr r, const causal::Config& config) { std::chrono::system_clock::time_point until( std::chrono::system_clock::now() + std::chrono::seconds(config.duration())); try { while (std::chrono::system_clock::now() < until) { long long expected(causal::Global::written_value_.load()); long long val(r->value()); if (val < expected) { ++causal::Global::violations_; } ++causal::Global::reads_; } } catch (...) { std::cerr << "reader failed" << std::endl; ++causal::Global::failures_; } } int main(int argc, char* argv[]) { causal::Config config(argc, argv); causal::init(config.db(), config.write_host(), config.user(), config.password()); std::thread writer_thd( std::bind( writer_func, std::shared_ptr(new causal::Writer(config)), config)); std::list reader_thds; for (size_t i(0); i < config.readers(); ++i) { reader_thds.push_back( std::thread( std::bind( reader_func, std::shared_ptr( new causal::Reader(config, i)), config))); } writer_thd.join(); std::for_each(reader_thds.begin(), reader_thds.end(), [](std::thread& thd) { thd.join(); }); long long reads(causal::Global::reads_.load()); long long violations(causal::Global::violations_.load()); if (config.compact() == true) { std::cout << "Reads " << reads << " Violations " << violations << " Writes " << causal::Global::value_.load() << " Failures " << causal::Global::failures_.load() << std::endl; } else { std::cout << "Reads : " << reads << "\n" << "Causal violations: " << violations << "\n" << "Fraction : " << (double)violations/(reads == 0 ? 1 : reads) << std::endl; } exit(EXIT_SUCCESS); } percona-xtradb-cluster-galera/tests/test_causal/causal.sh0000755000000000000000000000210012247075736024123 0ustar rootroot00000000000000#!/bin/bash -eu declare -r DIST_BASE=$(cd $(dirname $0)/..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf #declare -r SCRIPTS="$DIST_BASE/scripts" #. $SCRIPTS/jobs.sh #. $SCRIPTS/action.sh #. $SCRIPTS/kill.sh #. $SCRIPTS/misc.sh SCHEMA="test" TABLE="causal" USER="test" PSWD="testpass" for i in 0 1 2 do NODE[$i]="-h${NODE_INCOMING_HOST[$i]} -P${NODE_INCOMING_PORT[$i]}" done MYSQL="mysql -u$USER -p$PSWD" DROP_TABLE="DROP TABLE IF EXISTS $SCHEMA.$TABLE;" echo $DROP_TABLE | $MYSQL ${NODE[2]} CREATE_TABLE=\ "CREATE TABLE $SCHEMA.$TABLE (c1 INT AUTO_INCREMENT PRIMARY KEY, c2 INT)" echo $CREATE_TABLE | $MYSQL ${NODE[2]} sleep 1 echo "INSERT INTO $SCHEMA.$TABLE VALUES (1, 0)" | $MYSQL ${NODE[1]} failure=0 for (( i=1; i<=10000; i++ )) do echo "UPDATE $SCHEMA.$TABLE SET c2 = $i WHERE c1 = 1;" \ | $MYSQL ${NODE[1]} echo "SELECT c2 FROM $SCHEMA.$TABLE WHERE c1 = 1;" \ | $MYSQL ${NODE[0]} | grep ^$i >/dev/null || failure=$(( $failure + 1 )) done #[ $failure -ne 0 ] && echo "Causal failures: $failure" exit $failure # percona-xtradb-cluster-galera/tests/test_causal/hard_causal.sh0000755000000000000000000001236212247075736025134 0ustar rootroot00000000000000#!/bin/bash -eu # # The purpose of this test is to test correctness of hard causal semantics # as described in trac #688. # # Test outline: # * Start cluster # * Increase suspect timeout for second node in order to generate situation # where second node will still be forming new group after partitioning # while other nodes are already operating in new group # * Run causal test (shell script test doesnt't seem to be enough to # generate causal inconsistency in cluster running in ram and communicating # through loopback interface) # * Isolate second node by setting gmcast.isolate=true # # Test script will run three scenarios: # 1) Verify that causal test generates causal violations with # --disable-causal-reads and no causal violations occur # without --disable-causal-reads # 2) Verify that isolating second node from group generates causal violations # if evs.hard_causal=false # 3) Run test outlined above several times in order to ensure that # causal violations won't happen if evs.hard_causal=true # CAUSAL="$(dirname $0)/causal" if ! test -x $CAUSAL then echo "causal test executable required" exit 1 fi declare -r DIST_BASE=$(cd $(dirname $0)/..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/kill.sh . $SCRIPTS/misc.sh . $SCRIPTS/signal.sh SCHEMA="test" TABLE="hard_causal" USER="test" PSWD="testpass" echo "restart cluster" $SCRIPTS/command.sh restart WRITE_HOST="${NODE_INCOMING_HOST[0]}:${NODE_INCOMING_PORT[0]}" READ_HOST="${NODE_INCOMING_HOST[1]}:${NODE_INCOMING_PORT[1]}" READ_MYSQL_CTRL="mysql -u$USER -p$PSWD -h${NODE_INCOMING_HOST[1]} -P${NODE_INCOMING_PORT[1]}" function causal { $CAUSAL --compact true --write-host $WRITE_HOST --read-host $READ_HOST --db $SCHEMA --readers 8 $@ 2>$BASE_RUN/hard_causal.err } function wait_prim_synced { cnt=100 while test $cnt -gt 0 do status=`$READ_MYSQL_CTRL -ss -e "show status like 'wsrep_cluster_status'" | awk '{ print $2; }'` state=`$READ_MYSQL_CTRL -ss -e "show status like 'wsrep_local_state'" | awk '{ print $2; }'` if test "$status" = "Primary" && test "$state" = "4" then return fi sleep 1 cnt=$(($cnt - 1)) done if test $cnt = 0 then echo "failed to reach prim synced in 100 seconds" exit 1 fi } # # Stage 1 # echo "stage 1: check that causal violations can be produced" str=`causal --disable-causal-reads --duration 10` echo "causal: $str" if test `echo $str | awk '{print $4; }'` == 0 then echo "causal test failed to produce attempted causal violations" echo "output: $str" exit 1 fi echo "stage 1: check that causal violations are not produced if not attempted" str=`causal --duration 10` echo "causal: $str" if test `echo $str | awk '{ print $4 }'` != 0 then echo "causal violations" echo "causal: $str" exit 1 fi # # Stage 2 # echo "stage 2: check that causal violations are generated in view change if evs.causal_keepalive_period is high enough" $READ_MYSQL_CTRL -e "set global wsrep_provider_options='evs.causal_keepalive_period=PT100S'" round=10 violations=0 while test $round -gt 0 && test $violations == 0 do $READ_MYSQL_CTRL -e "set global wsrep_provider_options='evs.suspect_timeout=PT9S; evs.keepalive_period=PT100S'" echo "round $round" f=`mktemp` ( causal --duration 15 > $f ) & pid=$! echo "started causal with pid $pid" sleep 1 echo "isolating second node" $READ_MYSQL_CTRL -e "set global wsrep_provider_options='gmcast.isolate=true'" echo "waiting for causal test to finish" wait $pid str=`cat $f` rm $f echo "causal: $str" violations=`echo $str | awk '{ print $4; }'` $READ_MYSQL_CTRL -e "set global wsrep_provider_options='gmcast.isolate=false'" wait_prim_synced round=$(($round - 1)) done # if test $violations == 0 then echo "stage 2 failed to generate violations" exit 1 fi # # Stage 3 # echo "stage 3: check that causal violations don't happen if evs.causal_keepalive_period is short enough" $READ_MYSQL_CTRL -e "set global wsrep_provider_options='evs.causal_keepalive_period=PT0.5S'" round=1000 violations=0 echo "running $round rounds, reader is isolated" while test $round -gt 0 && test $violations == 0 do $READ_MYSQL_CTRL -e "set global wsrep_provider_options='evs.suspect_timeout=PT9S; evs.keepalive_period=PT100S'" echo "round: $round" f=`mktemp` ( causal --duration 15 > $f ) & pid=$! echo "started causal with pid $pid" sleep 1 if test $(($round % 2)) == 0 then echo "isolating second node" $READ_MYSQL_CTRL -e "set global wsrep_provider_options='gmcast.isolate=true'" sleep 9 $READ_MYSQL_CTRL -e "set global wsrep_provider_options='gmcast.isolate=false'" else echo "signalling node 1 to stop" signal_node STOP 1 sleep 9 signal_node CONT 1 fi echo "waiting for causal test to finish" wait $pid str=`cat $f` rm $f echo "output: $str" violations=`echo $str | awk '{ print $4; }'` wait_prim_synced round=$(($round - 1)) done if test $violations != 0 then echo "stage 3 generated violations" exit 1 fipercona-xtradb-cluster-galera/tests/test_cppcheck/run.sh0000755000000000000000000000104012247075736023771 0ustar rootroot00000000000000#!/bin/sh -eu OS=$(uname) [ $OS = "Linux" ] && JOBS=$(grep -c ^processor /proc/cpuinfo) || JOBS=1 TEST_ROOT=$(cd $(dirname $0); pwd -P) GALERA_SRC=$TEST_ROOT/../../ LOGFILE=cppcheck.log # --xml output provides us with error ID in case we need to add suppression cppcheck --quiet -j $JOBS --force --inline-suppr --xml --inconclusive \ $GALERA_SRC 2>$LOGFILE RCODE=$(grep -c '^ $GALERA_RESULT_DIR/out exit 1 percona-xtradb-cluster-galera/tests/test_dots/run.sh0000644000000000000000000000010212247075736023155 0ustar rootroot00000000000000#!/bin/sh echo "Not implemented" > $GALERA_RESULT_DIR/out exit 1 percona-xtradb-cluster-galera/tests/test_drupal/README0000644000000000000000000000700312247075736023222 0ustar rootroot00000000000000FILES IN THIS DIRECTORY: Drupal 5 files (obsolete): drupaldb.sql.gz -- dump of drupal 5 MySQL database drupal.jmx -- jmeter configuration file drupal5.diff -- patch to make drupal replay transactions Drupal 6 files: drupal6.10.tgz -- Drupal 6 installation configured for drupal:password login. Unpack it into webserver root (/var/www) drupal6.sql.gz -- dump of drupal 6 MySQL database drupal6_8080.jmx -- jmeter test file for drupal6, verified to work with 127.0.0.1:8080 address drupal6.jmx -- attempt to make universal test file out of drupal6_8080.jmx work in progress loaddb.sh -- script to create drupal databases and grant permissions. SETTING UP THE NODES: First, running the test requires nodes to be set up: Drupal installed and Apache configured. This script cannot do it automatically. How to do it is described below. It needs to be done only once. Second, the address of Drupal site is hardcoded into the Jmeter test script: http://127.0.0.8080/drupal6. This requires glbd be listening on the client host at port 8080, and Drupal files be installed under /drupal6 (e.g. /var/www/drupal6 on Ubuntu) 1. Install Drupal 6 in each cluster node so that it can be found at http://
/drupal6 path. Configure it to use user 'drupal' and password 'password' to connect to 'drupal6' database. For that edit $db_url setting in sites/default/settings.php Alternatively you can just unpack the provided drupal6.10.tgz package in the webserver root directory and grant webserver rw permissions to it 2. Fix Apache to use clean URLs by adding the following to Apache config (in Ubuntu it is normally /etc/apache2/sites-enabled/000-default): Options -Indexes FollowSymLinks MultiViews AllowOverride All Order allow,deny allow from all RUNNING THE TEST: 1. Initialize drupal database by running loaddb.sh. See the script for the configuration options. Galera distribution does not come prepared with Drupal user privileges, and currently does not support GRANT command replication. That means that GRANT command must be manually repeated on every node to create drupal user privileges. Alternatively you can run loaddb.sh against each node in turn. After that you need to run it olny one when you want to reset the test. 2. After database initialization, Drupal needs yet another action to run properly: You need to login to your Drupal site as root:rootpass, say, using links, and then go to the following link: Administration->Reports->Status Report->run manually (cron jobs) Without doing that, some of the pages for some reason become inaccessible (no idea why) and you'll get false error positives. This procedure needs to be performed every time drupal databse is reloaded. I don't know how to fix that or how to make it automatic. 3. Start glbd on a client machine like that: ./glbd -t 2 8080 node1:80 node2:80 node3:80 4. Start jmeter: $ jmeter --testfile drupal6_8080.jmx Hit Ctrl-R to start the test. NOTE1: During the first few runs it will stop with error about unable to find requested JavaScript file. But after 2-3 runs this file will mysteriously appear on all cluster nodes and it won't be a problem after that. One solution would be to keep Drupal files directory on a shared disk. NOTE2: It is essential to run jmeter in GUI mode to be able to catch and analyze errors. percona-xtradb-cluster-galera/tests/test_drupal/drupal.jmx0000644000000000000000000044512712247075736024366 0ustar rootroot00000000000000 false false 127.0.0.1 true 1000 300.0 false -1 10 10 1233463594000 1233463594000 true stopthread 600 0 true rfc2109 1000 300.0 http /drupal/ GET true false true false true false Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 false name test = true false pass testpass = true false op Log+in = true false form_id user_login_block = http /drupal/node?destination=node POST true false true false true false Content-Type application/x-www-form-urlencoded Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http utf-8 /drupal/node GET true false true false true false Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http /drupal/comment/reply/2 GET true false true false true false Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/node Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 true subject malo sexa = true true comment titek net! = true false form_token ${form_token} = true false form_id comment_form = true true op Preview comment = true http utf-8 /drupal/comment/reply/2 POST true false true false true false Content-Type application/x-www-form-urlencoded Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/comment/reply/2 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 true subject malo sexa = true true comment titek net! = true false form_token ${form_token} = true false form_id comment_form = true true op Post comment = true http utf-8 /drupal/comment/reply/2 POST true false true false true false Content-Type application/x-www-form-urlencoded Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/comment/reply/2 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http /drupal/node/2 GET true false true false true false Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/comment/reply/2 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http utf-8 /drupal/ GET true false true false true false Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/node/2 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http utf-8 /drupal/node/2 GET true false true false true false Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http /drupal/logout GET true false true false true false Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/node/2 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http utf-8 /drupal/ GET true false true false true false Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/node/2 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 false saveConfig true true true true true true true false true true false false true false false false false false 0 true false saveConfig true true true true true true true false true true false false true false false false false false 0 true false -1 20 20 1233457549000 1233457549000 true stopthread 600 0 false rfc2109 300 false name test = true false pass testpass = true false op Log+in = true false form_id user_login_block = true http /drupal/node?destination=node POST true false true false true false Content-Type application/x-www-form-urlencoded Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http utf-8 /drupal/node GET true false true false true false Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http utf-8 /drupal/node/2 GET true false true false true false Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http /drupal/user/2 GET true false true false true false Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/node Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http /drupal/node/add GET true false true false true false Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/user/2 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http /drupal/node/add/story GET true false true false true false Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/node/add Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http /drupal/logout GET true false true false true false Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/node/add/story Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http /drupal/ GET true false true false true false Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/node/add/story Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 false saveConfig true true true true true true true false true true false false true false false false false false 0 true false saveConfig true true true true true true true false true true false false true false false false false false 0 true false -1 5 5 1233457451000 1233457451000 true continue 600 0 continue after errors - trying to access forbidden location - it's ok false rfc2109 300 100.0 http utf-8 /drupal/ GET true false true false true false Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/node/2 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 false name test = true false pass testpass = true false op Log+in = true false form_id user_login_block = http /drupal/node?destination=node POST true false true false true false Content-Type application/x-www-form-urlencoded Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http utf-8 /drupal/node GET true false true false true false Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http /drupal/node/add GET true false true false true false Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/node Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http /drupal/node/add/page GET true false true false true false Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/node/add Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 true title test page = true false changed = true true body test content = true false form_token cc69ea143915e2247edd27d40223a3af = false form_id page_node_form = false op Submit = false = true http utf-8 /drupal/node/add/page POST true false true false true false Content-Type application/x-www-form-urlencoded Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/node/add/page Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http /drupal/node/1 GET true false true false true false Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/node/add/page Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http /drupal/ GET true false true false true false Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/node/1 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http /drupal/user/2 GET true false true false true false Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http utf-8 /drupal/node/add GET true false true false true false Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/user/2 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 127.0.0.1 http /drupal/logout GET true false true false true false Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/node/add Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http utf-8 /drupal/ GET true false true false true false Accept-Language en-us,en;q=0.7,ru;q=0.3 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 Referer http://127.0.0.1/drupal/node/add Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 false saveConfig true true true true true true true false true true false false true false false false false false 0 true false saveConfig true true true true true true true false true true false false true false false false false false 0 true percona-xtradb-cluster-galera/tests/test_drupal/drupal5.diff0000644000000000000000000001540112247075736024551 0ustar rootroot00000000000000Index: includes/database.mysql.inc =================================================================== --- includes/database.mysql.inc (revision 5) +++ includes/database.mysql.inc (revision 8) @@ -167,11 +167,13 @@ print '

query: '. $query .'
error:'. mysql_error($active_db) .'

'; } - if (!mysql_errno($active_db)) { + $my_err = mysql_errno($active_db); + if (!$my_err) { return $result; } else { - trigger_error(check_plain(mysql_error($active_db) ."\nquery: ". $query), E_USER_WARNING); + if ($my_err != 1213) // don't warn on deadlock + trigger_error(check_plain(mysql_error($active_db) ."\nquery: ". $query), E_USER_WARNING); return FALSE; } } @@ -261,11 +263,21 @@ * with table prefixes. For example, db_next_id('{node}_nid'); */ function db_next_id($name) { + global $active_db; $name = db_prefix_tables($name); + db_query('LOCK TABLES {sequences} WRITE'); - $id = db_result(db_query("SELECT id FROM {sequences} WHERE name = '%s'", $name)) + 1; - db_query("REPLACE INTO {sequences} VALUES ('%s', %d)", $name, $id); - db_query('UNLOCK TABLES'); + do { + $id = db_result(db_query("SELECT id FROM {sequences} WHERE name = '%s' FOR UPDATE", $name)) + 1; + if (mysql_errno($active_db)) continue; + if ($id > 1) { // exitsting row + db_query("UPDATE {sequences} SET id = %d WHERE name = '%s'", $id, $name); + } else { // new row + db_query("INSERT INTO {sequences} VALUES ('%s', %d)", $name, $id); + } + if (mysql_errno($active_db)) continue; + db_query('UNLOCK TABLES'); + } while (mysql_errno($active_db) == 1213); // ER_LOCK_DEADLOCK return $id; } Index: includes/bootstrap.inc =================================================================== --- includes/bootstrap.inc (revision 5) +++ includes/bootstrap.inc (revision 8) @@ -466,9 +466,13 @@ global $conf; db_lock_table('variable'); - db_query("DELETE FROM {variable} WHERE name = '%s'", $name); - db_query("INSERT INTO {variable} (name, value) VALUES ('%s', '%s')", $name, serialize($value)); - db_unlock_tables(); + do { + db_query("DELETE FROM {variable} WHERE name = '%s'", $name); + if (db_error()) continue; + db_query("INSERT INTO {variable} (name, value) VALUES ('%s', '%s')", $name, serialize($value)); + if (db_error()) continue; + db_unlock_tables(); + } while (db_error() == 1213); cache_clear_all('variables', 'cache'); Index: includes/database.mysqli.inc =================================================================== --- includes/database.mysqli.inc (revision 5) +++ includes/database.mysqli.inc (revision 8) @@ -149,11 +149,13 @@ print '

query: '. $query .'
error:'. mysqli_error($active_db) .'

'; } - if (!mysqli_errno($active_db)) { + $my_err = mysqli_errno($active_db); + if (!$my_err) { return $result; } else { - trigger_error(check_plain(mysqli_error($active_db) ."\nquery: ". $query), E_USER_WARNING); + if ($my_err != 1213) + trigger_error(check_plain(mysqli_error($active_db) ."\nquery: ". $query), E_USER_WARNING); return FALSE; } } @@ -244,11 +246,21 @@ * with table prefixes. For example, db_next_id('{node}_nid'); */ function db_next_id($name) { + global $active_db; $name = db_prefix_tables($name); + db_query('LOCK TABLES {sequences} WRITE'); - $id = db_result(db_query("SELECT id FROM {sequences} WHERE name = '%s'", $name)) + 1; - db_query("REPLACE INTO {sequences} VALUES ('%s', %d)", $name, $id); - db_query('UNLOCK TABLES'); + do { + $id = db_result(db_query("SELECT id FROM {sequences} WHERE name = '%s' FOR UPDATE", $name)) + 1; + if (mysqli_errno($active_db)) continue; + if ($id > 1) { // exitsting row + db_query("UPDATE {sequences} SET id = %d WHERE name = '%s'", $id, $name); + } else { // new row + db_query("INSERT INTO {sequences} VALUES ('%s', %d)", $name, $id); + } + if (mysqli_errno($active_db)) continue; + db_query('UNLOCK TABLES'); + } while (mysqli_errno($active_db) == 1213); // ER_LOCK_DEADLOCK return $id; } Index: includes/cache.inc =================================================================== --- includes/cache.inc (revision 5) +++ includes/cache.inc (revision 8) @@ -93,11 +93,15 @@ */ function cache_set($cid, $table = 'cache', $data, $expire = CACHE_PERMANENT, $headers = NULL) { db_lock_table($table); - db_query("UPDATE {". $table. "} SET data = %b, created = %d, expire = %d, headers = '%s' WHERE cid = '%s'", $data, time(), $expire, $headers, $cid); - if (!db_affected_rows()) { - @db_query("INSERT INTO {". $table. "} (cid, data, created, expire, headers) VALUES ('%s', %b, %d, %d, '%s')", $cid, $data, time(), $expire, $headers); - } - db_unlock_tables(); + do { + db_query("UPDATE {". $table. "} SET data = %b, created = %d, expire = %d, headers = '%s' WHERE cid = '%s'", $data, time(), $expire, $headers, $cid); + if (db_error()) continue; + if (!db_affected_rows()) { + @db_query("INSERT INTO {". $table. "} (cid, data, created, expire, headers) VALUES ('%s', %b, %d, %d, '%s')", $cid, $data, time(), $expire, $headers); + } + if (db_error()) continue; + db_unlock_tables(); + } while (db_error() == 1213); // deadlock } /** Index: modules/block/block.module =================================================================== --- modules/block/block.module (revision 5) +++ modules/block/block.module (revision 8) @@ -188,14 +188,19 @@ } db_lock_table('blocks'); - // Remove all blocks from table. - db_query("DELETE FROM {blocks} WHERE theme = '%s'", $theme_key); + do { + // Remove all blocks from table. + db_query("DELETE FROM {blocks} WHERE theme = '%s'", $theme_key); + if (db_error()) continue; - // Reinsert new set of blocks into table. - foreach ($blocks as $block) { - db_query("INSERT INTO {blocks} (module, delta, theme, status, weight, region, visibility, pages, custom, throttle, title) VALUES ('%s', '%s', '%s', %d, %d, '%s', %d, '%s', %d, %d, '%s')", $block['module'], $block['delta'], $theme_key, $block['status'], $block['weight'], $block['region'], $block['visibility'], $block['pages'], $block['custom'], $block['throttle'], $block['title']); - } - db_unlock_tables(); + // Reinsert new set of blocks into table. + foreach ($blocks as $block) { + db_query("INSERT INTO {blocks} (module, delta, theme, status, weight, region, visibility, pages, custom, throttle, title) VALUES ('%s', '%s', '%s', %d, %d, '%s', %d, '%s', %d, %d, '%s')", $block['module'], $block['delta'], $theme_key, $block['status'], $block['weight'], $block['region'], $block['visibility'], $block['pages'], $block['custom'], $block['throttle'], $block['title']); + if (db_error()) break; + } + if (db_error()) continue; + db_unlock_tables(); + } while (db_error() == 1213); // replay on deadlock return $blocks; } percona-xtradb-cluster-galera/tests/test_drupal/drupal6.10.tgz0000644000000000000000000516637512247075736024713 0ustar rootroot00000000000000MIN/^t:QA{rBHO~ԋrms~%0o!ݣ?{|?S_S~9Oɋf dIQE=9=:9yqp}~M>/F^Wj{!osѫ%c_RhiC}(X/JkH$Kkc@-:ad1_k!ȿd*s;@"h7e<)$6;2~wOOCL0EhI%Ybu_]Hd8Jfͽ׍m'Yi0Џf} 2,NrՌnZ)ˏxVm!Fȏg?8@^gYhмQ4g~I8 'fh#2{+<#l ' ~1ePXp~_h kmh@yIVyiBTb H: /TPf;! d=u "XF*`>(V:a\ D+arNԁ~T˜A=`(&:JEķ<ei%@E~1<%*˯$̴300 ~|dUXn[Lim:SJJ,TH#a(Kru" Z|ykC\.@d'Ċ?qB(gQ4 d:#m\]%Wǒ\HwaU O 4pjB΀ K\)Paw t=OilkɄ6DJz``_Pg '?-#'&G,r/ʿOB7Bܣ:8fѻ5c*JTg]~m8`WvQ3yiKk1qTN4i{B@4 4 3L(n AΊ,O ꃈdX뀥7Kѕ +|pø-0j7EG<)#vb\x<Z@^TGM*I9MZKUFDN+YփrO5"C@89]1Ȼ53%>4"AD ʓnE8Bt /\fZ\H\+WBjb &br IHY]AJH/Ҩ'~^Qd:w2)(vz;myKk IZA7X'I tП9ik 6Yꌭ&E@-\%l(∡! Ӻ6@W*i!18,c4p[Kc7kXDuHm@x($Y*Ķ\""ٕW`\G )) -6ϺE"6A"Z %!o1Di5"s Dg lXW|08r:u2>rx~:w>O] ANYUd {Ɇ;A[9ŏbgĄd>cC!İxa#qDe hbbjdDm:'fly+ C\h2=e"jHjpdRufϋ"}niqKlfm4xNd|&:Wnp5(ڳ8Y\5k=W;=^|пh0<D>(V2(D'R q7>~`D%3pgFxXm7NJvNL ZYh n @&qR܇0CGLe^qfn9&۩H̺J4]c<~Yq7 rqFn60)o?N}G r_kzh%3 YۯEd\F(6U > JX^T0#M2B˱%qMoP̏0`Ujkt]9y9~uYSh\eaBU2?̪*8Ǣ9ƁR:$]w5_ &T˓>rP-ESa)BIҜ:Xu@HE& 6*DզO:[vDid`ou ؋aLKF^²mT26d^q X?Q; VJS"^%!T.&cFMqQ84'ТKV ّ;>qWFPorg;eL551NUMss޼'XD,dIÖJVRQ5|ճ,6iq]O91љS ƶEv㹫h"P !hT@>ҙ+4 )1>sDkJLm%j{f nEVN0RqqDUC 0NЯr#=Py}4 %P7[(Fw~T= I!-9*c!ۥo3ipDS?f$ɦALPT@v+2S7zw#z2d\0s@UaO U7(o8MKڼݮ)m`@oU@SS"m\NQ4 J.jJl2&6@z$;\ka tJ%:\K@۱~.aZC8DaD)5q_$)]B:^tJ]"$m $HH<(;FzUɛؚ бO?r.N{?J@沀t5F:g'J }|N5k5෗둺|? F_ՃOV%Y}i)ek/6oQua*G[Yy~, 1Ja7aFsY]YK]}R]c^3J=¢[6~!cW+wbc8I^\UƏq(ڒ=. )?Qz@DM@%1\d1x L8cΩaCZH ,@@1\Uzʮ:*, ZNa9ўS̒ JcI3>s)=N񒂁Ium0'Olzpy;W׽ȇ'N ] h#`%im PY(>wܳR7|ǜ2bXZuLT=n3Ho\8i6BKMhr nHSQ{ocg'X!tܒbF 1 rޚI( S|  JƹḢr{8hSb R6|kfw.2&&0 jt9TS,&q ITEl!>CC#^Ўa.gA?/vMeئ\ߡZpn@nMZ3C*6[uFE)V[\chdwL44F]APP"V/8읩Xř2[SHfc,dM%oHϚa:E- 59BqA~TLz}apaR@K>l C.$QG9 -V)6.M>2 ] 2U%^yߡ17ރǓ1~dkS!jaVڦ;GLF-}h{{SF:UeQ-,|Xn NH9XEL*K5/(s PoqnSGe"0iϋED2ԒZL8p/tY~ tSksb`$ g\,'S\+t0h-UsM|#SN$ y+hL^\+ n;a$baALON%Rk1vZJ,Y{ouRT͐, d+ރyV 0?8m BڼUmٸ_fmR=K)sCTL]u/b3\=soI <BFC3rydN0բ{=5tXTOCjAAs}(̍5g5]ZJeb򃈣Ԅgk6|ص*>y{\sT~0T/LҵA/hrZN㽆V"DE7seٷ& _U!%I4|axsAr(UյJ㙐b9"D%djIOkm &@ge`6,1%A]u ,˶l5 ȫݑ4jgٕ,JB !@B%!P@L C1`jC%{$H `iέ5 фrg(9CP !40.HOra aFܜF3pgX x(=8v8&3pUvvyZ5)Le)Z`&Aܮ)NRr0LYPDD/f bjU_(V/}X3&xQl*$V ^qO@DQBR3@)MƋ42(~IhcHtBei@{q~!1p$4l)NavpuWDlVoN!n90B+o._=Y6 $}?brmMm);75. [9(bD::g6vv6Ύ <^2._-* o[_Oz!A fnj ۺ<Ya_JhR])9-`= Oa1;#pz B_y>%TB2};ƥFcGB51/L\0)Ke냕Hs;J]4N ӊa3QV `m p[ >K߷Db:E,`z hąߨPH,#m29KUL\<P44ICB*ؗZr#yr Yia+*&EFxTřrnlt[3KBzdP 1lDm 'A1" z>RJ6iSB@y1vpM6Mj3BF#1*F:O\nJJTb(j3EšRhmүz4VfcUPj϶8 +(`OT?;a ғv͒𽊿7j^?\2J%ܡ4 @td E3甓k099'#@9&߲m t !I3áBD%ż t4:XƒHiAǣ,, 9I%155(dcg@#Xtε0/1H\ĽkEP~WdU6AO< 8BQ Y#T텴T0!BݔhB] )ʼ}RXȅQ1t5w6E[Z/fʗB){UT h3Ɇx="y!p9ueGO|&XN*]9 |ä1L1.f B9R ML;$ĹiA|)hr;xӀ% דD_,xA:9inTL2).3(I*k*bf2-KHNWPtR<0$-bM;`q>h4 O"ZṖ)m*c(&v 55?D,2bDqT11g=G S6bQJ4'Ҡۂ%I(s՟܂KAAhs?g!Fz,|u&J/Ң~[.*,Pw(@ J|ָ)NqOW-`sLT>OtPQၰR!wW'rPtMvB=@El-~؃EX7 NFw]L'@&沪o۬d@6r l.p jĽ,YBF^1nPm[<$xD7t_7HKv _=Eu"]9gh x7WZ )Tr֍ r B=nl$kgBҀE L_v$1 !{L8qcPx% bkr:Zk\"R/YLOM㘖*}HLͶB tt "eSQDŽ?P#GNP\ Fe=!~%!g/ S9J[Θс|? xI gi{U4Ϲ#E]H]G榆VUTVV@e]d_W@d5*TM r[ٍF{ ђl0 AcnVɓ+ĝ dg4JJ'M⯌Yiq`#voftMoJQD˅7"B49,qu %z<$WŰ (K [.iS8m.bfq#𶥰EXɐh*@#)/ɐÁwf% U {!ߡ&+Fqؽێ@3afߗ$dHdC!*Vr8ʢ (tos#ǀkZ4J0#=$bG$%%eJy}Pgue"h,4+`=VhGz(r@U7p c&Ⳁ,%ն1fFN&2BЍ,8L lꙓ%%on@^jC;:tbr'!:> >SJ\k[rʃ2Y6X$FBgTZV"Vp\!]2 7=ҷ+X}m/P[qX|<Lo@_V:u v لўbTDwYRY*毗 y/&rfQnj&4 6^ %BVHz;r(ڊцmppS^n|DUe$TM%4>{RXJ'$8|L8q p$K&:3$7;fJa_P;w x6F;D#T2:^Ll.$I6'Bp&I KpnLE1;Nڤq\Jv{8J.NPQ9G:( TM5C 3XbO$ &Dq7M[ 0(ea`,9v.1N&z@OaG3zƀcih!C[&pDYm틚Zg>hх)`K̦YM My%ލ8bdQD@~%0%\ו:v")Tì2@V]?4RKί]i֋کEAʲ PeQ[A>ξ" )LqIj-7]R4@e@c+ywД Gjt5Acʂ˕W J\ HFPG(@/ris̛V -:+׋@Do4 ʯǁE(8- RH?y^@c$꒠w1SI¢R8VPҤ1go MGTFl=yh)B ǔzk0oldY!Bԅqq#)L=+NZ kY~ؕԥ8&L4~wFl ErݛaE8YѷYw#cAV@4iRcҕ=h!tk|,"hrc8{υ%E*!|0Bi媉biB2(RJ"{xWu2eDLIuͲDs׾T¿"06 <;/ol^Zdr"F0Y^)( mpy0!6"QvbbJ՗(^*Mc!{6ǎ I-ٍdڲk-Zҹ>!&ナ/-!Ov1zyCpMp)ȆŖj|D|ɗA\@t>A _ps}:2z0~wאfP]*QfBu*o&2'ׁr1: )pj9p_.gGx&jXNw}#I[C(@׈zIbrjm3w@uEacFcC}W*wo1";Әdwn s)YTq!T%NڌEF}{h~Fs\@f†Nc@4I'Y/4b*`7U:f45l@n:hwt65FfzUT.24uiTշ.25 MPƈ@"z(~5Q3D mbD?;ۂTdΈ-Y?I|;['\\O=ojho4P4"&)2ϨxbW Ŵm`FĒ1b3၀|&ј8iX^LEt57 Hc@Gc{}~Hwt@+m$[*ðxb4·= 傾yvQ?[6Lm qW/_$QRPًx{n*ضwWMm03D[#0!D3[g7F ӌ$Ɔ&{Z7ӬStH"@Fz04؇dpa="?%}Es[k>C%{,=hlǩcMKV\OsSLypYMPgėBԂM) 0fO53 ucpgX}AnB?.ZbYoNCټXL`RS 1Dc˹l4‘@޷%ABhki+$/X2kA- FCTKi΍T-(h%_zJGp2Hx[ĵ=j\DWw(%K !qۭssoG?唀12SyeymFu5uUUWTV?G??@T]YEEYeQQ;vJuPQ@n`sAbp`Qz%\R?pdt MhP)+x`PU Bˡ؎[Mn@fl'|Qް.e9qa/r { yЁ* UZ>'/l(iIMj_AZ6H}1D+RmBn-)BZf"NEPuȋ42D6 YL^9E75?\{4M~+Ž&.֍ t %iSwŐ`L|OŽ- Q.;$ M(4Z];S#آ HG&$=.wxEOs3qhC,$hy҂ 8'!O;ܨԐ*oMe76J0q/!2ER-H" qЪ,i.麢 TUUKY%\-׬zӞ*upș/~wzo::`!,A/b'pJОpjA2B)^t=GKh6uL^~+ K,mx735ܕ . A$^GUsPd.' iaB>iA>o\6X)Ŏz=}T+H+ /dHSX\>VI`s IOhF1ⷢ|j6t7ríEWViAq_! kd% Ko>Ha%|rHI)3JM!^t}|\>LŮ~3 ɨMSQpW6 -|FGhĸB-"R%.[ {Za U 5$nD/8Z'MLG& `+̗6mX- z.o &iD@%d&T|)}䩄w:svMw QʭPWܘl\̪4uFl45uWO '_ЋƐ5}~k1]#qVe d6Pb0z`#EU9oR @"(ۓ+l 7t`o!\8iL W #ƕ?w,xLgU'I(/YK<~>h1}Sh#q%,Rhoi:A |:@NdGqw^d|Gmj%)'a[mV@)ߐH2}*XA82gg> q"SUGq7|:bxW\H"jø$ZEIY׽0Cv:An!W47Njj # xnJ2fbN7 mA(UZKVQ~7%5B5TtZ6{ ۡ/- k#-*O巅#ZO-!z, wtǝ rVE= t–Kn؉t;fd? d_J[]Q^ATE B{s #Z*Լo.\5IMR[h]&gb{XPh#n^miƎ@o-BUZ.kZZIIybW {sU$C>} W`bt"niE]PN`c`sCv# Z\z,#tsIV @0ik%rKAf;g)4Ym9fk_$җ"DQ<xUA40;Boooï. SAd5*0Ҹ:ň&8@J$`vBHǰ}q:JLIќ2& @9b;d(y|f4)0qҬu;XLU,{&. C5} _D~wS{bF f~[GU0 81;a5 ^fcA(]BW! p d J;6`eiu[v}匀CL{/SxR;0`@ejEe x 6MݙB$ Zw.؏l?k#B ''w$NއxMDE;yyZ2JP9uU5~a)jZn0 ;>mL daPJ!iӨ+DB ڑ$;=Ԧ(C<;[;^aW br+X8Z[>W<2 z]#&fyx¢(< iE .WlY!+'A&ϱ Q oi- )!Pp!xaU|҃*^h^5\|E쀈T(l}lUD-"Ni`]ژ)^@c§#n83F6K k4%ݕ֜FZX::@LynT )g{73FqČ[b %߼˵FB7 t: R.5J${ p¸PAf $~NɁpLYI '`SAyS/9f<*5׌9CtRkD39.-<(F&F y/Je0r<%#pg;۞ kP"#T_I*5a@hX u阑 mR4r.||$mbj@ #;%rJ.D ē:.6p}T])ɊNB! ]Fl8&=CV,|$b b 2X ` +6쿀"-ͺ-!$.Y08c%xpʷvRQ\)IGMAM&TL"w[!ԖC\~QDb ZM#y)4\!dGX"h^uuY/wᬔOWq&6՛ao]=7HQ#\F"#8tcp;N[5FK»ynOϴstȵ ޝZYnty {.9]GE_*R~~5A"<Vr n&U#4:JKݚPe!TAFyOYftэ C7n< Cai&B,Nt~d\1riWz?gI\Œ/= <抌$Q!=MUjv( O1Wu2&VplNHYA<(KDF7wI,B\n'5>&C)3c!+ \ VAE&HCó%w{0I^.%Jv!I 3t [Ar R Vd=-A>)>X6Iȅa`յ*uJa) ӗyу|('(@k!"}D/GLXA*:R x $-{0Umk S,;x̐FG{E sy,liu7K1CSh;ipNJX&ŲPKԋSKsX))۩"'j _y)oZs<(!dʸЄU!Sg8^v}y_ ;G$bxۄN6eFsh`ldvHNp8컏JB{H KlÛl*`5 ~T!7F ǰh)?11BDDEϳH8* `x&(`f7򺅊ֿ\)\;)|7 c9# :r?rQn\Nf k)Lԃ4B[!»/jL2ožeG3?U u(I<_ꜣ)W=',KAfPrS4%GNiNA,'УV(Ja:POg~kYdbA (BH$[ a吸 K*9~A9O1ek"[!><;3cxgTy=(NTOgHid5^O`7#͂BG 8KU!i~bfg;2'AdT_*e;჊(h `!䔻Iꂾ%oqB͍$S?2K# -W ١cC?;X\ --hop=CqAI ')ICRmԐ𣿁֏b3u qN7.mqf+&UX_Me!s@AIfz|*SR*S *S)~*S,Q#L$ϊNR izMa58 ݨY*N0PWS9Ea Os op- *g?} Ħ>]!j 6=U`8|_f^P-8 nނe ǽ I6'y\V!W![d21JġI0Zsn#M34Os:0(!/#8s=#$7s!_'ոd|ЙW $s8vX)rd%:BMuRJ5sP)(I@!@f":;Kf%&+ _@W K̰Jڣv)8Qx>)uv dB9P1Q*LW\3ص2 ͉B78b} ?#F<'HLkߑVJ͸ײ]ԼhX(\"b%Dע> CGiYQ? D2&SK"bl82*DG~(9or)n-yBTzm2: R;Y(l1z]!GȦ xLG=hhBt^7Ml( hBA(өs>TV,dubƁFyWvF;PMͫLJ3 dVɚfC$"7[\) .2Aۮxff:MKBcODJ߮ 2mhxBÃC]ͫv<%ҫkor nL#WN4V-Nr<,iӽ\գ X|\UzT3vCUO䱲)v1| P- C ;xvSc3,3T/-"Is(m3DrL76x Xk@"IXJ61̧O("oZ˻9-)%/,1/%Q 'T@|sf$gebya` P|' C }<~K뺢/Ÿe"j)cB25+*AU Ollj G)}c"7=BC 709, ľ A#ˋjFRau 7.&.1} d10 Cܳ}[PY#Jqb-vT4er N}%옢ٶ;\HHEAIIwz<-aR{Sŋ.]cHfAЁOuMk  [ Wg_s\r9TfRNk2ZT;ȴ]NH J\WBȿR$Iafb#?68_`p KD-?!+tQDkaRBT A?\'4'Uc<^>ZV Ms]r}72c0DKDy94DN; [(:׀B) Mz#!ߨ%: ޲A}TVKz,h@efT>ªs/Ě\sETH^tw" yea:Yk9G̩\J@bzN!tJ] ph;Fe($$_ؗcTDɄ Cٔ.Ya(Aˑ8D^] %EOt'l;]Yr9PڜH3d9|T^i@)qm8ҧ2ez-FOAҺIKMknJ(ܰ{DZ:92$UUGT.}-pbչ:GP&)3́dǞZF 1ZIJE,^ǒLv>le%.t!)e:w8~D`e HCK ADgPݦ 5 -W ZK;J+i귅 gs+ }A<=y4G2T,M~*%W3'zPZ5EʳU~H̅ C`omLcz9bcWca{Dn5Y\;P Jc@[]Wi躞t %le:T ($IRtL쨧;&&~zK9L& R"PuRW&0I㜋# j^"2J"Yqh0Q,aeCP ﱒ'yB|_tQ'*g0WJH9' M3ћN0ݗKdW(a9_Yd*`UyX2֓2c䖤̕ekiLA7=ihA qz.( ޮ&44J*5*Q(#jdG^^Grug;Ȋ[ts+UM!mruQz݂YPH&dJFM$TB*3GT,56Ps¦F`T&  A/"# J3&LEalyB`4w~GB]{@I+,.Ҡe84R5F:!,!&LU}U94.YICB1?fBޓi~ETL{D=ZKˬnȧAl4+Ԏسn )޸0 Wg(E!zZ\ @O&{rv5ZιZj{JGWTbwfphyTY^2R1R@}̔+9]75h{PjuRYM٤tD`;`4W mtRkYꗪ> } y|SA_FjɰLa%Mq= ɀ8Kk/OFvZ?,Ih>>}ӉJNR [) H=4r}rp⪘ \|߀bJ3c} }YFI6SXTTV7|AG|As%E\wk9(wmYYrըF_?B^ &G/N7,sXmahk+uj5B pFih).P 1>dU E08/ tmW>or`0 ~' k e94T)D"ܵwd}9F< 5D¥D%\"xԓ<-~0&1kDM+`!em¤#Nh‡A,%0́sVZyM@>r| !Ӳ0Eo\f 7xȃ}Q #nc0l] mB&tT}(AK`^rFpQ8!I, !AQ)4k(|E툓&uB/&YXQݺA=z?SaLtnarlBq HDoG[`BTB;mZeIq.FqC$BVqQq=숸 'f Ά%I?]o ͈2Ew['y8ÌEAAB!&H =ĥ -rr$%h~*MQ!0A{JU )"RV=X^Bu W2O?,2:p.IZ*(¯Zc~- +Z *V"L]MOPu^B1qi "(FuH Q,bvL1(]z%29R}⹪G?آwq+W?7߆%6)V(qEFYeD`1sN\k/H\Gh M:e! Ѥ^dB\E#Q ;Lyy83B$AO$]ԣs*BW9GiJ }0QF?Tn 17r񧶺]^]7gyFյUuuuWTWԔodBal4448 =73 聲2cBS|!wApPZd1++TV>(R]9PB7HB~L%h's^Bn * AYj32iIch:cbY ;5eǶ3 Sa%3;뛻guF:;ۻgu57KlD追 ?#jq몪+* I=j\eeF)յS',: JԈTm7lJL20HX]estcbƐy9L{`Jp8-Ҋ3-=˘v;3t#"LEMtbqT9[#wk5&@8껉y&I7 H;XnܧB&AZXjSJ\ zWKK2_̺)С[[[:guΔ2/ig1W:HS}744F"3[m79Z;YZ}̀}͵rLpLaf@Ln;a;@@)1VR\`U,"FS D&AI1r rswCο` }C2|]MMAO٨NJ(5P<GoTՈJMe]ğtǛ.~W_S{0L7MY)F91LBNNO1y1:p$28L5D/;;UÐ=a(cg2FjA}c686xBOQ3h< H q=߃1g&?KédQ]0++*jjk mEUv:i-78k_[dZxzv3^\,~yϼ37;.=SdԜE,o܌VlѶ٦+_{p{g߷W]!u]en{sYeg>';_oլ7Y[^icۛn9sio8;/=v=U|9W}P_g˩:mӷ;_w-wO=m&M}:M?3v^1}c&u5{nzg=8oN YSnهxś7ߺHUϝoX;랸XݗYܵvG?q}'ިa|Nҭ|z:gU{qa\>e{yCk_68~B׏,?ҡU߾0xWO7e߲c}%3N|a3^꒗7ן}[_~ۡkK~juto_s#,x,rEoK~3_^3~&7uӏq}lMv&⠥'?Oo+b2V>:`?[<;W]若;?#{h8G޽uEuQ?Vu<{>Vx{No:VhI;dЉOtԳ/񒩏/([K,_~᧋˷?7.9o|M];/p3w-y?oe䬧o86\G>?.x[x&K瀶61YҷC0}}q'OsoʹK-}wm>xJu Y0>nj_0˭;tMfuG5wLJ?8mWhTGOm:>1)mk.7ۭk/ӻMH]ycsi_TA75֜`g+۝O~~+6_u?/g|Ox=L_zK~ׯ9s:."ylvݙ=*6wƵ>V,崛7{ꪋݺ幁zyy o_c We[_YN+>V:_>pV,{}%y?yQ흝f]9iJꕹv9?ԛoz~lߒ+CˮMPsߕO[;쾶G>oO~¿]o>j~wckzO}1q]qm/Z׶~Hd't͡CY㦥ӿ^pݻDwYU>q遹'Udο:j喏UN9攇:7~U^7?vOkU\/'~މl/ދx7d\O{x3v^+'}<|}{bS^#>xunjO?U1+ G.y%5{_:yo|m9vyZ~χo4J~?\\%gVpܞxFv . %~/7O^[3ه[c ?SwJK^|mF5/Q-Etݮmᯖys\h,x~q=˟嚵_oVQs'\2#eo붻g1S? 6yl͍xa/ߴb;=ۢ~g&e[Oޞ̭㋦jgYҭ]ր}/3{{{sՎUǬyǦ(9|pWyIɳ>zZfqp0#i_oyߥ/w_?;;7JO=ztDtc^;·[v.MϘwFjNsdSwZ^{;5g{O2~wE/-z[rA{Ι߻m{Νu=~zI|爕/.׆{nt^SG]}̫7>u;?'g׺iGz鶒fX1 nݥh7^j{K~~oG_DZ㵏=?{ympnq;o/~x3]QG/pmwžMj_v袢&\56Y{A'~߻慎_6#E+/}3> V/qsμ=_ٸ[)C6%pcC=5Gf5D^{߯?rgډN?m>wߪon}Y?iw4W7U-O*| 'Z}]֗ο+ok%)_svuߊn=˧Κnz訅=+|¡.ŕ{p?y]w{=g\pڹwݝ<-'_vf-zӻV⋿tU[F.xon=k{^x[sޱ5M*uf5ߺ&ϵo]e̾?wwv.8[CGMO|s;<5KuuH=7cM.SC__}mͮ])ػ&D/DQAt$,9H\" QTB$PxWzR***Xw=𨢭jjPo?fyfvv<Ϫqcǥn}5QU]g:'=8Q;^2,{-Ǣ1}Ҏq:ԺN[>vɬ5}# ˂e~|ex1/s+VO:oۘzz|Ճ$/jyIG <]xh>wn*W{_$(E_.O>%Cl J\GW3#")XʤK?^BO8`i Y_Q_OU/XCbw/?@# ET04li ifE π2?O9 [T/ A&B~0` C(y"_W*c,Q@Ph4Kz!]R v5.V@n^S8|G Otg>0[Jޥ鄔V ]4\U<8gQTdTy^釢j>+LbSSRA, Si9 <ܩIj*@jcf %p4G'j)Ox~9\XPr Aėʇt͗.kjOiJOx:؅)' 4؀ 3Lq9)l"TFE%(?(SHMbZ9U1oNd0VN DpaL|4G* hL` ٦% 8IkpQp@J1*3< rH~ҬrVԬv%Z*1刧0'44%V>Ki`+h;d.Q/]\F6LQ)7jJ;%)N,)Q 3pٴl>j&Qn,y>BDVߊeR{90ۀ?W1r70S6bUYJ$[tU?|_uYB-Xw֧z%XI&{UV*do5:02A͵S ӍH֏iyyKV^}-R9AIH9kkD:i, i6Ήs;DXoPz}i{N}{vȖüj_3;O]2u4] ,)H|hس.ve֦oqPOw{yP%JeR0JqP} ="9 Efch0ȡ)B`0f4x߂4L3|ʀ&ʩ"> 'v'!bIp79Pe2GpqǥGЄY 9'1 |',` +u`G_6T5R=7IZ{&BZe$|AcJ-%4%1Bh/o~0pk<3!ĝ㣵dT^P=b`]Ԩ#)h2Esa&@Q@76 l%N넢&m3`@nļњƶ`g4C0lf2`Zt#WvJ1LLdxĨArCaWeG} Leя|s@gN$ 9Ӓ3οd &O2EBH=*9xTQwD.oy{ ) =e=%hc!4U]{m>j X41:CB>W(%=1#{Da5(.bYTV[{}*\\z%99%t1>}vml#,#ȰHddniVQvI N݌(^T mZZO2՝Sv3le9X؄NW^zkSmc; GDgmBW71"kKK̓$)f 6>dٍ_z)}荛Ξj:V_!(x{'M~K}q|ҬC5'"0M貓u +|?c^˷;vk7T {]{ [wr|-Ueha]oݮ_]ωu?|ؚ{dzsz􎘶 /j騰pH; ˏ޹艽 i%>ZzVGvOG^]cl]NKہ(d )r QE[27v"?xୢ?ڂOUHmpHdoI]n,aVY%H#GLZuȑ])n߁cW툚%>ct]MgocXvDșܨnQç9NTdׯ6^cD}0X]~q܈c XOa⵻Qإâ|ϲifZ(>jҬ_Յ'n?څs .c8UlJ+_>λt,e{5/>ㄔ\{_ˆ_5as_8oԴif_~0V ͜-UEv6׏/*+ȯC?])Wkv+=F]')z~z2+2f̊I܆mW`h;b/]u?>z+[婛Z䣥!?XlYUxpjcK:ͳ)ZZERzvnW5;EZ~oa؎N' /ZfY {Vhv5P}o)]h!3n? }f˽lw.8D鰍#7;ͅ\X?:{w]R3[n1nFeWV"̾[΀=mkC~薺{orֽ6'LYC%=ٱ+kؗkfE[V[h n*7%pV~~N!O]2FZZ_'4w|6]l|;EUqם=J 7>te~UL[-[?>}ڮޱ#gq#O?|rc+.eb-Fƹƫ&i=q?UhxWSOvǮ&lv?o/w{0j VqDƖ݆UcS r۽'f^~wʔo#_SGn{ZOe)_,v><5(s'-p`U|ZySQvfbdrlqtw]rcҠ}돝 g*td嗶M[_y:ikm#bSe;.7q\9Cu(*ø/‰=u/i<ڙSi4.+p#Y4|Љe u`pOY]>|ڻX?~ѠP%A'.m'M]4ȑjn/y©ݤqdʆNqKzIdlY%Ұrk6n|F-#pׇ0u9ӭ|T -տoX$JkBO ~]}^kV߄c""KQW4)l&=ڨaj̔}2%\b \&&EB' #b뮜A8 h4p{rƏgo0b&nxfbrkV6%$+RdڶLJ:O0j=!9AA0L1ý0y#'M| ]2S[i~<ï73[!!~$Q&cI1TO)*#0z{h 8'\fs4jHt*#%H00&O1zeg/ι"cP¨q1F0py!Xxk& '\)HQaЧ;abE92RDD ؔU)!# u,p !XJӀоX`6MFh)J `|6hK:EblMk@XakfbJA-GIK8<\U>j#Mn4gᏓ1܁0Q6 GPxokq x]x(vhXPf`LeB!O|.ok-ڊa peLjyD9?lDO\ Mi?6%1,0P%$9J,^ ַĀƗGk@çx!~r D+!w3`R])F.@0`BGS)9+Y|s2r uu2e瑩I?'|x@\Ǡ7$7KoJ`hc6x4YY0 b84לg}1PP9rДe* ^r@7&:VI }p\?<:$B,I#څ̛&Q hAq UzB! Uzw zU U,PQ"Mi"*HGX{N~~53噧3sCmb}paBmƶE-³g9П#ۺ-Pkb[F(4gm*758 lBu?gT[oyY3~tp,=]]7Yr5bb% _:C67sW /ya{5n:!ZNwoF?W 8rT }{OOV<]y]np\F`7#w[|zl;8 #}H֐s@o  7;-aGHm&] AzQӖ8--ȿ Q nԜЀ? k4C?7r{m%a2l)p%x;;?׀bK*[Ztai !`@PAosMd?"hsv@߻ڲi [?'0$!)<UL?0)|ҿ=O͐mlm [~(okb R[?Mp?3mL&ôɿ_{s p JP!~ %n܄6óMz7:Wڇ@8~hS-&J"?#mB=IBDs3['OɌ[znI-W6iKX0;KmBB]-ABB344Z H& (w5j8ӥoME(V|8 P?|l?I\xi%T98*%JkI" 7j9VO%HEf J$6*9jۼM ` 4#?5؃0Fs6mc PgBY_ -T^tbp@|p@|`_ TG@`w2@Hyɛ?)%D7a9͂;@< Ar$Q|Ƕ[0NI'}@50;sŁ\̟ÃR$x*߿r@ \q>!X%Zڰm.LI<# oC~[8~mA ܌[ŀ؀GI`9 j) lI[d?hscs>]9QOysfz鑌t oCiҘX%,{Z~NFs8+TF>=ޕ66##׾{ATqV&$@0{k\dq t MOgb Ԟ])Le/*/z͗\]ݳ?Z#-ĝsh3՝pvhP>x!->~&Wض6uo9,hS/P!&gO!?\=(%ZN.ҁ}&o{]Kĺ "v#% ԑC:g/_ʲ)x@I۵F9W&A"+$iͬAi.u]e(ܘJ74*ut]hu ij;Oy_D^kH>ğD`. 32Җ@zlpWRc1 O(QX ֤MaWY?зCͪ.x2u0}9BGC!?V}9 3}1lcټx sesw o*ǪoY(2ΊH= aoֿ^fRyU~4<+B&P($[͎E/r plTWY'bVxՑ`LA6K- p*][<6*g1w|[كc(PYfƲ?BmYH5Sk & ?sc?P/aaa[ פUy4F#Ozq!;-4!Eć{) 7i'ʒ@op]y-y#o>>$\`İ.Mz7JëhYv1Q'.N*|_`7NTVD:/,?ƁLq7wW/XFLSdW}Fzg8KĊ1E%mQ)е]>^!j]:P_Y®ƹ5Z%1bQ*1̮=/YgGtq"#FcD_~y/|q'L֢_ vzQAJF8Ց wR Rb=' }g)R_?q#o?p6|t|%d{e$zx`Y:ANUf: :HsI;t+Ռ,Nn;H|QwTn-C35\xeJnrŻ|QwkQ[aI |[/qpGѲ _>r=e(U_ /K{XsfI'~7eJII>έ341)G$N+!i/}?)ea7/<j95AF܈+иY LX"n 'J) ⋤}vo8jRpZDƭDv%[ WcTJ, b[:Z@̈́ɛRʎ*6J3ZOfeQƏHݧ7{ӛ^ L[2U'e[fP -9M,"dYz }mqW=}3&@1#FQ1I]*^iGU\dvSCTR> >{ bEe0ƮZ5hb;]ʶ$!{"W]PFmi !4:q| >F}؁ 5eg|]64RZ?/>T+~:io"<ͰqMڰ}jg!稡wO >I-`ز{0,CJT_HνTrE=Kr FzUije~W{[x n rnݦ<20ӊ6Kҧa]k? Z,FB,shtHY6|;f]I~ߒzRThCH`څ~l]4#-jHzs&8߅: ϛ߷w7}Adog)/{{}}wٶzF xjRXߞ<1UghXyյ27)}~5XK[ɌyDջQܭ'x %UAH>p-T*QUJv!1rb"2(la9Y6` }ڔsռK.oV];Z_?|i$xxD,C DB6.'zH)AJFMHpi(Iueu+ؕ~@ٚXk^` }ieN(g+}CrXK|Vjn&4å*kՇ;xoeog;;]+Nw4OM IS!UzqNdLe')?8P2 7_yӢSɷǓt'v',2m&I OKgJ&F!]Xb치}\ s* 否ɗ~Zd ]aZ6]хsJ&N+y:E=$%u}ڥYPr%iΏJJ%l IÖYznu c)8){bF޶8SUU<\.]nI8[Y5JBu:ETiY\H\je4{)nSᖦkz|2s{KGHq{tOeA /&8QD'7Ҕi|Ǵh\9;Up堀>Bcn9N"tڜ|رE$EddzqN挚Tvvk)G{ΆS͌3R'NI~Xm>by6S=IxF?Jvg貋n߲+I[(2ޥO⥤fawIJ5au6!q$Ygd^K.GQk3[_&;{B77D7 D$f{׽1M\U^2zAKJ47Kp;]9lድ 79{ \uQX~Oe.bFNt2㺏ӊSo6/1xTO޷{J9@[b|Za?ӥ.bя 4^[lʜBg8sjA(C(%'⮞z~l{didj _NaIJe' /SyErٓ0L_65EVUO^*Cae@N'px*+Lk2I 3yWi U؟epFpF⹂I_k=:-ゼ['6ʌ)@b`5=k"Ԍf/7 Jg#k,==r.um=9C2,ڃ nR}*eR@g>TKT ePM΁b]e?H(wH-R,aY3T%On3l{nuP5SȂ,-X{ߟgNyduVU< 12֫9]r  ^;xe?N QwK_,{.t&%VF?8'b. F e?xwb,xol&K%Wr9͛[?ĚLUBѧ> 5'Ωd*h^+x$ט.859=ٔ]z+f 6=)4> d#a5-L^ VfjIuT罛?,'k$m`,>s.q$ׄ͠N';ilӺ$, _h\cDWq2H bY(tk/+f%goӡ]|bXfѻ=009v9lȬ<}~Qc>H*UXȗ՝ *UG?F)S?򨨙$6۷$z/%).y]BxEv(b %'iIF|#w&ڜʆ,7#oع.Ĕ`ɬՕ/hxUdk#/\4e Cwq~F/jqU/;L5w3F}ގm'aOLT^".G)8΋Tρ Gדe͎ğB=>[* }]th ٢ͅ7 /ޏ'Y]5=mt:Prě=HӍ$B'PXC*R[qGuӈJL_B$~Mm9*^hQ#YĎ}ZV^1O揜\ 7IxRy :K$jOvk̳߉W N. `Lo?X}or՛>DO8eL[~"GF -(3vsxY*.=7XPsx<V=R*ag_fFߩ12QK9{lekIUgC%\5PVjU}xYK⢟vo=ַVdZڮXchi,J%j~Nб-xTGTK4T˸zxO6pd n4z/=cޟ[ϒ=ϝ>렼rعŰ}|9y7۴$2zP%7?}{=!~Wp23Pt뎭F c3TM=/4<{:. ]jг8TEF<\AʓOIdۥ?P@3c}IQ)28sVsRY(}0e/]x΅1" 0猇=d7 <.qv8ʇ1ڶ4^znO vDYn.Y2yᚐs*T ^_qcq["ΝdffTh+Dlm OC1 uˌ9D2 W׈ɣ{e^k((2^V[2#'s=ܛ}qj;(:UL9JI\.'wuMw"JǷ6T}',Ɇw_+Zˋ7[_gKߜ\6+S3@a}տok:#5t0}5R=YORɐ !Y:L#k L\~Tk j瘝_gߖG3@vl䳍DBljNJPUB~R=,7g 1j"]ĩݜa mZ5{G^vc?H^Xlꉆ{#XHNjڡ[؉,Q#:v7LzJn >{&:dךž!'&<ʕx>q4ט'ѻ)Z@FJ;4 zLo(D^AM1*@z{qkVw(&>~&7!II^caW,ǯEu/QgVlCF%<0s>Rr1TkaҢq_GLrw0D-W3gnPߴu-󠙀=ɲ'r ?'2r]-~E śy!zwD\L>@u4M(Nex+;9#pXIS5:$IYJ$SEPSTPWw]x*tCyA b] 0(;q>xm,}񏃻'Z̏U_}IHp|=~lxƋPcC=yo$/IL ޽iLk<W@˓,%Kʾ~7y0t=wӥV(5OZ&UCYy?Sndv"="gRd T{nQOgw93фL/S#R|Tc`Syw㻵jT:Ŭ!P'/pbJȿbpC:_ z<ѦCX~ȖK*Y{sNSR:O]=<5[IY.qɪݓb;z=R(?bR%ɵn!ֺxmzuu8U:&k4/D_yq i>d}<YBgSy mԸEsۿyޓNk9s7A5MJVa̽b{Yh8%yM8Bmj#fp_ebskȱW,o|ˑsI'k}I<|nUilA7].TIYtn̷:b/,ӡn:4 O!!?V7 BTg esst|A[^Xv3]8F %37J ]es1#Ń:( Zg"hIMd̊wf^0B &k+C 4@RZ*WSjtZsz1-Bnw!竩aB4&h'D'_YvdfY—I?%IǞO3uw8{=L$b]E/kR+{QIWUaʕw&sPcЏv1*9=P 8wUS,OlǷh:H^5gB%܄2$r+=G܄pUS?`x)nx H%5T*@pz=+>Fx !fS$ -|^cSL*G:K2ދ9Q(12jJ;1 [<)#V̶~crT?St:q: e@bToזZuz(J' ; ULJG6Hy[K8xޢc?_1H6 z 屸>֗\k [Zh[ntȾPhc d ]D}cZ+4! xnŌޡ>MbPXHħSt\n,\' 7 pKZF]2{Bv=1hI|ٕ\U#t".bP@@yc_"HBcZ".Lw>R er9D{ =l3N"OZ3|au9D@Sb!6˸n m{3ns?g$A1Dsϓ9% K=.#T83,xR|IYNXRGLt&JVA{J4_2V([iffGt&+ͷq!!8[6Na]D\ZPLI$8~PCť5`K/##jT|"Uo'= vtMj%ԧr0 fə5Cho4Qһ}+$[wӠR=*Cz$$Y}v $1ŌUe9za7 ?V(/HE{2ժy34W*oLECf,>/ԨN~^bۭ8e˷kRo*n=ž8R"I*YcL%H=ݛB!X-@fݰ,'+ϳ^/l,`v1R}chҦwdV{n^q(:9QwCõ,2e!(y>N#dLAZ$u!EGՉ{EjF5N/H|x|^` B";Ѕ\I<*U~% |?%Kf;=z@1rm#)~`~fyjZ)5|uLa* ܴ^De?L'ҐE8zCʤ&csIOI<(gUXRC!zI]*V/HŊ r_n'U\ 'j#C^G-_Y"Z t4X/ZjC1UqėpjXμfwFAg13i u`'J߉9VIyl_{x\~.Eѥ"COqJ O3Bj:Z~lu ,\?`´aT\oMZН "`R}}DKOZwxj\ݗw;14؏D'7y[C FFOt&ʮ"/4 s51&CiBqf^RxC$jD,jߺd¯ qb7#\J'-_T.pTK:W{#OU|I2vL]{yW!&!W_8(&cѩ#y:t>0`rWAkM Ǚd\ C6 ܻE#.M>a -U#(?* !s}onqNŎjh'M`ɦ̖RKVS?吥| g[≜j1H(r{ 4*On1]^d;򡋡~_bVA8=/wrx7>ف.zs/E5x'gV*Kqyg!/F1Ht&9v1ksn })v{w>K>M5@YZ>ߘA{}0P~]}zuS>B)/Ln4g)ɽ5Nj{xB]\b8q@{vUMa(UyRzG%~֮0a srą0-˧yL*nkoȤ~ LSR + =ݔAtjb;1{B1'XL e eYYږ-v&NT$. ?CZn:[o]b:;+S/ nBQaP iO)jr Ϙ ġ*@qu8QzQ=k\uw-Q#1{i|Iye_zҁ>xCWP\uxϰLJD` =h`r5Ǜ'StQD_覬ӄ$EcN}Bܽ[.CgO |Z`le2 C\|cxIUtCp3O~;g/i8MnnM?ZR_D7&y}RU0>S]:B'iQezM[E2\j"b <%xm{.Rs̛|ήN>;rHHR:b0@ƒ\xׇXoLN}y=bEv8ٷɪ[% %-g$Χ~3$`tI)S*QP~J żDoug\{-GmtG*!-D0қxPþ摑^+|)F%'Qɮ> vP{%{RZ=ax''nj;:VLzMe] GB՝_Z )>x'?O" ]WF ~Βj2*xP6CcKK\<| zhj Sk71$E6RЩ3T 2eɬԱm9\>AeA2U`7Odj}2t̰O{Nʚ"S-ġ+p-Fn \MPyjɞ'rkcuL&r؛wɓslytd/)<8ᬐ%IX~Tg|QA&w;UQFoRߎ:=vA5E^Ʋb~GM"̖|5tO@9Zu=IPgU|!3oZ9]?JZJ<_\ NEIš(iYqqY85Lj>wN[Q(%K9sOh~+Р`pp{`g/6vuSKk$Lvح 4aͧ͆P_"$O_$ m}`|bCDŽlG6?/7vA_xJ{ 0D?\oGTf~̿x_G71j}hX;ؘ{ aAP+#hqYq4;(;5Pk>K+(m`#IU T ߰d,۲KGMX/ O;1iS1~H xW!rV4)G*t:Hp aU[٫l&*|-iRd)_=$+ ƕokGDVyj[P JK(U$tX`ʢ!|M Y8'输49}QUsN jUn)*z(5gMa2"B5tma%+Fa*B|kVtW}j.{"uo-.O~N>yI*ɽ..Ή7L*{U?y=َ]ۍQdzMTo 1S"/.7 w ;!rVdepR)\Goc|-zHj?4?tz7#N3(I<;1 @a,x7,4 deCNwM&%bEF'yȸ2 5rt4P=QQhg7V}g$Gi1 dx\3w`iR_aL(\4H/ ׍әNLeFlrږd5̌bkS .!1qK."ķƼuvǣ7:Gk:G^@P13C( |lB@& ?ǵ//+Ze 0{)-21BBZZG^?>ڳeC9'"(>}[N.gRt'̝ш1]H89F+R˓&ahNt.~Wq OG_%i:(̆򡁹-yGυ_6atcX"oן?Ţi5Q4vfjn1LpQŽsK'n<}"ƈ  8{ܫT0HGըpީ^hR+Ŀb XX/ml>PNV {*q)r(IJŰD1JF0JKb¨!;pI^ƸC)9D'Y1W>F.p'5Y\".2a5IYB0f`;I+K;&KE;ZjbE.| |UOs٩>V{މ53&?Oͳ'vao>1z^cUl$YmQaav{2ZռFԬi]J˯VJ |v>Lq M%9#8C!ad$QZ {2I|.yYɽw:,͗cٯqy\*XW~eCjѺW&pҌYsE/_r- .xU>Sk=Eg;jNSwx5}J-zkpq Kwk3'$ށݿ^C?GR .tE {FjA.+]Е7ED&%&\64AZv4⪓u\4)( %}z:'.Tj'(N-.!IiqG?a|M ȶw|_uΏR(?.t4e:&rbwֵ-@J@=XT~?hc>fpgNȔEMKحW\s^Ћ2&R6#Ldq4\u_+mt?JWxy&WaJziwHbZKWq~!4qmzO-ϊk=|>8"!ScR%EFrz~>6NB ltUO:x9[}HpĽ|DUs\vDኾ̳OgWFqU-^}wkٕ{_ Xseb˟}jQU&ۣZO5ExwD\c{Nr(ó*g['|bS`l[ۦl/gv~u1S 87-w´(O3s_ZXֿN!*2"(NX[άI8%Wd/0'~o}c<[RTk?rcKqnds>t7\ھص|AdCSqٚ7rQ9">99xjbvL BF mVY>amJ|^YLozzdݰ&[RnV4>,^?W%pzt$bd/ _ &n K2֐\yU:&^=6(vA'Ck<t&S;~Jhw vː\q#飙_XDVWbZ4̽a{k&W(t&[l=Wu ?ʲO/E}DPȏG}MQ4~}ilcyA gS .Z ޕPg\ް9T[s5 ÃoN9Ud 62Ү&uH2Pu]nωsM{.ʇy6\ϔ#p8[2G,ܫldJ-3Up5)J.]Ki2Fݵ:yddD6)Dh˗i\w]:{ٚa۷4_}hx#U\Vx{rvtk"\՟Ti8Q4[SAQT2%S,՝%'>A>w+mJET޽~o{K;-h+DQZ)9FzMU:w"8PS601ꫛRn{{cN_ il>=IR;!JK&+xԽ ^Qo0 rd:e<@\,'nLGYB 4v&k{<=+U=ڃEbq3ŭN~eAvX3$(FIb[Ϥ.Sѡ)*ƛ\t E^5>jϦ N~o|gU-JU©])G gx)35}DC pޞ'<^|`ؾtk*4^WEПP%[*}$v(J^9M>;=5՚@ FJ5XjPw'+I(_) Mըjoq;9[7IC?t%L-pRȇzҴR)ŗ7P*y*ގJ-YY{;h乧Qoл,-:ZdnoO?Yyt|/RhޓEG{[@Mo:}Yrk5GwɌ衝UhA>*R,`h&v/_:堉щ2#tN/)yv/vTDw;hC2o'sεqUz$=ei?=@ehrRe֕k_Uzw٩egeѩrDi@ rn⻈֬E2ɿIw"2/@ԭ9VF3uӕ;߆o?XQj>{;bU!%JlC-R8q1vy}IYssʁHg-t_Ŏ582,sjmx;x]y1ܡ#WZ<FNK6D%Bܖ=`vlDoo-^OU~@Q*;$|6ړiY(K2^ȕ8JB~?BR`߅ oֱ3y8߿j\ȕX5OT爜\$GkCh=I3wQ&RQ\9*kkNN;Zqi\[2JW05T؂AAA!!! ÅEDD( `0RRR222WPPPTTTRRRVVVQQQUUUSSSWW700044422266611155533377wppptt>>!!!111)) DJJJjjjZZÇӏ9p8+++;;;'''777//ѣǎ;~x~~' N4 >zhhhO>>11( ~x˗^_XXx7o߾}DVVV޽{>|ӧOkkk?ׯ_}w@Qvר7_\W^@A«7=h"š&6]iRS%a#* `ZeHz[;){"nAHEۮh 1($`vCXFC(Y KXGE%&LzU@UO / Qh( G`r.03Y; D %BXTYGHH/'84ﻡab`qPsşXL8}@Zfv,X -? r!6 " C=wН|!>TH\a,!8tx4,\Q\Rp<@[`e]~:'.yxA &( àa. ;/A!&VB( ,!{pX9tO@ PxB鹦8w@`@-g!A-B]אh2 & H$C$ r H,!+ a<ܘW:9fP,?&&:.' P@ BH1o/@#0$. c$(?#-0qT< >aj(Y?,h ad_φR`l9Aap 5h$CDAblxJ@ #h/$Kx pRPQU)H ,@Ap }IP Kh =#GCC11`X9Bq,(4_p > ap:,և@7 `BZd :`{* H-9OyM9O[,@` ~Y0@ hC =\ACीx10;/x9pDDE }\,"ۆ*(/ U! ⶡ~@54@‚ƊȁĂcܠ |@4j۽" g9,Oxo< h(8 /A:&؀d%!Gb,`||p9{!9,c (#܉b$[-ފ|meUhg'B`t|?1,XK[ .( eȁB>Li[|;آ/ 9_^7d@Y,) ,@g0FhDk &[h x@`܁2j UnԻa@Z10 C Lh !|+RFH(8@1@|H`s9(\<03R@p|C mh?%6U@[ 5""3^B11OAAsVpQrA YB|"xGœ 6U"gAA*Y |X !|g58 sA!nS$qMEݞ꽥^mq+ J-KhS=㹩w\$?%JcT){Hq) A!p*2). ح"g +(Eb0_$ Z%*0Lr"V x #$DuD>֡ Bqbf8(``|zὩ-  :͒XV%c:42QP610më`F+O@Ap1Ofs! d3mYm_ox |aC-o G dkA+lph`Ϳr,d&: LhAK P@8 O]{۠0 Ā+>u[ )%ށQ`?8: X DP φ/+W|n+:$oHhLL|#:, .aP`v't@@);ay'G]F(ƫ*(h  w{9|K M>~prm''gxN'}HF&!$!g9oĦd䶘(>c"m`߲hln(Il@AhLb'#A3rS>X b]՘AB11MNALR|9papYg08Fg <klC_Z4T6g A >sXr 3 [!BrV2Z`T ʵg:#M?H^g`!f5TPE#,Bf(|>A7ёmР(G`8%W,`׆.I@)Sޠ s*La#T!p%@Udݑ*@yJqlGlζ/0F"n`ؖ#FJ`P%A6\p2|T!TnT D`OVKBEoӠ_U(#ѥRQ >"# ,m,p @ O@QW^ͲQt!rE3-~3 %t(`n(йa"a0 x,(8ǂ BI!H#4\05_, u+g0Pp l@?A787AUz ذh(Cq!8+Ў;@>=CCp` QB!x%Ѓ<>QBCBk *ϧ \1ǸfCcƒ!>lNe^!|SS!珩ND^ls: #Cڿ8'%\T/QzpW0;jlH*!`ϕø3a9+G3\=dt2Kv/_=ƃ B r^;?643~/.hi$6"zP2b(JFJbпPpAAq0O h?C7}Bay0~:^E; (@70&"߰( (a:$wԗ`؎Q #\+\g$`?"Ah%4h@19nP/Y Jk6SQSWpB-xBPB;H`C!R$PQ(Z~ !Lh;UBFWpb0cIBmൽ&a6ܽU ?Ƀoу#ᣚ'cX-1 j0G: Pww`P`(fl GnW?RCRX hTi5HOW1l~d00J*(@Q;xUoȠvdA1@oC$-bCU_[xqu! A޸HWm oX%ut Wv^ϮH  вs]VᵂoW>Bc (j?8#G`5qa 4/81imVp(Y(-aqvIARah[z6 G\?bW0\qnX`䍇h[APUUaЀ0Z OU[ȶTi|KH϶D l+! wrq|8}[tPr$98?mi<}q ؁&dg[gZC?!Xm6s;Dq.5ܟ6j]"n :zp6_@wEӍC7) p'9tG ;_XTwߖ>>$&ۍs?~@ ϿEW09vAA$ؒt{0؟ߴlPrb0H>B" |@ԏ엦b - Ahw;m?ޯu҃ψjOl"*K20ѻn& mIYt[Nv~[y ;6 lGv ) 7Cal! L3 nFʄN(.Gx  [^/ 2!9! Y b_SÆ l>m q݉OoƝMh Yg@QSI;R'bؿRdž`U8/Nm`8sB>~zDiTJsg؝*.K| W|Ѓ"X`Im͛v值폛]G <( Ns>Ѕl7<%v!:8 Eۭ|n4*hKUe9жg Ixl.Xm}Fh`ةxa#Z@g6AEP`[7@P>"b#)4Gl,!1UoH ga-Ы~7l.Jh3}cu Lí.By]eB`T2@ČFemeVZ<"7B~&.||=f=G HVp;5}'MMlG/7ŷUmY!vS x]>'ۑ~zW*mDlh|B#߿~c7 Ѩ__#bk KD#KК}.lQ7l|kJXC^~*#E>pGfvپsq[W_Mr/(/%]qm[uoIO\) A uu]0;aa ^sV-70bHXgBBGRٲ󛊈ޭKMJI@M-ML,kiq !e$B\\fL].uaW0K; e_f߿=a?o"hz>#_I)Y?~{=Kç=mivÏt? 6@_^ܽ[ wuxv^w>RQpQ=wlff^WUճK66k2LlИKtt.a̱/6 Xww'$,浵m A>?MkneeKDwec?o\9쭬G_DO8vX[c 0/*ܫ8\ڥ9~p, 謴t@QQ}7meݻׯw3ڦU>z4yb+ KDfsBKԲݡVfNW`noXڤqr;]?{>ֽN46 #fN;/%H"*ӂIwaq VעCwft<[;T‹%y 3>|=)m$0Y z`2a`Q\hb@a{Ă lXFQo=(Z,P J,B%F׍H;S]YDLއӈ㒍 eӊP |tU븸 xA=W DٛrrŰ\Cg:W{y˪kw`|wS;Sx9^]sב6 ~so M{S}:vm̒m!( r٭La8BK f|2 <I~]GSL&O11_$RX Ph^ROr[зS)ߜeB{ã? iHYj}˭PY5;enĽsa _]V (,j΢ ~|z,#AaRGwÞ,o3^os(A5<݅CIOD_bZ?/5*Z.:.r 4hoˣo V&(al;g6`a9jOd ET52培iѼ)' E]YFk4Oz2# c^75˗%Ur"&oÞ6]ȽLQKlً}̉{'T5'~(8LNcܶr$@[V:#{ouWZ~/)O&ȮJ'[Q![z#p*+3)_:lhvDҪ],+Kfs[ev)m,ڝ ׎/^< AFUս{#9?YFB=՞ovzxȫZ?x/> u!(LRp~!3Bkl)Uλ^EȗD=Өa3h>F!/_Ids+ܛy|fӛ.2Hk/T]}+LHMFr`o %H$UowZSJ5|O4dqBm Q# GHd Ԧ1TՂQ?Wv馸߅< 6ds̑4/%8ǣ5zޟi74#Qr\}9Z&u]]~ޕ)$|P|NL4E5Y~GG;_ Dj 7:]guU`A,}w;[IW߇Hp 3$Pr`Sq%vQP6B}{QJ@VLr Sw`Kw=1Hү;H1I±[#^9ʨwXPǍ nta>ϞUsIM.8h:Rp$CssF3}jnA weİ[D|3Y/o3}3_5)ܝIz,8+gٟ~ ;J[1[`]Vՠy0{^u3%Y]nqUZeEޓoB.Іz ?, GNlI̸5҅ͽ!4Ig@aqR]_t{&ܵl!v/box55vZ5IDv6>f2=W>iiF>B(~r:i}iϳ 8GPF_m$7/{hiԤM2D~9su,Ň=~^S/;U{'yDq?1Ӫ`[~f9-#S {GlR'EEXK"7s.DPQ MfO<=ҡ-䵟Q| ʊ`\YlݓޞA5cJ( t۰s{ w^{:9TJb`[<$;pUhQdZ6֜S&a J^Zߨ;Dp.?Go\/ÿGw[yc'hF*n-;jdQ .q28^yR$~yN(b]Mpm?xIHKJKKR#rƩ?"H)vSM??? @"F.sP_Oݓc5ի=N wUzUb8k5bԛ@&^2wO^]"rY]$$TK]%*F\ v0:|=zL)؟AnYtר@4UGgdI -J#錻aܦ0ŽJkUj0܈?%8aaN U.XQ5C8MfAAQasxo;]ZBu(=pQ3-OzYz̅ ^`䷵$}̋z:wc[\/;_ }-m# _OOXflKe+;շY2 }l|w{?7zᥡXtǏKJ^~~OV*ޯwFw_Uҍ_',N>zGe\!j!Jk 껷8j}D,c`ZT3{ g!E*6dt? hRR}ۛ;#dxF<}ЙϺ,̂/|z%+}Q,hjCl!3n1\)*pz#1<|Rюk:+ Ȩww os+)@;wڱcC8OnLcum3ye<Ƀ?5uBgmȪ/_R^{ڨLꝨR|,^ z/opy;PFԐ|ef :2AOU.|X.7*DY%OjZaI1UOk0h!ZG49,j>}VkZ U~h%wl"3LLLJ ]IN޷Gwevsš QXV,;ts 3vH5>Z>qU t/(i-Dd_~t5}W< ?˪+i枽}⣸Gw\;$.8h1ǂ F KWb~*9.1Ǭυɜ9}WT-Z[giب [V -#9s+u5j[)PǦuvo b#uO c'.}>!t3"^_ג&c-̥74:G+oxb]k9 .c#qb7jx"ɮՉ]N]n$qf`dr,T|AUhf/[ BZџ@նzl{5AGAbh3#"$6f/c഼Ԥ!ߌ@Kc0r|VT =jt>/W9ȓFY\_;=i1^RȹQϺLe)1`37} xe4`xЇoVzE]Z]*HJjFTaw( ˞ϊU8euOo-&t]򰐬YFх .Z>xTU-ԲK{ ڹ]?|>ZyhhO)Y7j>T;V;N_K㌉Ј!a>Բiùs!?~0w"QX{F‰^~*չOCqkA3fW.8mmDoYul^G}oQ)T1cM"A:ats%'O XoL*VALsYeBk&Io2Q1LI a, aUƸNB9u8sV׮fs];Su]Wo|dL-۫duI38||ʚ&biF{V`8X{zu0W ؏Zࡊ>z Yg!} luTR`%aܺIA3-hH粋n5Յ{+D?VZgkʲj=}LRLYO/ &:o#l>U9Mb̸SNyОoc67ÈzU T-7fy!LJN^uJ;jj U$4?Xч]0|WQp |~!A!>G56nZ OJ#-.ǯCI;'.1aO;D M8 %%FAQ26Sze9X@Q;~_Sy__} Q߀~3 >d+ ~:ddg 榦#GP Ǐ$lsz:#DmNr66777=єqsH0MAH G!%zcs  k_v_4#!!-¿w?s 4э\I94Oږ=HT}}}IF_0~+~W,UJxɞ (fxϹ;b*Jy!:`> KiK ıRZ g!N{X޹XDIRW(:o돸,ԬѬD8EXINp gRQ2g993BMD]YZAGqz5J:n]47 /_&f`&>INQ${: uĥwxwi=ʃT\v$e`e_TG6s HCV{b(Kmn wE6ڼd%l%L.2h!j۟|_&vv?"Ktm^Kw0`'dϗPA2ZʯkW2 aL M;SG=m '!N){l@Fž!v6AP/̸o}8+,QBI^GJȟ'u3OE0#+xdi]CmoYfHI##H*ӞSNRV. 2)tIiJf"*u]dLd#Y~c (KN~XOi;b⛝'fdp#ס%(Hfd9KMvܰʄw59ei2N&c;DAcD(qeCYK%gj={TE~+D?D9=S} g9tXPq5 B+|ΡG`?UC2,20sS:yd^d}\ I1oaBڋ44+4,M~_p{_kjU*]/yZu-˸0"T_Q |L8>Z׽NiF>/1'?6~iʥhߴ#z=g_|{dJdw'Rnq/3% Z*st9wyڞCІj8#NyXwh/Bw"! ~O[ albᅣ-ͼ"HdXlWDpq@ [1Zr F6g"JaH3l 3;o2*F<"A38wET&Dq9 oÝUFK~q!@QAt<b?l`KGdaP{h c#L8HJ! + g1d?Re@3 <#(nV]ǡqD^Ф8HdDF 1ɶq_,ڢaỄ Nkt42CH~:vAZRiolb.IO3XOR2{T:MB&ց|˜|/lB"$/~& %&&Iɗ bj!],s BdRrK}{WNȨ\.y*U8>zU<¼lڽ(>g@Mfg[-ԭq'WQox8#Ҕ8@˙XX#8PP(ڑAc(@2U8J#ᲛyЭ,RX DA)1/”8ѢIKy e Is@.auM0$V0oPY9!ʌ1dL 9d"X` wx2I)+L DG<) JJ]Bg1Q(,/Aijϧ(<2OF[$RI1ҔZ$Ce -cLdrfpELyU {ڑ )ԑ>4A TT5W3 vܨt5rU3.d16(b2)V9v+\`\0R 6%Rq*c h'Fث:{*(*-̴aZ)VT#֒D`9* 9me:s8B8FhӔQұ V4 G:4PʟU'I 85VѤZb{c[1d͸:l.UJfd.l2[0Ѹ`1&$XСbq bb~Bbo"@23 Sq,טhCH4Ni!4XjX0 - -uht[nIJn(cv`aN۲-}dv6%f&3jDIܦ8J;%S"@f ^nGa F> 32Wa,A :q1Lj7,`.`zw /hO2lJoQKxuPQyY(Bu$j1&0~=tSu&S{01#@ ȷMkG#@o@~2aT3Tib)\\ߴ^2Db&7BW+FGP 8P9c&+oP'`f5ȃAh5g7 *~@i`b"k(MmT ][ K5l'(Ai]O{_!ZOG-7hێ;2${wBJ"<<-{쟯=t﷦_GF>fȯ?ݛ43.奲w'DZ2}cݜnA6\Ξ>"G7"k2A:/ߗ=tIT[bܝ}ҠefOm8!H }氛8"<昋} ˻cn]˱Yi3J2fx)OYyVHFIIzHXc#/OPTzAݏU`\ h BOV伭`iS=%f!;KqX7Ks3/H1F磯@K 8?>fs_1.%,0 ţwpeI+ήp#2 >uP 3vS Hr^Lbc+P?%,:)KK!T3m_]<4`aZuv ֗<eY7Tc߹f~wLr*a݆'\f跾Դh{tޘ`37/|rոLO~yw9E=r9~U|݅A_O.ݧ}凳3^վ}'O&f~Tmq}G~ڥn>z}Y:j_W)qʹx{g:I/Olĭ{.9V5}+=ٕ KvgF&epuG#|_r#Eg.BF8j>MELc@®[m'[/4g=杷_]xRYwwqc*=ҷϙ7{n.T{'/uլҿ>iϫ?G<;`jƦTΟ}eCMA[wO]X49.6n@Yy\K:]2cuݖK/qڹ̾z~> >GEVov؏7WHsݝfRiQ|׎$f5?b'ZS #ڝdւ_[rK7?C3|zJ=4s<5`qfz Yv>Lc&Go-\2ƠHWڴ>PL /[ϛ{>Lȥ)Գ˔id^xt1M]'r} U:dݲogE?pU򆳡~zԳ[_VmqIIW7?Q={azۅ\tuC详]~q;mr"֧X"@eUў%fmŴrcfwSqcrh0ǹ_+:}:KL8<^5 wo<~E5p[w꼶 \Sc{'tʩ6D|~|'D~=LJm{n͌ʏOz>R]N-yGeʶZpծG#/dOO{٣7ٟ5?jfu[~t?{MYپήX"cCKz7Ty+:nhֈ /,10R/&sfg]{xTյ?^{jmV'GLqNNrΙ3p+ $!$dL!!Җ< p9+(A|;Ak{ cy,ןxgo^y ȶ'm^אoUWA yi]U~e=w"A?X[̑~fugCL#Gnc Cv;U‚;Gd7l@ڧa/M[ >өGoGvK^_Ѷmyi"?1m;MVTsPz_6ĥwu.m a7t.{hNS p uK{+~h=; })>3-zD߄`<_y2d5d:~i-銸ȼa^r&0܇fw6}&ڳɛ[ג}E6CnrAM?"]'6^u`|eVMSaG=`пX<l'dm*D 0xo½4g,k}cdٌ?y#^hy?5>Ω.ҵ [69~_;\Mfl ׏&2{dǖd?kcGw7/?$N>sk{c۞?? W%KO~WXy @3:X_lE s%^A/p޶W= }:9$tri.v&mś,w[ȚEW'owýCW>g[+=}/+}ZԙT6?}c[rE[52 $;~byQd]oES ΠG O'ۚW~8qkbx~&i4&EzI\>@>{QvY,PɏN$pq{Ȏg|qWPdXNΥ|a.y?l#P CS{?w{r G%lhrWBN쎩gvhV>y__yyɀry0r>}g596my5 w >o[v|sWy6^;k U/v$O1K=e7ΕQv-«[誳:c^. d0)Kpsm⸳%KxAT.p#B6gyщ7ˡ;NQ25ß}{y%4=*3Cmn-˭epL.~$70Oht[q6ٜ=Jh;-D6nF]#>+\k)O p  !eI6/S\6Fnh6LpiI`OqgHU݊˖̴ efd"s6&rtgN?hLqtdJ^ Ib$dta#MoZ;$Ч4 SE 8Jӽe=:-jȭ3ب9E1 )iirYBFuE1BI>xF/FSoX &c~O'Ҫi1OPbn MFSrP-TV饔V&J҄|3n((#jg-u[#_0%KѶ\Yq4mFFz a GZD(W=ňhe^!9U'ݗUUdΐՃ\0"4wDAA#y򄠬8ň˗7\h(+'(lAU6Y"RSOuX`]jUZM\*#K4T`c.,\~܎,w&VD 2!Z6Նx`}e487ʊ7zVe§a0&;'G=U:6+dAɖR> ^t~Ӌ µS1}h&&D`|27LYg+oHN\:ԙ J>]R|Z1j᫶ YQ\MV^yqThupywy׀㶢pYAqB(uLTfGcʤ |P\3 UK]1H0cx!"}?pm43kR!ҢJe%$}a?&dYĻ<m x\<-\Yaڗ8 z1P2 b p:*auCSelTZl$\1tTs;'1sK872J1, :0#I%BV8uJIw]KY.\Ykcx!Jn"+P!ZDzU% 2^S 鐜$-IN%'`2]  lԪaC 5ʮ].G5Π^4;5AllBnvÌV bN }W`O!dTU5gJps50chAUinXJ>6fx.yǧ ,9Du0uhn_s>G+1irOf/J3i`)ST6gnT^G}2ng•*giRiRخ$|Z +ltDO &ЯwQD4 $B#BDՆB=ԃͣY0`~+9N6nSxgi90XL+ZG%( ! n֗dchid͢ õRTi\F)S1uBN'nN:`&@JHyB <2e/;Ƃ,YxU㡞@G~4'`0-:)Q,[φ՚+ P(Ӂ28tRL]0I; ~AI'~ /=6$!,KhnZQL7I()=+m(95eȧ(ࡋ &M.|҉ngGָl0X`m[MUIaCࠐ-JIp*#gy|z~|LB#й;%+f.6_mдP6vT37N$gљѬެ+st4kVW;JZHV z !=&a;# s{2{)\[1JQ| j8| ͦQ^r4&!PVr(@P!IP|9Z=:1=јqc IDQC G+I`"t+͆ +2,!Q{,钅U)޹_L8!+hT)͂h@ՍJEE>P knwbN5R)|b~p[6PL?ZnK0ѥ-S)LK%m8#)XOo0Fu3sfA A@BJ;o2DxuCq:^WH·^ mX:cꨈuaPzv+A0jaS? =nQbz|cNEKjjK-^uGh%I-@GZ b)b`XdMž0&ݠ^C&/$9lX)_ Po}oy42GvYd;;B0;<8 4g .SD$DY7{w cLbϹa`[J!u2kȒ)FDtz/鵐xIrZnE7T^xw~7\uCj-?11(g@e j#eafa8kALXx;=D5טcg?q**ވo"i3ڴ ̖Ֆx'yMEF(**׃ܢ}DT{s{-|fd-tbAD$ŅvQi?FvPra$t%[T > -uqRk=t(,605b5CpM3jeao'Ҡ֡CÍfɋi8} 3daR*Ley3ͨX rtĹp@%h63;a*PJ+zH!=͡0 ZԌR/YDRJK?SbK(y9m&H5+5G8>>,3R ޤtii׌V&kCˠلaАF[b 1fF4g2xX@>\:8?TN2#v n+AE?MX_XMt=mQ !9@ph[VXOYAiO?{ /^Lf|G6L)Y pSonH5]jnhA01fJ !,8> f\qtYqeđG4#ˈ @3F 7@֜ s a-6ojB2:(Hbz0^&aܮae# h&6V?iIiM܌ݞ#5#ry[-+QkvVbPރf,hPաVXuԷ!WrUfwʹU( mf\_`1o6P@ȕhKUwx]X*bjR`>[ "PJ#k6bf[S} X*7(WbL0c%filu|`62@J*:Y S-ES1Y)@OVUz2 yAk،d" -`-(ۜB%E4PxK\n! b7PI!s`13ogfjO~OSIꢖٯ:?2Y}J:Z`k5C\BTm.tfXrOH $(*'bfOv` %Y y\ 3}YMMZ6 lH@宋'0@w>3.09rȌπZyjOǫOeĆh&CңM.Ѝ,Զ&MU.F`~3@;.\qZ9;A4 h0vQ9‚.U\EWPlcZ*%2F\V$=ɑ`v :T]+ 8ܫ̙J‘cJ @AZb4k4V3`򮡡xTVRT嵏m&ì,9d-5|a)N6k U ԟlm,/KND4 Y"8HT L-0p•ކf, mH1NCOws+9M(ƻ* @+4eةtQs ZbOad;C"5"cZhVx ]9JT.$;0BC4cp t& JJ o-zDN$TnQYa= EDJ]/Ua]/[ܮ1c:;v¨}94%$Fe)(?+M0)&OF,\#YQ51thk}mD5/2cPQհ~ѡŀH%aQI5+k&K bl3Ba*賦x p5"|\ Ll4PX GС>+0Lf1tA0|Lp:A<uX"Jqk8aq"lrD꜁ J δ #C0%V aLM`[XYK dNAeeW'K+׈ 0yd.V|6\ FeСQP̲P0VH034/ . T,,AαHjwƭ;aGA){?8sF_Í ى-VԖR QVT`RК^?S:zdž2eD q*f>Eʎ3!ra "c;Er8*GP*ؓBqt E(CBP,7 S(X}BaPEXp .H(v E>Np E~aPlm@` P^Q }zh8#|5DXOzG]3ᵳ܋`-Ǚ=o`MJ2㑿:يTi( XŏҘ]VXQqհI.A%1'c`ͻ m< [>}3|D2w:(^-8&'b֓²!HD6I'sNJK)0ȧؐ;/8{[|]w!nRN 1hkUaq[\# :q!z՟!H/ɎRjd"9~šP]GG@pq',XtPE?r'R{FY1Qx[i/Xy|cCS #{Da6 (X[8Z8!>{68wyyb2rY gd "*n`l 0k<[q#8SG-AeĤlw3phx0id5-I /i>I:d{JMC3t-mL&A#whSZ5th \ƶ7|alhvNp^:Ͼ7"2g=kCRPP1IsR&cp[N@ U_*l˥=UИ5؏|Rr{vM4ܑd`^Lkm5G&zz<#͇RB*Mp1 0OY?GYcɚegm3s2[^(Gs'Qt%#73C͎C h-böSj5ZT\kE7 =:h/b~ԉP$R Qntf@CYdA:4dƏ;;aatt)]sB W od~x0+݃ Hy~M2 (3Hm.fO&kgu%K#Wd%7Sު3~kD 8l|Ȫ֌8pouV}yhwyMI^<*zlvb  S( "7=df^ *daX윌Ml,U,R47>:vE E'-N^ ̱JEK=jdYiPhtʀ:o{-Ou`}.4-ܖ,EWYb D%58W3 0kjK6f6:#C?Fy mӐM3UW\^=L0N23{2JYp!EFýIT_ (=x*a`s" <OqC̔ 9*ЭuIt;=d q}]~[xM.{d/1*Ĵ 6xHMH Ѣ1fɸ 'xDx-:P?&+RO(%cIX' ZJKm*SU}j2VIV6QHo\3flER;ҔMC.wag,1FL݂!RkijQtRktd}(?4^:JK\MZNfBb3ug2C6@e%uY +T.ME$riUE6,ވ5:5RO {DU0e$΢ϢϢf!̕@2 >\LGJI#HgvŊ-jFf' >ww).*xd:oڱYBQ=V TTF Tt+Щ.6@bhVK :oؙ~\lu!-5L;`Yٯ~i 1F{8.Sa+oc0 љ{m {2!>YVL&!5t:^)DG{BPY ǭ!(ɨ,p7`=MتYY' b(V-6Zّ}$,k)48GLa@l}^jK)?h'Ȏɇ1K{G8tHm5*ea"T+eāaw3nʖ"_8,$c)^b-Is9n->tdMD]gbz0 ZZzc9L S;+ i,g'Z FaGհϨ69d?Q_H M-%;kk_9dkFrQ$lD81thbD^I4s%Xrc4aOe)>#LP Gg5Z8k⢗596|Ta󷥘e]0`0d @y"n':QÀ/i~ Qqj k6͛!Aku @2T#•Cm@kt!},)Yx H]HvU %;@&(BZ- ! )/w~M`q)aR䥠B7`$y6m,d/jSVLďqN"iU=ˍVkC)򜀌kAeqP5[dQ) X"LS<d\ճmY ' Q6r9 FLS`YxUrMy6iBʉ5%awI$vI]D+=07ra<>@]ߜ~~i| V^#/r%jB%#{V<G3'2>:Yl*>bR%쏵HyQ_D3:!hB,LiVN6*A3K= B G7e':ne :YPp]x(<R%vӠPr vf|njS΢P)tl `l({lr*aRdX/Jfv/ȗdؕ61 KѦTs/ɤi)#R&o9w0btDDp& C6|1"ᐤd*wCQ K_"֞XORZ"Bx%d&gC@=:3q1'@d"ɐ'("uL fXF f" KHI@f2ƓL’{N1`K35H 7_ {ELՈ~GaOĉJ#18-IĻHl^(3<,W 1`g,"_ "8aiv^2%%E|WCdD8J$0,?be !Ӏ&WWV^'>1n@U\!-ʥb '2쾩lN+(#!X  DS@Odj ,7Ϥ_) d\3t*la, o.'#>h]F06q)Z\@o? ^4=j@+5͒,Jh6H%hHRS7@nT.'<1X@}6IӁRgU$a"C0-iCadjSbwV|Fw$+ ʹKV؈HM!沗47cC|f(p3vx%b hBLZZK j艬%\Ⱟ!Qc̈́+0@ %†l$0HBs,rXI@ZE&\|`ԍ2 R;/Jʓ 8E?,v,HT%p_0]'4&eLA $(\E XEcny@deB*_=m(Cuxt^!9m. 1RȋF2<}ƭ}Xƴ?ڐ`_2Nc2?`CBRfIZ-lK5 |R㊞GPϩ, "81Rs נn|>Lv;*U߸V'"?Hs VmAF6$$`PhX@Ed/kmH ܲ&WTl b %`16 $rIw)HK^nwd& 9`$#HJ*Bj% AtdPRc2D9q.6Ɍ%EȌUO lt&(36!z'3.gC~/8T~> @%]];6E@: y#fwM>LQzb YBfL2$%i"d̿>P2L *&ch` YtQay6c+8{N(pso w|?z1Ss:wR}oѿVWۤ?cMV\V=WQrseO; 91}Kl__ݶj/YWƃ/ygmrLJB&[^<5Փϻ'|߯o2oJZ|Y{Ʈs֝K7>K9'6g>?sZ8"~cwQV]營ۏo]z#y7.Â|ze#-w?WnV]m^v˟~/7m{'cY_8o;.fy/fޱy};Fx}Nr*9kSgγڢs[`pyUs<pO?ijqi u'ErF:^ 3lV8~=͈K?L,3NM?v鬒%yK}mˏ4/Xx{g{kG\}?? К։-Nsғwtp_/O)o¨c_0ݱs#ƭ_MK?4uw?َeҧ-M'sS)OF*(z{;U?%W=;7W|a;>?#^םYСlYy@s2w"WsNx{Mkm:}W\yl'euUGCW_}ܑEW{ɡ_܋5W<[ʫbg6=1~>}oW|ʱgz޻F߰sQ?λg]bǏvYEݧO~E6S ss}|I5gw$>O_}8I52;WvC?/_5m.zC Q̸mg'n*2ˈGO,;Wn{}_N=_$斬~ <&~=xCN9{\y{:G}E}Wr##5wtͿijE87RVEkΪGoӚ>m!gI˷O>$d5ֺn.pGM]4%g_k]|ԖO;7|n§g$W&~}7'2*%1zΪ;~ x|j^1ⱎ=]?5$/jۦ >bk.Z~ҝfhlߤN />5N۝-V6mKؿV3xls7.>&{1l瞚Ɣ{T}ͧK4q~m嬚K=smCߟRy6xȩf6͜9mN[[i%W~/pȚWqRiKyDZ?f}C㖷t}7O;wm^hUb }1;?ڸձyw9O[7~/}C׮qmO+>[QyCt/'~B祁E_w>ۑlCal儊QvN%'WX'A H_`/_d/r8v"׃?X|3#)ogcp?OZ cԔ 59/7D7ι`]sVدNߡ{/z{OkKǮQ`V(pڋ๭,MaHEB]P.t M1(Tvą*d2|^F[e>Yl ʃT_ KK?lX?aP#((@?!4 1%tKI!Ɖm"3 JB9@u)Xɬ0f9S0tZ@!{¥{ws.# BBjBeB h斊 G[%P +T x<.dN22'6m(ihE:6*h,7N&YiҊqX+Xx & 6Zk)h9m(|^GV+-JoFٜq`Ƃq  Hwa~m*l0:FlL٘qe9`B lh98dL@<)AJ/>Ib[nHGKӂ]ݖX ^{HA5lHb IcyT%ApyJزMXX.GAÁR讁?~!{SAvTy&)I;ױ 7 ApQWB1=,EB*}ዻ1 a(`PxcGiny"hoqIF$:kʠתqȮn m y\^[6RXS6dUXGeO$Vq$Vz}MٻڢFnJ W桇]O*ɣ#M]F@hEq!G`YDF#-(gV*x΢F)wK`vCq@c)9~ُ ~ᙰ=e&8޸ _do& d0cVވjZ6ZXɣumB&;aFhS~[M~&SI-!q<RMJ%C)@!L#}ҪM0etmWF¡d-OƪiGL4%-ɹL6("'sz,7 2 MAbSh:AD†aQR]4T,D)sb("D%$@J8h IbVc0H  %X[lW nn PUd4쏶_jኣ fԃA˰F9iRLiQ ) 2 f!5~BPRŠC¥-@=FڠqZVaRXBS$@Bd+W4F0I&+#@^̜TX6'.+cT +i1#HpσcvW=ww4(l=uh b-%d搒ZV(ʶWFdVF&D Lʸ ҩx:%D@sP#JϵCD<X1 M"Z(Q )>Rm[ x 6o.L Uې0 bu!V:C7 & =ª$BR"8fFNʐ 5&2DXw)Q,GrP<#o¬O˹fY @G:aiU^b\hT z/z+2WL5a0#%  ZxmSE{E&rx2\t\fZXI)h*߃&(FڧV\${oln`h6$O9c Gi25  tbs/푏aZ'Q ݛf̊ܐ?>* #-L]+Xa2i瑨EmCN3^h/2:p(/ ?5=Gs,ӁQbhNꠇ',bZqJX UiN1BCFWU)i^9vg*e%LTCVHCJ!M# aõIXt+WO X B:4MB+Nc:JJh`4+]ƭldd MLI.iU`zhp4Uq驚X0SG󅱘2}N׮]vu9<>ϜO<ϱs[sȤEߊN[;f ĚHos]6vwaN}i9렁OԎ [ ܽ/,'L>v/j:|ߣD3u+}co]>7c͏7V͝[3u>ݶ`wI.yמu-e݀M?8Nw~Y|ί?s;~:-}؈΃|vס=Ѓ;:걫oG0[趷zW7uyw7n:u8#dt˸!SnثcG/lr8.|?/:x^Ens?Aۋo=Zm\G}x޲m g꧝a*flr2fƏpQ_j>?w饗>nŗ~t-\{ѵnr!z#뾻yhMW4&C򴏶ͦYnxTkz$2sU357U~8YO/h…]K/8TixvӯW[ro=~q(sVWWWggY}ݾ+y^ͯmvWDv7-ڹsW_}O, Dž.ރ\t ~썟Yv5,> _pSOZ۹>gߙ}ك9/K,<'Hxqst:}%+ם:_O?]~G|͛og+.Ek8ѲyuytE_?|_˙y^yn=lc^z/"gΕDžlN]y?ο)tOvEMz衜ă9ɇwmw!v~% .8?}?G7~3o{$sZ޶gߜs+瞻φ]I]Opң. i^>a7wyet,r+ۖ>عo~Z~/Xye?zƣ-K'4;ex{=~cMwTK:~q}/G~pOO`~=}SOt;~Eww-΂NO:)?oo?oy?+\t'Hds^Tѯba%mmJƮu)'w艿^Vܠ}~S[e5[WoMٷQ[>;n;Z?o:xc}WǼ,}oA_gl>K;}fbC/{*b/1^^Λ\^'v`iW@xhG{?:@}p1oIʽ(_J_?zaه|zQC~n|#W{Hc>6z)ƫy`DOߞw1oaO~ECbVR応zꦎw.f2's}ײ冱o^{˝s_no$z0(ΰ_YrYy<8?^r xeQl=qwoޢWg~3S)vq5n}WM,qڟ+|y_?o;ҭK++p#&}cՏ.P~X-_VmqqsNxSkoys 1 6c9ew|?gܷ5{Yt`g[:>55Gs Gɯ:l}臧.3WteKzǟ}`+ߖ=~ot8qJ\:j`򣾫.;ʰzh;G;J?t5=9h՞S/}yĶͿ>11mx.:5ݖMKM4߼7D,i߼9\xF?$uWcǍ}pp3օ=̧9x}MNg/Lֳxuӷ/:ھq>S+Fqk;l=`z:wac=?0p/z7@iG|ቅOL;ϘCe>_ߌ~}#L2C>y샿~~E?z-_N!\'*x_ök'_rKOi[qi0#wo]{F\fz@a}-xo{9;~oq+njwWwYOp TMo$8`Sbӱ>MK14o:}^6Ѷ}gbK<9%o\ԍ){޲̹.Hq}ǚGo+/_zX%g?zmWn?xW /]|pc/>ܿU:|%]~ɩ?`A_ߗ^wk7WjFSt>/^`ЮbİmFb΃=}{לy@ݢc:lF[l'IRKKK<okk;3@3Xhid@ FSmD4K@2s?Q֙k~ţoנm-ϼ]{>53bTrh?J H[Xuolٸu{{;;J@.U1gݙ3+ X]|*ͫ@sk)Kx\K׾d~<ϼYtM+E]< -_̎۶m[/{g\:57ҧ+^,SOt2vgnx6q&e+YǣZy&[A,zt^_;sn0/]]ܑt;z|m᎟.fgQGHK7p7n<35g㚙cw}̙3~s5'5kgb}gNgl)SmrbUhRU6۴؁#GO|񶮅fSSfή˜pibG'oq7:ܹs-kwqs6ܼq߶ŝJ}>OR gt#cO{lsjDz\a筚䎑yKO?tβw|kk~wlhWZM~z_~pOe阕y.igIpΩa{}Lj8lA_~wd~~3w\gmƩs&xߑm[\`wG_ZaQgG~8la/<~A/}~ߍo>3/*zlkׇek;xwV]C'?3/{­x 9-S?]מ7IkRѷjY39ue_?S. No5--k[WV}rg=7tjqC.tuA-7<> xuo>l㩧]?_\7_)ཙ=#-U~'4_o;npn[FޞW/loԂA_yˇo[yChLt{藟o| }{Q]~ßǑgz--% 6>IѱtZ G,{꓇L^~;xgĔ{߳?Z^YvC}ۯq]6}(HN{Q/KO}hLÜ>nHœW]uᯝ㪯Cߪs=Mv+eBnU#>c0yB).Y|ͤs=gߏnxoxᔶ5҉t8tHݪ oB=roTo s/վ Ni{^5ԉWj\:Ł1I7hw?" EE. _34GMf͚)C“())g)xұO*pҜcs4͙>9oC^oX"kpv˿__>6mںu+Ђ 66 x(rWݔEҌ.=oz583?Q90k{b/knuuK+DZ0/'6KP*NUUb!Is 5ⅱq+MЄțۨUlŋL keMF gmkGuvk~\Z{ƚ,cKnc6| Pm Ma~66&*\؄T𑳥4fwC-[:b>Dvb6L#LJ4\;`#hjq˨5byޤt {Fn- \ܪz 1VW3nJ $]}JphT<4Tx^֣򔬚y5=:ueE F~Q QYB< }Go@ЭK &/K'ek(Vd?cSUR%b y̸x',goTxz"ief-}ֲ=J`n8޳гU5AiZ֓X9HM"+SaҲ'|+ſ2ze^=kKǿ<8j`^TUQN+)b]UGAbXXEᰚ\5PFU%3Iv PA ɲ[){KT%k[,q~B>qaa,4YI$tĐuY-sbRw7 P" #} 4jn $Ħ@lBg6QB4c?|a[ |K(p K܎⨀$-@9X`H tyuI9g?Ͽj2Mg e SDmEyۚC|G lb !V<NGI}l.6 ),= OXɊiǤV:R" )70Y),UMh.u9,/ .k,YؗXGMRcAj(C %`Ӛ&d_ 'e+QRdh=$a6萒6U:JmB E1 V[4"jE#+CUـ\4ra6[0ɨa٬rNw2ÄR%6,mKTbx]OL)I:?Ζ2ԁN{Q왵LlM[尃S?K(ʋ > aK*'Yv0#ĩߙDTC!6d\ҷ.j Zmk>`;fC]IA"jqB& q,S͖VJ'}7^OaX%^/)pIިJyf/0k:kZӴ4`@ޞTARvF%6xP=*B \@]ca3) 1& DNkh*񶰄5x4;*8R.:ΝvqC0ŠYgRf8Q(lP*g/f!` uQig g55-8B2qv :*`ZFkм!$Z]ΚTØTƫIҟrE(S`ȏ̿Xh *{= S,qIǟl7>mCԊх+jٔCnCu2mJݺ^$u=jqz*Qm7jĒtvNJ^l+.٠jxq^]}}r@0~4oiy^B1\"2QȢ=BO3CnzO9A!1,$4RXBe?^]T,& (Lk;0 ǪoRR0HXaٰRY trd/mY-t`̟ҽdַHf|lGJ2Kۮ~ZΪ+Q8ek]xJ2UܬqՍT` B%߼P(P$pھ]q]Br,^*#ҕ5|yyW4͘]HMSc2i|{B !&_̄C^Z\{j,s]:mat %!F|e3ȌM(6 3=u;56T. {.jªr.JsXeJ+RʑـUhuNte6j\}uF6ƀQ)bo,P^,t_7P^· S 寲ITOЏO۾ۓ<G t6R$^ ]d{\m1SήGxXӬKO kJ;NWLwzY,U7‘?KIf9;qϢ&s]L(QlK5d-ME HᔸGBq*d5V5P_bl 9ڠYG0qQ6)RZs<,=LC*R~tsn&M۠ ɬYj\[T/8SK Fm",UՠHqȣӵCN1=(apQJ r\je #6lhԗ{ëE dʤB)R?ԫH¤[bz>5C4QSnd4ˡUQG*Un4AfjZv/P.]IkNkw IhכT3Z&Lݕ=6$6K=]ZR_Vh:" W?VĶWRj#in/ɣ6He]W7 FB/:4MRM1qo ȓfdvYj<\V+V㸔X8*V_@vc1QqSip2SX:SZxıjD$|YA;,rCZZC+]h"#~Jy|1N'L@uc ^i}ᝣ !{HC<`ԑ~laN PQ"P2+C`S >!&ɮ勥RRQT vqOӐ[LvI=/)x<5jAãe[ .M D]=i»sq =ɍMb\oTG ƠO%``8WItX ;olr=*DPPք62ꚱBUPLmXYJ$&0>1`ɂ㔓 x F'v3)4L#.423Q(f$Š4V&Ɠ6rVcM! .#*^MXےqiB+ e&ά{& "7 MSHv!fG8lI^z W'Qb07̍-%a#k߫|Pq̹[XJoXڂ#" ˱VWz CUaw8݌"ܭ])!{ fe+v3ٵCKdz&B2{q'e:;%Xf2pèѬ_eAWo\K|TF;2JeaI>WJ t\sL2bETR٠a]3Ǭ.Fh^P7LRz ՚eȎ. gn:k d vtv"=pPi'@wd*~tD $-0P`mb8HfV[,4GDn@ާ*.{BR{N ڥdz152S:J'u9 tT3˾Hm`]%=-r.bYW[T?s <2 c8pEV̺=S+BgfQe:#c>,p3z֗.ЭxQ# =%ي5v6:fA(wя5^;yΒM!)05Tҏ!S"/>.THMS4eÊШrGSEv[#mʆye꣍6E#ӮPDL2J0uמ5([:vi"Fub%@m2[b@5X6Z?vfvYNɔ!U׵_a5xսVnhwP8@ ]p(=9KP0g68 .z f:+zP=wȞh2E)k 9;6puYZ`i)uf(T/R iQ䚐?[ԗHnߚYg9EhhQS1WStLs~'"pan&YC@q$?7 S9Z?~6lBѼ$͉9snJ@jCAIqF(}ha0ҥzViQ k?K}SnlM7LHTC{i"?*[Ic)K *V(ihP*uV89M.?0ՔkӅ^ӝŪ+SԌ^ 4Qv=b(^R>$cV+ą&ݲUWL[2F?0MM*U|,},&ch$.~aάYk5v12^ň~hfQ_t/:W%wIٸ YeW+te,S^',6RK\ Lf ʔqT;fKɖ7гڌHjcǍrKN6-eH=֕xp|n Sr_UEGM `5#,ʀfXU4JȾRIq+E} 1߮1խίc Ect@B,gKr8Sfh!s"r, >mB#Lgp.W.+NbxCbJY-dYOG1wGJe{{RG{DG$I5Z(9'$ѝ>.|DVD2Q0Յ ABʤ͐:i@Ai h">FTC$M%Spn0  ;JdQ} j,jQk|GM.-=G&N9(Kυy9rI|LZۗ. iV"=LRvXQDE(M)4r1jLnD"Ҙ֡d>IIazO#Ih I]i1Eu+_P٘S>\B*G()iAx`n6H6$JAV+32p ڄle[5ؤT/8/09S6FH%v |LVL ӌ!N .P¥+ƻ1kX7q z]A*Xzr0.xMV]0a??Ol롤fYnVT5u}ʟY^WSiV>+.v7S?o\YuYd˶d±X+1C"\7q1›9btDG;u/ID|{[3fes~1GxEYه_?"mgQtPAQtC^rÖ^j1 %S]scLd)D8QMEB=a62O0VsT3hՂFH"<8bZژOl$H'1 ēUM293:vpɉLRQ}'=jH==6O5)S+pJ<͜QN5t2H~/'a̼S=!lBjnAwxEv)<QQ"Iq̼Ђ0:#xVa-K {a*TlD&rHsVXt+{j,NbNS3<+/@=\5O&RltMP4<̖k9cT &hd3GirIpdxoa-SpNhgE/]&VgЖdW HP+XP5ei7&/biFqVoe7P)GYyt+| el!#DER[hF{#Q݀'Iͅ[즰 pi,zZ5:hD]԰U% HtaHd`CԞpj 4! e$h"?a r9m$J˰䩧ka5o'iQ<",C FɁO J>؇䶊{LChcdǤttRgdĥsb,@l,IZak/1+T,Zk}q hwbWʆKgZ L*)lia+AF.htSyƖVB *DK^FN%;ܐ^(^|H4ҍdHKQYP9365>:8daj4dP 8RljIUJ"DvtxDJG24H4bR00"6` RF+ f3Y"O U#a!ОSɾv'3B&6踡IJp1`ce[:@'iA:Nv*d PEƀ. !hIu(ƧpQT'j JДK˲pבJJ˄5/q2 (AN>(l85蠁9[ݥ&2A<ЋE^ 0_aKa'^^gڶ+5h=sɗe&n6F📕mw>P?  ֕QvL#y':JE`G^_qk~QbI@OcD!.BDT#:%W bi=aJ)p@``9) 1,`ӕhᩛ3@&RJ#$%i4|/C7!3 +&Hc2NK"T}\lrEc먻݆[h$%C:1P>!%QګaqZYobѺ`*U6Y=\IC4P'O# SW/=QPhw*fBSQ*jqO'#ړopc\9=cn<ԴD'P mj0V %hҐ9$bL1(|+ںP2}bs)S.27EfE,Vޜ_^FZWլ$p<־^ 'BRG1fNjq K cv܆2͆Mkz'SlQ&Z$rpe>V8>+^U垨!x 1|q3, HX$PLJeorRe~7-tGEIy %4G0@`R&[HckA:<8RbRɰtbmjits=?*T{pz&,,$ȈnɊQb8˰RMu29^l%NW^@kDG-❋uL𘪼qOa֊P'eD̶3`B]XNJoڸtqtW:r!nGg(1=$=x[6f4h9Q[ߨ$,u- g HID@i]yHf%$) JzR=J!m4P{q%,,!lйXxp ,U6iViTPۄ?X Ot1,C#M-ӚL<3ú&Z2.1Wl,R+uB00䋦>t^Ub;,oLOlԄĠ Ө ,|Sǣ̒z9EPajЋ҉6o<^\X@GD됙7DZՈ`5IIvr]ZaU`lj$tDcdJh6}ʆ'MfJF&328iɉY`NAD E1|ߐAwn~85"1 UBrcvTӑ*6diK,N 2,Ng/B+qDeA35u 2M6LL'D@rFEؔR C r'P-.`Tԩ@"ug?%}^e; _Y93qJCo릴'+Gw=sp~\G eTb{ع)|;AWw2譊q|G#r软i]y7&jpumxϧuHy¾ǂ?c;6v8>[Y?/r{qȗy*tl[”Vm wjkY/[/:rs]雿YXMON~gТvNƺŝ:z??MwU뜔ԩsO[qCi%7~3آ3Kmê?^:z/O]+nomݑINMOPݲKk3w~'z.|ŮGb o51$u@Eu=EȠ?w;]9sŀs/ybUCÅEӇ8$qxWGBBP o->hO\f냕޻*+{oȯX{nӦ;&p:>n9&Cھ탡iوO/,hD'n8EO_XΟ5]{1K]w&ˈn{%pUC}wAv5L{nik,*XI-EŎf4-k*?G"CVڽIe`շk D]Xa}0t-_uDG+n0?IV ku8"Y1^p ;бv۳].'ib1;.|--NKxoooOOO[gW򢦄EMc-U[;֬Y :SKZSB֭N mNj)in]ѵcs{gCK{BԞhM-mmm]]]7oNZ{:;::׺z\ٝк7Uܖ\ٓh]{{{gggKKKrC:nHYӞPjihu5'5W5^5ʿ!]rÂ3d7) k~}q*bY?x܎>o8+R:1ߞ-7\>oЙz?zWG| W?J7ާ$Rgo/ײ`\nuݞlL}cG?Ҽý*Ŋ?&U c//s}gȖes-ػã|y߆aoՒ,+jc^v'>5̑V\~c6~x+ɖM9tՊ'Go~rk;6xֿg7џSAŖnVOwTmw KvfxfGW"ejr]쌒JP]QX&:K-Q 32g[N$n8 fQA7jԡi1r[m]vgVD6TU$F-# # |.EURFRH V#@ۢTWҳ8)@wn^?C_Y; C/shwRz !kӨ)4_C99!;-ο՝ 0k؃ܓEKVZ.:S_r(.Eu}KkbTju(ȢН,Ȇ(K-ep?Ц- r#`lѸ <}{rv c٩1g֢xxٗ^~/<ߕ\ovPS7Z6!~&O~sp%/Ru B벿(GzstmН$H6H{?m}0v0Pc'8CvodFkeCXS\}4Y4X,M3|CDqFM8\Xi'RU72a1&Bz•@Z JRB^FX΀]K밾n./t&WarFo~^ O( Z+k+Ҫ0qwjw П9u=!-=}[i~st6s3[Ewo8?v9֋94PLf ]C,=nYsv|  )60l=9tMtrzpOyؙy6,rґZ韲S(=Dg!݇;By f.xڣ unom#[uD6zZ-|^{v` Z mq)z MMKET<׷bښ1΁`V*G o$  Y 2P˅٤@MZqQ\Rk[MRT}kbl_jp]˨IsH&ḪM1.e3i2o&EԉI$T34@6ݹ;X 1ϐ +r1]Sw\' ӌh'ݼF>P%=yIlҠFH!@#w6FBÕ;(IF=>…=á?>0HWoR1C{ Wp~[Nldc([M{CE!hf -lA N-ebdϚ˛VˡoKh{~ҎLq  5t[xŖlZdK- '[w-eBFxیIZlG , l &žhecK4-lnoGnX,GC6[ j?\ H E KXJޏs(~Bc)ᢙwGoS#B128'+͂MvW.]KrT+rZ<_@:P tnX*JZHn}+ ]I  pJDQSȿ%4 'H $qmtoyҫ qCq0>R6zPg/Džt=26=G98G {0V)Bh= )NĪ!Ere;K}AΒsN[M_a¦$DC= Ȱw])*`uq2IIJ0.\TI {>@-[P}h!Fj;WA&y}trD4;&Z;|>&,@oNcYʖ5?XQ99w{M(EQxyxwo,|<"uNN޾SXnV{b0iN謒Z"lÉܤ·K%^aLjHr\7˲KC.nP[|}]c\r"ܲĖ};U$hQwhQ=69h-${0sArA07-'tGtrG!dzˉS@wM&7O=2Xv"gzU 8Y D9Q9,2{b!HfEIE+#='ΉkllsmALa+ KCD#Gr//ַMniP"B [Mt>kK/o>Œ'Ʈ4އUYQOzt<5R<ęo өL\_U;o$l^:yŋG*JN>u.eO2 &֔#G_]"}@MX SgF%@oi.JE/@I>l?Hb d/4\~SMTq, Q.8$zz+ZHFhI;x{rwxOqʜWd u^6ߞ4oޞo$ѣ޽:_p$3gM_]YhQ?46/ay"8Ln?f+4C51rK/@ph+֌!q)syb`!}x!uTPr?5z|Q_?J_c]|ObHyǀ-TlUU=>CRkLmZo7s9mC}T5w"(
qȣ'u?e8!JRf$j,ƁM' nzoEXqLc-R)ύBMy XeobT҇:T!6"ruI'I'@Xo2zCSJ9k,4eYZXJN/ە#gX߀:.|jx?sq6AZVEJfZ5xL/taN XLb|Jy۹NJxgR9`_`#CHXި⯱j-ODF).59ű11%zQԟrXG@/uPcunpTu0_ ZYGxG-9* ;d.m?JXA'EBIg'9g)k phV-bVAu_ڮ!L<੄X&(jQtW::P)7y׷n[tZ)@wӫs֊ }I(\[d>F"B6ڷ9 $Qyڅ}BӅXc-Vt]Wge:%"AɢOw|`ߐ6EXƊWvP'd׌;M47ᇕ Fp3<%__ZFŸj Y C_c?¿_ońԞXՓ-F aOyw!W_g!Q~g-ں b0`RCJ()Х(H% )# H0tw}wf\{^7r9Xxmh!Boؽ3'SA8MՇXz{@WZ/T,rJ]Y[s%ޭ..͉gZuGN1KnֻN1 %hWN|@VG{#~={%b8RtpPNzv?]'},n$Lݠh1NmKɕ}2NijtZ)+1s8_O:Lg/md |yyХ\ҭnOa+eV KՌQw>%,%eٜOO{yi2zB48 =0 ~: #uE$E^Ο8<ANq't˻.V>Z e vH H^-}Nka1.fI^iF8Z[8s4UÜW>lO?6|WGZ*5̞儇 *cm!uicp?ܹzVcVfjIly)!toT21: ^4ꮰݰ-5=_Ϝ p/[W(]?M0g d̸ύo<"rb"Ղ+tO_V`! Wz{XwzsIZZ[97}t֪BCq)򷔥t ysF)Csa-\woX}{A= `҆\ճ5 lM+5Nq}a{!qG?}@e,G9Dbm[t2=zY*Y։w?;~-L?=RAK*6y1/ Kf IfJZv{d>2eIp}~U>4Ed  -ғ5Uݚ1Ŷi0po#Y򪵙!"/羋_d}h 9Oɗ]  b eDo҈þjiD/ QL69&~ Ng&\f}1P[$ńy#//g잻ZW\-e~4ʥk$xϭ9 ^.e<=-tf.ъA OJwDw^ų5VC$rSF#c;̽B$MddB@8Í]t6&j1ȥ\J}2|k6+˴7o4lTOŽI ((j] c"%{^GA] 1Ov) [JI~dGB֪:jw7cŵv1mfeޭdkdt马2\1XTi ;B[xqS"f >rOt:*')yac kӷ+?%ijmܥdHyhsAjE7H('ICV< 8'Qjp?=S{f~;?P4]8{qX{9}7/H4$vT"FBhI{[|9TwQJ/nf!jW+։MX^ Wגs'~w+⻎:kN?+nCS2\a, >5_mle]_%/O%XF j+n5F(R]P;0uZZ&~{)h.](zD+,ee5\=xiG:b|q\(ܰIET4Uċ3)%!FJZ|?3wL"-_R]jxQ1 _+b X+srF>!?0hCȨYw«~70YH|9\B;DoH)]w ڼq˸P"?*B(wuLu+ N jer^hƤsB\WN麙}'cܯwd%QZsWbzJaR;`E]=gF=):;np*Ev27FʝR*?_+zO|+>aϹׄ >?x.c!K=ZmeR[y51 :u B x9^*goS)QLSHO7~ב!4wNg(3֍qQMbxJSƧa{mSJՅH`oW(dVeAH~EZѼ?(kF`[Ř>F/h&s$= _Rm̹aXvפxMfu^$& }wХoMIyR$/95+VypzDž 5\#8CrD2K̀{ L \D]4 K  Pdv!KY-(CfOp'zY$g`>nA*^:kM3s>0]3X. .)irW c ܋_+d"u633lSH 1sn4stm_̟ű5/_>6trar;q*il/5Z4K y'H]W{ p'-f 礤u㷔$X!@I'߼|΋Gq֔>j3/ #m|[_X'}mM̰\OןO[Q=IrW }`+՞y[1 D_J!nٱ.4)˜(΅%5]x@ f!ԕO?Vm^e=rʰ0I b?Ȥ'zUgҾ qne^7S8֛LfxJؙn%QeWq(mU$IqfMeNR[^*LUn`8 2>Ǚ6X - LߝP)y^$Q Ul&ƖY|]/_{-0IߢvֆC[Ϗ0 o` d:"DMq$KD9b apS'.P?^T@݁aY'a"EΖhY  ӓT~mVJ&mҬqřݨzUq1Tvx?k Nc<COLsX4WSΙw+De]e4I,uQ]zQī.O5/]z(͗{wBB…ڡOv C8uwS.Xh{[ʘ~+zNS3~]hR939r'J0!Y|QW[4?¦֔!>%H\RzFE|ߞVB](&S҆\Q}+h*k95ED#8D2C2t޺/N&_𩇾-zy`^ rխ"jKrFW2t=.0Xiaߎ>qrA: %GioXwGrRwO0PjO&{ ؃$ g:m圣 T|8n}@ \SD~d"X깔E/ i9Eəl_ kIKkwÌXZragsi:wf_O xt$B147k'k 2"TyD_{MuśdhRLe~;^'k׍~pL*Вp}&`Poܗ@ci{E[u1W<7z:4.y2?{|پ!7U/|z-*`TU0/9.L¸,.`~?mʭ+O|[tl_-vAsb97BPQ򑲏O &?YhU\z+Z7z}E!'E2*&+D,rWb.?u}!2REWaߡ(9Yq&'?6G'+Z%E}?N_$ǘcwmw{xGUx>+r% D7^hipjâ)q@i^=/2ZZŸrǎR g wxDor1LCyz/ths O:A7̗>AVybՠ5}k H>A47&keϯ)?^-"ܿ500jӺYm[4W: ۢ۝k)9Tw:FU*jͣ8'ݽss?,sccVhFɕD vˇ&j 4R-Gry^B+ꀕG"u(461KRjֶ׎L05_>P¡fk2l0( 7,3 7U5fj0ʩjgGK" ~`D6fkj?"]jiym/f|X,2pؕ2"$vɾAitd{6ZW)7dAC exlCݞ"O(skr4E)_ȽťmtX w|мLlwÃ*͆/PJCE<)JǭXwњх)kLͷČsX Y4]c i[aCҧ 7nE#ՍTP+*}E -ͳ`V쬪m#Γ8I{`\;]_΃-6xxD Ҭe^|xBU3%;3>_%GFj)K9ıbWb/*E8x?|$qMlxM|Mcl #~9Ou&FȇSrug= &(X6⽺m#N(7Nrv\ ˭4dv{}#4,O- J*KiI j#W> 2æ zgJ2FD􏽩A7kO==H]9^ݴmL~rUg)iDʒ?TlemM]|^&J~4QWc˰75ƹɰ<8@. M*.I Z5ːWZlYԈ^ܘ22MNg$yH\ҊHl8Ch h5MDXsfI=^;E\gmr"C#Sf?xO|`|#_OI/ߘ?hiiCC#ο󴍿_'20=?`|h ʣ%0.kFUzk;I' E]{6Nì0CO_؄B W癉#Hb80F-J_|oҜ.n&'9 ҭw6!_􀳥+zFؔɝySVe44BZ&ke~yJl}q힪w)]zLei#=iALu"e, Ԡ; C⳻@󕌱 粠db!-8|CRs]yeELĪ#>]iNz҃}.0ev%񬤵狨GN>}zʻz%ݲ#뛜(&4ii x{9.Q5uζ]I%}F)wªr;'8Va%rOW]I\sgyԆ&2#z4<WNĔ [L=H9zzt(O;u7!Cdэ>ҪR7 UlpR9fЩn1$CЕrPZQ&7c)-N><>NnXߕe/sr,%)x7ǫ*rtdJsw|7ua_Fmxi)!qwM!2˩䩪zt5iin#RH񩇃MrdDf'Ęh\ϩ :LmrK|CDŽ^LD4wj$#L;]Rg/D+_甿>~<Ǖ'tW$[=cԹbj)IA/d SI'D&X{dwWw_, \(e?(BH.z, oLc"97)<9>tyGJ_96la*eQԃkvX(œv[oٸPl,,w=(AaN9l5-u:fZ(iY]M{r]~P8ѫƯ(pn=:!:)C1"<K70zV7X̯gTv9fHWI)v^ f z \v.l9k 褈,}W(%] K\g?0 (=+6s!_;@L#ېYLabE2@ _V۶_ƣE؀%~=ȬD+䳑2-}qZKl Tύ$%8'u5~qEϵd͌sCךߐy x.Xr^B EZ-+%'sJ~.L$d0֮{$cD0u,97WSX`FciVC~l`k:3FK;c{)|Y'„p =(IN!iT]uHj>1wd]CKK+ H}tth㾰^ #"):0ra 'R̀ti[;^]dv;wH(mcҵ2PQWP+Af#|o}<޸qp'XGN602KF=|{K\͚=N;~Ah!+slk;}Z(^K+MPR)XDCXSFkF Sq.X\gc0*qO1C/t-+EnAvr)Q yy f_1;*Mq8.Q(PԏYEW(*hĝ$c_{sk]HT;CT4ypέP2&+Gc{6ܾl؎rƊml;ﶤlFx8s)_(Ϝ5\";Fu՜b-~_ bd(>' ~C`:ZۉhJENѵaj}EV |40}F6-At|A 2@m#M}ަc^08<,;jdP[F9iƔB eedrSO?m?bڭ74 %J7Wvfy7HDFῥc奭@ `0Xpppzz#WRR.--]\\A0#_deee[Jan^FnJPdKi K[5R89Y A#[j`ċdK$eqppqrpccqiO xM&,MQpY(q*/b HxYSen$啮-ܒ\&';̓Di;;•QnF]\fSr{CCROe30WlJMVt`'I3=Nʙ..)N-mOF/N -fAL\<L2>Wix/--y}lV}rj/# W ؅ ޖS ]B#ls]O2y~,EBҘ)F'E嵱q8yow)i"s_^2lb?M9Ƀ?3cg*ko߬`shXruC]#f>rbDۿ}@Iez,Ս TW=DGuCxlYkn x25YA#fϯa O`xԘ~ө(f"nO!~)_.=nj-& RlE64ط~b[RX ĹBP=/+)(O-]aEֺ棯ڋ]rU6eWpVXoU"/mwp;qkM榆õ38 }fj(~zk*}8Ҩ&1\BBiNvy ~ʞQ+u\Z%uW9w ZNp"34֙"l,zc{A;I1Q M`ʄz'֋itQ'k*ɆeWZd˺eP&b+ {Ć{lC?O=N׽xlxIM7a@t#iXk| "?gsL}% *";f,Eueܼ ~j!a(zSQ:7SQSw%+kqû!OMMxv+vV$ҎB}Z<&j>@y0PzQw%t>W=-j2/|Qe5o}يt^Hiɴ/N;X84Na|SCbw 6_DS2RY&e988#cPʶ[Yn&S^={xNT:>W5h0 apo]89=&䚷2{%zJ/+Aӵw:CTiugY-tMsAD#rYv|ObQ:/~fC̏h: (L;:NxK@$#_+p78;!&zy$}`&"$[T+ -Q?Ř鑏Sñ=qyLWC@ *9/y;\Qrg[dK^fu/%.6ge3Oz[y x_Ɏ6~2r@7?BX \`G}dbŦÈ-\][q{Q*Q髣jYIlj%_K/\;֙Gs5?<_㎇61ɛ|aNz4;|UdUMʫ_+qH)F.2bEGΨV#M+j gg{)Z{S=:W4bCm!3fd+JΜru|[Tٹ!keZ<6=z/\ՇuշΖÞ]:崐m/EBϮr17o++]coz+|LN< 2""r'c3emƖy]9TL-B1ץ3kEKHf4QNP1 oO_Fإe!%N )ʙ}˜TqSc!355z-rF+GJ*#j*']YgXl;szF𯭣. >_>hXWꂅWK0?[1930U<ܒ Sy+zӟ5\EreI9eHUFhG: BcleIÇ#e}]+u4Vf0hڠ>ifnOXōii>,uYZi[[APQC ,ui6RGQێjh!F+01b bDDzm{DCw \WuݵtPE[.2wPdB+i)>bȡO tu`Cm&:TѧܜZZT$[0?C`z,?-śW ܔ.Qfdf&,zKVҀ2ͳ4ԶbfrC̴aܖ a, (͟0]L:V:*Ԃ*ho[m`B])L`BQxZQD&X0 whQ1۔i+\Aj&_bnیi3٠:DFK:BVG)Q3@1R5WrA#}TnjR3ckw5CLGwM+GݯCRCjN 6C82&]K*¯Y*:j\G &֊暙. &E%, fW^YAϬ)j"J(FVD5Lc]MM4 DС=п* ]0L~Lhfq`h0- j/JC*eS@W -,+-ݶ^t i 4:MmM'ۢVSXݘPCovw NN̈́k"hv־ZPj*jWa`Jp5jwI?Yuc}73rbLa s @qh37 rzX{Av(<武m>~CoZ Ӷ6V#CD??X1EuԩѤ ,J^cgw)XYö3~kkl|ꍊ)?##( R9d؄q^ 1Yj+s[33CI]{14LII?Z(g9L;}' &[hfF/GY fm0(|lw{99+]MzX{(5B? ;d{Qr-FA %ӡ6#h5RFkSsk-꠷&;=&;v&ꟸQ{vv?|@[AoD ]2m{/[_g ߅ |?+~A!l\lНj`"0[A MA! ?Q[!o_”QI(e1:#Q-QS46VŤmA#c!O8~ 88wrqo YcnF4,okK뼀%?9ŕؤw>/J$^)J@encp=p ,G'ilҪE*~gtJ"[ǼI+y\c8}D];y5R{pEEFd~c5r{(`d3{Gs+ɕ],s*Z8lL|̣?"Jk?uW~TD*AzmZ5UWR;%-g9K ǵ_y|n5< $Y8baiKoljIqYÄV@MB># b0Ϟ?J'ЍvK+ +~4p5}p*4@ P>Lq#0ׅ-oJ ܶcSTfay7`aYZG~r *yS\8[Zx~$r6 ŕ(zUIrpںsNyLQ5Ue>i$S*ʳ5k>2n՗|z.EBlio {P Up*r(i/?SߤO,w"<w]$:Ɨ<+,~X\RQ(%6M)׉iҋoݯchlh]7'w0*,0viu5䜺_L79|vY8zLf@-Y*zdeJA"#{3(I #>}I}-;z>9j9ٍ!5b>GL̓v驝Ez-7丵击-ݹ=Wyu=8?,d<~G6b{Kp LldpU) Kn^JV9EtQg"ӷ;H}biPh5ޛ$^+_WӮUNw8EUd{y=# rj3WEb4u4m}y`BK=Qfٓb1nݔ5>~8wϠDqjbD" hA:I D(pEDX"U"El#< X (A+AEpmc j,HDEͤO >k=ϙoJk\]@{v|gO:'ϖX2aɠB0ճ ǦO6] ɷN0Q |ٞ|NVzQ?, ,F_YHfP޺l8vS\k4ءߋ2nan)ACێ߹fV.h8{u˓li;u&7^=u| I7qȍ>:\^5|2yvVӖ0yٖCc4B,Qa] DW]5@{|wuş/ٺys`C )# ԖVaYIOz.߽(HG?R?zdDNn!?9 p  (\b&"R"v̂Г7[(JIm`b91$D 4*j+G*%W>Br?".KI :|1&z1D:qt)NY-3J!;a+51$8΅I$*)%S|,pI+U肾Q8KM`8s =0!b_#eUNɂ#l~# sTUѢB![tW;h:)^Ȣ&b߁ ;#.CDT1m2Mj@ NJl i1`B:lUXP[BE? 4{pqpQjpH"H/0ؼ]1jP yT߻7 ,,Sqk,BC}7 [$kZ$4 i$7m% 8"#`t+pR({.Y ,7$*>T\"9j-@b:J܍'A-E$q4{$pC%*HB KpyVIhK .5%M0Z&NH\j\(ݗy,re J?e<I_THJ\&g*@ȵk!4Tވ(]*Ǡ>8 @h" 'p,~>^Pw_%i%{ aDnR ^^=;.Jy;*$"\n%'MqFf·I$}(Po]ªHKK[ZںW=};Ib69IRQQ1ʦH!U9Е^r`T*E"UCq*Pm#b Mx~lɃDRJ϶lvP1xl~pKƒ4B m- ѩ8Xa0xBQN7(추 K"md8(8,ְJ,% Ţ;d] u'8^! [$턕Ty ?U1 3¿U`￉S쓢;z@g3Ţ#.8Lb w,9Yh6` =B<RJ=!0]8RD nP:OrDDLibJG[ĈY]Ȏ:BЂX ?0??\AbrqE;{")3S+:B[S|&)dԀMW!# h!hHؚirb'B!PFN乬&R T1_R:WpR":pa))9X´BL\*&tH-IMtEC-,z|H'O '\_?2iÆ ÓFAOYoP9TicMKKHF&Kf/PmY ׹}alRǘRߔ31q{ S.!kh}k&R]_:$Aayŕd,K Kww7˨S1%V:DYfs\6ͅB`|>L&ST, bɄBS999\x\q$)66OaJ4&0bq%1 s9Tx<"7|7Lg.^ ___9spl e9LPx|<|!3L Τ2XL;399tŠ p l0ͥd(k7#19\~7X,n& t 0h 6KB,ՓVO3JkZL*1X5r!qq+NML{sLKuXYҰG:v`My<n۽pb^M⠁}&; xԿQse^TU={Hovc[ke->=>}sCeO; ˶Pէ;tog\D^)}4JVk^oZ:$>9\;t dmI)&g&dހ|}190qȥш/yJp#{mydϾ_^)eLo2W67;aэ)ۨOM2̾8|,#+oA&' ѷ2qǝQ1X-;N=j ̓뫊]p)V~:sΝgjvc/6}֞KF.gFQ3G}qc]SӕN,Ȋ g~xnLpTCռ OڦJ\~CkC~Ԃ7@=F6|0G_:Sx[PS1&v_1]yL&tl{yw]u-Xi+en3$b/u?xbjۖ!\dMew 7V;-|a9 v@WkZ-W6 *6b’֫kꏣ"#T>_]W]5h?^.^OtZ|\SBQeS?ez^m 53<+b$^S K]quLjGz׏r>7V2OVV`Nt߬/ U ^67n툁e ZWszqm}x'Oa'kklvꡄ%v.未W_N+}"kհEgBse.sZY`j0kƠy~QˑAG? 枉]dž1o(FsƤZl[̠ܺ#Y4{>hS=u:3j24 &@YnUUK!Nf:QimM/캿}9}rz/u)@b`V]]N\g`xdV] g-I\v~Y;39zXm\|9dʽcX<)7\V y8Ftu/ub3XdMS|܍>МWXzш'[Wjɍk_?y &ey\v!,7ڔ׾q&c8w\93)ڢ=gd0xZەJ>ajʻtFwS9k~=ёhYP #_Ԏ[sNxnizɿuǶ{Cpw^ jxHߴyub[T\1՟χ权їH~F`(ƻCwFGd?#.L :ckK>~]$/n(zVDB,~ܿ;x"4(2cQ3v:N$$}O"dI$Hfy>~?p9sϹzn{.4etRy۲t_ +MN @QgD\LKWixFUCχ|DL2^ OBuCTGHYɚt,2ޟI8%݂%O)\{랣sr3^ dӯ%{_2Tg_+hu?L(,8.ItV\=c7NE~ͤ$H݇Vs0VcO,dž|Co!I#Ь8v|n_礤}]k9 zYRZ Ť 2GgA[1MG|}{[AK ɹЬ~GQȤy^v?Yc2G">GWܿe߶#CUEv,C!@=SB TO EiM Gm}L"&|{(7L)E8E[j^7(c3o NP$ao%ِ_Φ ]pSd~hT0xNK 4eCIq3nEPeL  pkBzZx DVoMeYp(!;t۪Ʃ[T%ZlM {ʸ7^Һw `ג3UqG6L(Bdרx3 C|$e2ɦWԭq"Z|ld<#;Y?c:aƺE 93X[HP{\B%Zh]MXgPkҼt(K%ɢw,Y0+-Pz+86{SګboFfV?RTvl("[Z7Ra>kXhB៧Lm =;!) M Aoҙ~>XIR9G Bmҽ}C(qNC%_31RCUR:7weUݔEMb1ؓL)ͪiC7LZX5i/@t4(;.5+h&e+H e0{C'W0e+ R5=Օe27ce I&n>#\Z=(poYB2 xdnyJ ;Rr twGOh/^,>X;эC~DžKvc͜Vl- è*’,rzkih;MewEvc?NcQx :p`]ʌ,"$H?, Zq6 lL @ s0kZe %j@>«8l1j@rcěe\:7os s$#˴@?E?E!_6b(N\? 4ߓsL8!eHh{Q XagݏiUAîE:gĩŰ Z YNP)9E t+ܫy~D! m`tK4(1NU_yǠfPkʅӝV'Nbw {l,my^䦾7랏O` v)6"Ȼ\snu,8 ^3wpj~vpyqHIpVYY(,A^_C*g?~s![vM\3=4) 윺"f!f!'X^kr2JeVn^.^enjX[iכ.75s 7H@KRzze.wJ5@ B`5ѵ&ULHH?b{^1S ;b8Q1I c{GJOJS ~| @4w^H~ A1(.꽏Pasȥed4ʘ$92w ՞Lb#})ҫ^3$+a`gse9gII,~?򤘥R6P']o]w-41 =&G+4;(-O65>J,зOC:ioh b=x C53$| ӓ/\ X?:c}>tL.u_" _XM"<4[sl4G}j9^4#UF<Ӿ.|48B&@Iի<0(SYlx({=>ʥ2_@ `(OO ݥ"UbfQb^EyY2f"9k;*.hu ϧFe%EW:I&ޢy< 6CZV TZb^o_ $jX䀏a) =?ORzK J67Gu5SHف]ohЦ4 7H;?8ݲ ,2&{;m: {+S|{B܃&?쌇R<c5V/u/4k[rB/cD(_TƐ҈96ةs7~2'UMX ^'O~cnc>c=U36,/̌'xc&'i>h f:$nNh7Tt㝩!9䬩ԄOb~l Y嵐+t)b=n/t&_f]pXꋚ49>2"pٱ'>hɵѷujWܼ6r"zS06 /=׼|>j?*6TzDO{0=v/l_@lO ^WH"K"e*\:6≏rsޘ]pdUY[{WuӦSi;2KbF:J#41,,LE&*?IlٙS9!1 ih1Ƙa0}ELΖ fh蛢b//_ɞO @󫛷=9{[|PpOOOSSpt@i 5aT(Hty΃,tN=hZ'P3ӘW-k! `ns V𼍜?^N =pl,\>Uٰ w@ -tj /ƿ+ ?-]o?OGG[Gikjhh ]SI⿙F@Pk N ݝ . tX䩀 !hA1|U?=#Oo~u$әv>] #N(4' ?Fa1\ʯ44E8UNfx>kHZQ?QZZsOUNy #6 Cs/L= GϠM/;1S]XCW}kNsDu{+ng\OӐ κ||y=2{gRg7[\=apѼ0_ce<5,s_b|ي{&߷ob*/==5zW &]tӱ~1-H=(ZLi-aCqD|tSVa[V}1>֛(DlTX|X3V(E/t}a&C(nhT:Cwr*J*ٔ2m,ۋڀ_-5ވ኏T&\NGN$lǚctMx%EP:دu)Cn?`;@OMupM%#O46w`n덓a{}|dLd^C|89yB >gQIcԸ.%Ęl]Yx_"5Χês)A4XD0EF9`QcY[Zc$4nwy8R0vэZ%íqqǏqyKqnp56}7/gkځS ' z̠?]V &3lx&MMv5y6Xe8&CR~U%K9dw)FDL_-a'?ޮ%/ ;O(kˏXrӛE^:zPqw!@h<~;dL`L$x~UVpc:wU beWPtױ^$Sffk׼#m&$X%!ꊡevnTSP _Ge;Wh}0|Q'fA7:Uo;bY}*!l!o8ӛq!!Msvo&&r/p֖ PNn |=֪#SFڲ-v7r1!FxBahtF⌽ɍ_gd}]Ҽ~聿??^1z@|pWX|lq#:/ex<9iк@ɷA*v6h9U0 ~?.|E:jǝ }\OP K&UOX;riG[~pcSK;^&l8%\oQPc߈*K2Sg>t%f8%,Q ;N*e^}1ٔq+a)ZNX|Olb`;c{#]ͩ=sK";Yg_sY(;ZŦj;&pir6%M,5gӐGHu57;9 ,^hy ÞÅ1>_x]D=!~阾?&Kڈ1㣝nfG/ixt(] }K!(=jGt(ՔrBeQ2yM gݿ8Z~u(JܴC< =VǢGrRZ{ī;S"Iym/PJsfSxRqv䰮iS%`~m,% $\2%;tx zx9e잡W+|v=/,0Yʩz`|2\:cӈ >+葉Ⱦf? ğ6aXlx&'דTC}KxӖ'_#w?|20M=ZnF6|^t e/bT(f4c6^zf]EH{+?z!(_[kx`㠲o&>^dMW -A7[_ֹ]UȕQ\d *'54-mMWHϙ;RcTv T/ͼ2-? >jׯ$ ^&.n\wHƆ 0{Qn=M /p7s~;d*rm8ʬcKvQu&[jg.P CݡZZ(aGRCb36imm 8*]2֠Y{+8A;`݂ * 8Lc`6i9m[M\eUOL ׳ت䤐rQYeiFB'kT,#WkMy6m"$pOѮ*NQSfUϠ/L?]Sn.1 -g9*뽾qxFx05cJ'1s?Гveج_*z!/Yb\^mTjG- 9@wKl֑jiTEz˖PgaP@NI`lӼ&^G[g]uN\T~!1=mD`5BTSrM,˔ߤ9a/{X$񌿘oRp:0;90"}L$ÓW}\=otj2$vXyٕF_fS l tMwU\I&Tԣk<D9Y iRLgVvVsJ:J+EqjNT^j`\C'5fw(E&o_m5+גqi&\?qrC 54;l>d%nSt_/w4];|݆L%*E@+.K&g)B2RAE /QO|8u^jlj2j C?Fh%'2lQeRH3KA8ZcS"&z~7$ ^Y>(2_o.Ƃqj|lX,o=hF?Ư)|}0fܟcX]\AΦ3&~uII2t?v $<Ð$q|SZa + MO 7~+2i&ӄ %pķ6.(Fk޸9bzR6Uƹ9ǎ+Mj2L0+TzjE>.zػ!x*~5^3 ,ځ*)@XhB !+E]Rw +ۧ(1F-DYb֙ ΅""ZK$X2` >a*GsJ7Xxr1[}2+/V#׃kNމώK( ǣ1pr kok~d%DjUmC[ 5/R7>3W.{Js_-2}E3KnRRBЎH1zUGCqʥ֊ •wjh$1eҽH, AƼ[(/Q)'-į~Uрbcܬ2s֫11¶ﺔYͺz;gL a^$eYZ,(-A{`m3/l/-ixoK@䄄fd%`tEVvsEfme3 TBRwGu&̊= S@dgs$n#bL2o!G ǶF^J^67%Я9bK ^AQÝ Ҍl7ںo]AL QJT3pG%&6ژKo0,[U Z'vkyJRKeӑkÅ0S4^_?d0Q\fvF-f(Rtaˀ(u/dPnGt pG^c9ݒ5kvvY{ѫAESҲ AvkV6Y&[޲gեw( 4x(K6ËQȮ9ܢjW'S*57+zQ<(Sp)_&+T t%J 1duiˎcQ?3'ʵ.{=pϼG-oԙ~"^2`X {5KGVF JXk3$ZYw? 'mѬg{tě,|kTL:;lw>AY`PvЖLX7sVRPmMkش/v\\9K(U ( Xu9ycSsnra- { )5UYv}ۻj:zN6H6S*ygyFCo7ɒתucų\vkb;#EMmTYOo!G&EJgJ }(u>aUeS0K2 &i4?Qa}xgbgf#?SYHǜTXaMhsu5ؐk8B=\E``|zȳ~&#ДD1(c]ٕ ֈ+dv-v79ņiOqPO L>wKZLn`o?FVX+zcXJd{qr|r~t%Vjs%ʭ.w ]zmJA3&twNx .a`r,use~Bds|9՗E3_^͔.B$TDtL(Z \I#S<_Ĩqk+Kn9ASٔhмP۳ w;XԬ>Re|_j ՜(G4V͞T$oP߹ /CF2dvn'78fU |Uj Ww}ĭs33WkxwPcGS6[ H$:CpˣN[o#Ȕnx"6QHbO\MT+,2 7,dV1m.Af8O +PH|nT]|U#MSvq۲@c[@E׍[?=i;{h><QG!rtϹ+w\\5  *{á >Ő6=ցy^װEsyRb_at1nNLoʗ%9ܕ/:_Y6qJzʆZҊI[<6 5rUN6[|[\ͯ9q//ǨާIjpK>kC{XM0?34}K1tPlȡux^x-bJlm*5:^{̏i@H2ܲtf_BhfyqX{ ,s(0;b[J^nsuM@hnӪ{C<PS 7l2;pBLU[GO81v=[`K8 )§1N qNn!%ؠPa@vT:g=z^x{Khɣv&, T=Q!tcrQ tx#3H7L){5 3a&0v@+ ]Ꮛ;-2Na/-tm>7U}:Qļvv/$T.oDw5=u^9ҩG{FTm_5FL9|uvZېִ݂Pm9(x\ Λa˘ѲqEI=u--b)qɓB3_/[M/=w.~_K,ygtEonDZ60䛟c^4Au9KvI,Mzu K'_X=oDufLt f. j*К fm ZSjL HӗyFv}747h7Viŕcb aUYkjJYle2&.F>k@۹~ҿu.gD~|0{_e pag %JyNTsfJ.{G33WхG;ptGAcorlpN\sL:H؅~~ #<;zO޾quX n5S@M&*[:miC%@]iyaxCgE.(ߣޒ_֝k} ؤ|N.?P鑈؜\cJh3QwV0paj净$ 4i#O lv|d]`;6(5-[=kd T̔M3BY M,r~Ub Dv0sٝ+eM[)d%!٦yja}d/Yy{Sj.OԺWUf.C2MX[GJONM8SX+ӑ#FfXm *4 Կ$Aî sg~|9XǸ::ސ*K\]P }ŇjI1r PrOc.쁊GhBf`a{EK=߯[kARpL8>.X3 闍1X96lՃ dl}ڥ7<3o'":ntoȔvewFJ#[.i8wPixw|m+`x)Yoz_NY.8(gz_w20ZCw(XCǡ foPÞǽ}r^we!X FӅ\w?9)#Ǟ߰TQmdÓ Y<>2='{JJ%6J% H'dN`I@(-n-r6}74%?̷i/UHqBŌ%pI9l<7wB)Qە ß?,LUc;fW\ڂx34[5[e@K7]Þj)UՒ89^s :X[Mk dD"95GGl;`tE,c n||@7Ipg^[FP6V^(m;PY%05h=RtkGߔlw6ڄȷH6ӐۤӽEGF{#K_5uPgξf"AA&M}&Af1 K˖%܀#"+ǣ输9v JRK+ 'ȁUhM>7JY ! `]=,fL Y?'_-;VyKZ2UNy؁I%׫C]͌B<0[NtڊGKY: ܳo2dVҋb~a1E&Fͬ2N{IuɁ;a((Tv9AWqW]A`J-:Fӷ/Y hlЏbeY]|8s7S9RC<5qiËDÈ3E#$/c_!0vn֡l%=[ѯ`_~H[aNąPg>{#ku4ϲGHWIF5,}f]gJFgp 2ko u{S<9>#aT`۫愼& =Tlddeu Y[Ȕkm2KY7oVOE|7;Z *:8x*aи-^;)['&B׿)ؐB\>1/4/}&WB@"zøa^ֿ)T-/h.[ZЕUKՒ7"d!B MMĵFYKUa,>JJ9-rm!Kػ!S؇BBXEN޷ iT-4<߷*E)?˿@ }p/"HPzg({wQio6L0R?جk<VSv4:i`m׺q飦8S[__G10_Kn:3[23Sf|,8(O5{@<S6ʻ|ѽ^Qi/ r7o%㖽g pIjR 592ٴB#wF- |sȵ76nWWmDU 'b31!GEsfo~ݦh,3p!~/27\ra3W,.;K!NvXp~>rی'cR2N'U+$0OZ6=i3?|[mwYۍqG?i+rV3}dh]/R (,0ZQ`P~`ey  cPyAώc붡 HC~Fk5uQ[i D>\gޏOCQY99ΰ8d  Xd)ѵ5U܋7Rݝ]0*ĸ{`uE;Az]r#fܨ"_lH3*_;΍ݨ6֙~^?x?- ?YV /cB(q,.4 wDtNAb6 ;[I?ZӘHS!%5f=2$OwE7 t&t 񯗝&SN/mm[X?mX2EL7y%Re69ez~e.iG&CVr!a /_FsdS~Z/r)^d:빔!f.÷'0s+VJzfb];p:ulj4䶺g?t|3ǵ=[JY\3:!wL`i%A/~{KQX^-qG2Mo0gMݳl)>knxz]Pr_XO- (+iPQLk~x zR!DxTC=8;"VXZEFd>b૑z t]o1Ŗm''޲t(XGT3Z@b-Yɰ%\.cjӚ60T?r2ȆW=r{{6ŗx};c|MnH E!qJc@5J]YG Drҟ~]|-kݭm%;[.`fӃ6$XSG9  †0ZTrJ|x.-˯| uTù8!g N ΀Y*gں %l:BTZQ*3|xOҹ-hhyfBL~"^s($ ݔȞ{w-Ι$,BH1$]ezY[=u !idsЫz voWI\`Z"Ϸ61mQ1H]Ic޷[֘ 1Av*e}q̳r67os;υ.|nU՛\?@ժ()g¾^F3G7vH6R@7Mk*^z_~mkwsNOȃ¹5y6M7ǟjAssƷo"ʲh;xƵQރ BW":X$dܦuӴ}XO~~3P[J;s.-`}JݛaM9KfnT5@G{Zߌ)zdg!;2D :z3_L/P s\.b>p["*8=;Q9qyE*Jʦ9 eI)P/ޙ9v Ng# Y%%6~Jxۛ@纍 y -6Rh.osPP}DLyCKsl ay3V@V-@vb}[ZI28{|(rm4O=!mJaqxy;xdcllf~C4 55[YkhuY|L$e?= 5xNb L )q+{,h1Ru/xB1[ltK3>1C>[ߙDRFPv.gxV[C؍M!ʜq[9073ҽs]R)ds€gj( |A3aq2Vr#n~C2}+zڔ!4էl lnnOVώlH]]⛭7Ur:Q//'}r`X^`D}*);0dM>%ABC ׶] Á-B~~#Zhıq ˉ'1PXԋ[5EhOaS66 t8)M{OY8ZhAaYL7nTph ::)yV˽A`_n ,Q>)IZRiy>rR`u Ll fV,WhM8ָ&f luC;t_S*SoG`Tm-Y0+MOt6!)d?9Ļ+V~ bJ&|MRw*ͨ?i*^Ğ[!f /En H+͟F9 "w9;ޮ~[CsNw$tHQ *A?@•҉ &ZB^mrK@YKx)ƫR+nfR,ʁ.o9yJ{GG9^Z(hg+/I*EdD9)>do4ش|;T>KO#s0$9p&c7Y<٘$τ;D^_hUB̓ȭnlS2gafd:gcrb6  >([>(jw7н -ڡlؘxڬt.Dk9`w@hMa6 3wYűa:,sZ ¢r~~f Zz=yKС{ ;iFetVJwDN"pr[4Lc璅)cPbZSBKa`;ƈ%.k،x==Yy4g~ό~xCь3A\ήD|L iMrY$n;@sVc* ׯڐ˕|^9pp;*k$/%:s0KG-syp`_ Eu7%: 9)>)pN*(* nͤNoKk%|o=f^ͱuXdlX5ZZl?䗻m,g4wkao W5-*5LťU᧜M5ϛpZ }(gE-Tg DZ ((HIL;Wα݀k˴dv$;&jvylb1a*P+n}1o׈Ep8E(oQb'lX0?Umd 4Pu*.̨qp>ϖ(m +P)}@dPw~O٩ܓ4ni}ZiAڂ,&mTLA.%L6TF]92L}n jk܃r@^c Xv!@GALIV[:ek iKoES %dFFȵ4e򆌻iEw]VFFcR,m&: 7c$4|mRs|cv'XbeFf+˂i_ R 0z1b:=<1V?Pjwߠ9-,c~~]&BV)vr80ei9 rmΤ\C؎scj Wi QRtk-p#oƷ8qGߤڙJ;ozAV7\g=0 j ]p|gU8y^? # keU= XBQVEIM lF3%u,t h^QWh2ō}]Rd\l\ɕVI-z;$kqgj(x\Ze4Y<ד. aKگJ(Of`]$hW zя7>W@iiٜjWt)wH m_鴇t,H|ٗ3xo{IQX v%s#/2='Du3@Z"i_]3} F ŘXTW'V1˽Ob[ Za 6K^ⴋ͠{EHNcO˭Kf7iO"ߋIr3Hw^~D]FLrE[B:_/J IgphFěLܒJ6[.jl)mYQ% ꘵# [WVU)$rѱ|,qK`D|Fw+/K#(# i9Rx|͡/㟯LlT8WOųRDQɆ9 ZNHcגo*ǐx'CEŹc:vR37ۼB0:k 5?@nH X2ębK h![]5xquh c9qԎF tF$r'm(BL=D({n\0="U F8v h G # Zj5_!i̒-cKfi5#lg0V+YbLu+ "yٖA TrOzhê\10rs;*JL^\3Ov@`-E3^5WO~'I_9(5^'!ȡ5j&GZ#ѱV{!63rd#H$p ?I1XqZК\,5FWr^j rs@3]NwnzM:NY: O5ƐMӵЇv=O+MUE -@t/\Jn|:%fǵʳ04jJfү/ΝhT:?=!O̎'U}B᷶i͕p8(ݼyIQ;BYbPtMgo.бQmtiF%[9[nI|f/RK b Klc b] Ftccqb~zu6'}H{{ӑ3=АKYA_{58+g>:ōRKJV6BW4Lbq5|Nbϵ \{M[.ҿ,N[XcB)dgzL3O-tvn G]?l0i5OL3vN(v$hpNPZx[lU2@KovYzepf cbɸS?- eĭQ7F˜PH*߸.rꝘNώCCĥB(!ӾC^J9X$_(Q JScrXiC 4Eu̔vF&_t*Viv g"!/ʿ[w~L\-us(2}U*I"!'/RD|I[N{if+bWៃaէ0CHsYɸF&VTyCLւAM`qi@$l^ntHE>y1] Z!9Xli?+͙F9Ĉ/vPhe$퇉#4v?IO oHg#;ZGFr0-Ie"vz5Bʫdvl^t9m9fWY nPu)c% G5pvK,W84]E:eޓ,ZN^Qy)[VHpv55R qm(?Կ@мa 5ٙa&iq6¶Se3\uR MBN> E몫CP=ås9TJB5t6%K߽iC)fl#sJyow^B=Xd2{<)M@׺Y{fˮt*'^[</GSG$uCjdIJZgng| %J." )b_ɸrq#,iĸ/ ޔ*)wȗ$)H$ᆵt:a`y'C̶bWU BuIygJy-V@ qmX `AիxŴ-%r5ݗv1,@[}s2"cw a k-Zq6/նv [䏆]TϴU@=6|"isFhyy%U;?:REmb:P_o_R'2!I3D؋ȑJ"کpT2*QV^nq Q3F !wXz)օ崸S:,&i a,Ma!Ka^2mMt8ݠa#@cn)Dr^.F vj\0Tﱧ7xL7SN3=;$}7+_.p -@cc]9 Н#r$`@o d#oQ؍mD8,:f^ZR#`FNQOuok{^?l[q^Aa-~ sPtk;[Ϋ 2 VuH֠thtgu 5~#&PE>t6ta6dr-y)!#z_kX+3dq~TqDEEcDEkZ!˖!nF`viHCr1-G,ߺš4=(N2{q[Yv$@fFr:Z!d,~Ixq~Rލc,@ӑbY}~K\VƮ5ߠ]lGB6A .p8|=jL7%Fs[r'f1 Ʀ爝S\׷QL]9! )Wgߧ #{˶Kw9+ TչG栝IguZ;v T771? ]5qa`dSt!RlffWu0 ɑvAv0hF_LklWe=o^#| EPҺ[y}Y,؛IwNSx),3550 vMc ֡O/1ΓMj\n_ *>VN* Nաhok>\J9e&]8ROٷ)ln@)=7>(I\ԧn7ufnjRq){1^+ZJz7&+\ҵ]~6D ijgڍy#٧qЈ}ѼiE oo c>\Nv4dJ9/UfC1;gx~ ?oHf1hpo;pLG>}Ƈ/{l>{pFxO:qH*kQtj9֣,: ~.NaqM h^ze{yeM: :Fx(W9ZKD!e2y3͗)q/8 ǻ9c*gn4%9Fre"ϵ o/M~K}>Mh ^@nɦ>h>Mb[,+9:rkǔn0DWڙ\'y|zwI71pxlW"4KW+Yf$!&R'n 3XkL:U?Q+by/}ӫc:ﹸg\ɩnJj =LOuI8A~T\#<0$R2\k1XVZMHH}޿BVQTytUsx˛ET sI0SxH fzIJQl'dl+v 6W^%QVBO888;;8бPO;NB'\ \A۳$K}w> CF:ž?CF50`KR,IK4(ya%c~OϔYM11L?1ٟ#vk)>u#X ЪN:pK ht$DR3UީTJ>xEQal`>7Wcכ\y^!(#0}vp!dU_)K:iVY=rU^|xeÀ+Cbyr{ %W^@,{`e#!Zk&`pwr%g,ٚ0&+3s?IW8FykhUJVU[)O3ux8R꿎^usҷ(K:.(i(o r#T@* strCtNGO?*;{&_Mlsyd kSP&C7gC|`KvsP k?)IwLj$Q-N2GߒwD~=G~I✥77%T_v*MRBb?QV!]">^/o\$}p*Ԓ@<-QX3s5D6O"ӥ2g&^"#N J6WZ9_&RyKMw'uT/ o_~%BD+ ʍE'?[1C9+j)AS#-@y#fDD`ϓIB荥b˨.E!c3L\/Jb^r#i՟ 9H-e9E,$9DŤ%D9$,,%ll9eHz^w9{۰?]޷v 8LV\DҖn!j)jMW0OW!8bwa)q~IoUݽ-\,<;40Qr11•]HԖ?ْ$IIԒ2V#?D 6-"%-,ackm#,B|=?~¶D"&[ˈW)T-^JJ\FXJTb2"DHYڜ*ePqp;QX Qabgl$EHńmMZ6^6DrBJZZ$)%/'QaV K [ % ,ck!m-h=\-}kK"GBFFXRZB"2Dm @EDHoKb)bi[1[X]ZFZXo?qpubBCcXoIlJY[)*B^bDH"C՟]bZ& Q $ŋKII1$D-me֤_1l,n_Ѓ'Hn@opZX[n6ւvC/Vύ8wg ۿ7UuumrߵrvpcWvpqzxaA4;v_/{vWb?ww$ϭ\evqbOl˞ދEa~v1)~v)"w8I0 A &ob[ZxsKiHBRL4(D22v...*?؂(1"a?J=HOB~ubaQr "vϛh P3 b7{Z qǿ!ia&K;K 7 osA"'Zaea??Z8Yx9]_MGzmj^7qي!)YBx _p#!uw?8İҋq\ o0b ˿؟I+8 DRh&b! %.I o@$#$Q,i"g!uI85 3;D>%:nO?+p ?+K?{DN8)4 q8Յ7~_L?wb"bR1%$$Ǜ'jID3@yxp2tE3-%s-?Ĉ:a叨W̠* -2 !:D)ݿJRr~򆰊hBc pW;g/g7v;\7v3"d{:`{|,{!{;΄t`{~Yzz ߏ_bI_J˗\?ܙg~An蝻gǓΒ0*tߎ0R=r/ͩF;dyųq M:*s+z3_IHk30FI [B`GnnkN~S,i.'S<ҫ$S' h.D/U$W;O>A2P8XIr{8Ud|_?bc)h{~H3LG)~M:[mr+uÜKtY̦J6E_mϢH}mlމ-?U}2b"m=gyevNš zcr%٭#?p1k|4]=ѝ09-1ee]L[=7N)-/?_%0A(ӣW~3*֟ S#nwXb.-ξ=[aCF甔w Լr(̸uƌ6{Կf=(V)uE)xjR?iwpm0F8Lߩ0e>#ӊn ߳p=(lPfA--ʣӤ7G{g+6LN$8&xD6j`vn-Lzq~N5t)={%C/6Irl"7CSW8'7 &*?!+o1^xu-Eӻ(]0_Y Too'R(|I׭C͂o=0+с rǸw^x&T?0*IsU ~n>bL.E\b.W7]_yNnV}fBuXFs0/5@G\y{e{]?Z8UydiX_-ث/z"/  P6mB ݲ0˒vFjg_X{`/c)C;N'=50[HsZ [6?z8mxE˳L' ȤHaFҊ+Ի[nTg}w&IS]ֆbbL ›@j~BoC˺-uFۚ^zq][@>㕴6:Jz2nG\%5&X2M TcA$ hcJ1IY 8V*~eȭml: FIi{}rwi^ZAS^ܭ]- >8i´ƹ ^4~&zCrx)xLۺº? _J 4V{*'OvfԮw3 q>  S(.m"cNERvJ+ Eeg"gk?p;Vm!.&^*x8;I-Xd:jeMN7 +,4d)Ib9}5e%Nq*|ƜnȨ6, 5 Wɧ_,-\W69έP9TߝjY7l;Qp ޺XG;Vlv˸ecHU۟7ZNH#N?8ZՑ5w_<+OLϡX&ªJYZAepʖ rgV)*`<o{ ]O_cIuw3xW.PsJV+uKb /χAVlr/'mgmm㔙~nBmY=*h?.qy%(2ؐ2M3"Ӏ#ⅮU)VswdI b} MtjHZ(:C+plRS%}=3iqX _o][6O׽\sSR[+ YQK'ܢY|)ۨGb?nW 6v}s:6oxv9??N/wߨyE>E-/wxN`&7?I{P`ٖ"%5 7!)|7ʺk%3R޻VosHj9LTZG^ sq4~E׎aGirV'ޠuݬzOh]f P\znҝțyu~}vu) v+B [qƆ%dv3Ꞔ wVƝ&?D?(+yF˳b]MDqye`oc~L/(&=f :/P1=t[Ui{3-:RU%5V\.=&&"nbw=mr'qMӧŔ?ѭTڪ[1[\mk: ˪LzcL{φ݆'ʆfEE"B/ihNoU8C$>94|$y /E[霏^.HmغWobLk\M;PˬGJ-籁)G'$$$*h -ƬUݾиyPQVjlAq8 'T NQ3 w+cfS|-_1jwf."$ȝ&0qcijXABLl$ݿՀWbK{Aj?DAQsL7ǨKSZW8UUo?`Q_ E<BX<۰m;J_Nstl&[(p 5'bʴ9C5>}ߟU}Qm^_9 ٭i-qtP:4KQƁ):LW\OMԲ#A&y|-ʁF܈U0]YQ+3\twm%nix[ zzL/(k(,ܥ+[J_N4Hv,+ǖ@L/N֢ _.*{B z}-Ր_ 3u ~>gq~3T@{p%Wwydr`=]-Tz`3:߯V/UPF»y: ͤ:hdāӳȉ:ڣ2\[l4#i~7&-'WƣRJwOW0ݹdcadH)eSx:ksl2\Qa8獄~1m (MlT͓m]K{ZW}?_8ܽ y5.utU)aMx07;nFf,/-0 ՑZl\s|߉eJ-=+6 A~AhR&{v%-e*,=sgˣoG[h®?k&7)S -vj#ٔuCfOY*z`4y{=4,D-Pϑ![oM'8Fc}4źm4rY/G3^$ȫz /!)&vϠSةBYG!`[ySbMI9d*lZ3ɖ >„awBi5+"|Ƴcr.tZ0igrHfڨ}w +~|"জe:sbŅXg:`?|-يx $h:)74W(#ű_뫤 ٪A]a7NP}};/6e lM~}*?G|9'1 [(2ǯv"Lo\5fi 8"΋ l|__Wx1Ug"xMv=j3\pQ=Ahc8 պ|uhnŸȀRGǟTmrD79T #V!fJ/eX.xQ;#xY"Nyh 2=bEKF8Yr1o{7(^V$r+tqpSBR {Hblk:*œ[ :W>?-TNZt"f٠JE{oSX ѶmE ŠD8';HMGg|G*8OтO>ҝ^:h|[+([ݢ/s{ƐU<3vrg6;o&a FLop'6 ׿SKLEXvhAܭ_h2Ӈڳ*oH)iqũ-&l\g_(kT~줙P _Wh1&lUPkYo|I;A;i.$]m>:0(Iذ @V''298^."/wgkf41u-=znn}AZۮXw N Yh PnY> z\ƛ#40ۧY.;G W+=wU`Dݱ @e41EI^@%BZPѷ}zS4 %m_jl7s6-a:֧~V8-p;=Y2[tMniO7 *D޹9_*Sm4n`ΎY7G؍}b|+L#|JGwj^ {8tp<^AпD'R670'1BZuiW+Fc NNCV,:VCnWw~cÊMڷ.>Xʤ2>>q%Ánd _֜U~5ڏC>s/[kKKx.Ï@Z; /曮C?6p':!CzV#c@pc7g-DןԐ/~.3[ yA!p4kE?ĺd˞.im@]ZJ"c nl'QFI'VrL%R vf <>$Ą#t< )cgY#Xo߹ʯ\qo6۩cM\F(<+ %w6jo}&{o]G4pzwI3\WyEUI4"/eiǂ"bW"(H  WHBI@"(^AtE,RB&(MD$d$TVFv?2Ha~U%OCn{gb][ʙ ҵScj۾}.QePx-Uz5-r4Ǵ g1u)<&'v)/U$?jT:> gKNJ{LdGyJ8r8)UQIxgb%ۤ:_I')umC\9(9k5W8Rx:"K%1]dž؁$fQ^wi\/ O9%j!)ܯ{kKi/$EۧD˽8dJz3v^9a4=N=blߙ8W@pQX/;ל{mA >+F '?*9|:QC cuX+Pt5N>pDF`2ʢr9n."O"tXJm~)m1edxk[3b T5>pUϵ$u<) 9[,v~Ȥn6o)wv(D*Eݰ`5M b34ՠy똑A @=5!}֠Q_ +~]J֔/1LՅW%ok ;<>ӕ5w{zz/-CI˶ MR#-:$-K?Ӿnj=܇/Kne&R.YKͥ{UWogK5cS9Z6g(wp>)="Q&GVtGuxЙO*& [5ljy[d>ۚ;$'pNcCo֏)vsģM5>B>S,pCsq.΢_WT>a/m ֝*O273n7ip=fIE .r]Ƚy+4c+*Nӂ~oӮ;Xz8P !Wǖr'vx<{>2 sb*W1Ÿj׋xT}n` M) !z)ު)^ ʵ*"pLQawze>Z`S' sع|)!9lMv'imL調i 7D^ܺs@:*78Ub_`LS+1܉UࡼÛf+%?T%n9?ͼ27_T+1ZI&t#E^\U_C)<-j|`4D}ofR~rȺhs? g3Wr2z07X1qvy.6`ks&Ms]Nm?YiB㻿y|=FkfMϦo?乧]tPYcr{;f: ,n* TbHͨjhݛ?ޢeN۰0|[zV;+uY\ż%{GڡwD¶ϕE1hTJh)xy=nGن%71{Js_>m(^\mnwnT2W ɣ Ŏ:!XF~o[rk9w5}֪'Mm\w=n*$72q259ձAٟm[c[*k"c*%F>h;jl+D}üXi F2pu$=<>v}ٯ.Yc{idCƀ+_wxz9\WnIx42V\1Ӻ,1I58CSVu8=z-yԍff_jy]S`FDw+aV)mdcIΞX_:c?Ras-nZ78v= >Ftݷ(L[׵nь٘r9Y 7ܡv㟯}&jvR}{g=c9"ٰAkb0ZN/ŵ3W`yy\Iu< p=+w~1%9?&O'JE}e_m}tàkwJD94g}$s?Z]rmi\/^w9 \. xUy:COY{ɱ=ݒ^+wߵW;ƔtnÁGbJIī5c$mnM |^((P'ΥyqV(,ڋS,ft=)UKV{Ӕ^M+Aw_܌4kFNj^7mrZNOWI-c?=O>F& |7xUQ0b4NbTEH/_gRVI?io>Gը){f~Ѝqِ1%57`G)kbYM?.k=@ތa RL?n $Mdݢ/>!fޑKdawɵn;y$]fzعlnUCoc2g㶼=!N^X$:FV7[ɠSkX? ٷgp-MS5ubkwp\ f覣\Pci±du+hw:jsb~T~ӏif-/`L/>Qs'n7  ˷Y(a6V A| ^qmfVoc-uD[X-:HYPN6ĉ6bޜSj>ϵpX#tMtAy.γ}o~eM9!tNVo\*۵ѽ6\O|ٿ H{ k$Esg%y|6=WmLCsyT?ݧqQM'¯Czzi{vʪ\QRi!d.^s>} ۛ M_hmn!S-6[L5j].[:oog~+YV_ tdמ ? % v]Re콨눵Jit tj ڱ>:E ?ɴoUrAXfKPCF}qN%p4g}?GJDRU-q^Q{NmT5l6IrA:9\io;ᓉ,),){,czf"bnT3J$rT)-dx|ԦXm|HNv{׍kT frò?X#FA[OVT-QK[}4~QQ șs1 .k~aOzMmhɀߧJ5NNf @ʴwa{bu)ǟtLf:6vƁI4/᦮NaYkiD;NZ)4h4͑Vm=*:@Ť??qhӡf_9Y/q_cShh!{Egv3(iݒVsoܗhV69|fn>t44 <ƇfsjE ,UJx6s<Jʳt($ ضJ{i!JΛkKC#8rPY{fܚ,QʨM^&ifѧë˅2:C7L\@>`[xyp>:}F]|2BD>toO:]Ze؋X7z\7[BJ]P/j)NUPB\iV(]{=i^qlh.(XqSG_sHд _< Dzf~1FC=Ϟ݇1t,YiN>;3 w|eyv́"NaʎuzE^oŔ'JhǂY!zu_A9\g y'YW՞1\"z1M۷֑0Me8vv'(?ò>qҪWߺ^~\fjAԷqŭ,W7{.5;sWϫQ-nngG+y#JՒjUJz*7Z^g*|!!:"Q2=b}MG$G]qڙƭJan,Yܯ=߷3 5{vzxc:iuZt>Kzk(sIvX9TpqzNshCΣP Wu { PK ަ3q5R[Gu~L:y Q<ȦqbJVpMM}Zr !lhUm ox:8tv*C:R]\‚Vu@T wcy:FIe)6/p P8b3=~5ضpRDj}M%eBx˜k 8hNgpeɯܖp[j]l#-<ܦq5ҕ-k3ztLQqJ+>u0Nt'oJ}B^У9ϰi:-Ui[Re1w;Zc_fUӔΚ;nyҰ`'(IyL8"|~=qYxCZʖ)ZqEʴo2[ļ)j}q}I\9nMO}*y=^U3#[#UP,ۖ/K[ӨepPW1.+8)$M 5EkK 21O ԊҔbꊓ}xfMN' oo`)\J´ҵ=E@0ZmUUT u--z:\+i4z~Nqr=R `XV uM Uݜ(k5ghX0y`O\ziQ,!h2edhWjn=y+PP10-#8=-\S`m0hǟ~Y\*590.XOkq#s=Io彶W{x }%b=Ў>H4S"(-i ~w2"gT. :r&R=l ?:0ufڐ-ڒQf2ʳ&%8R8<&R佭u})}>z\& +OԮkqS:a?s}ڄ8Yawk`9СS [Xcqa|ԫqI[Qc.tb9tvmEsg#abïxbk"_kX,A ĀI/S@IpUhn/ЯQb9 gqVvwt%ѾЭt~wG9 $bӖ`bY9M4Nޡ̝Zˈ*rmU|gvU6=k$TP&^y%GoY*uj7ȳ9 Rn~e23.X~&яHFgp4dd[o((n eFheSE\x5^ ZtOVu{w)dn㘍r Y,̬;d=tmoK%]vi2?؆^m 's?ySTĈ٨("mNpٱw3_8يFMv7};o.5tΤwC.ws~B҈x";[y%Ϥhy16SQݻchL/7&<HTwuX΍7EV?3dݒzo(Z5o .&wN?SۂMaՌn5:[.U7Fl߄:]!'˿eXmhh[-yS|ҭLհ lmG Le%Wr xpJI%7g~e\ȫIi8Rp:GF T|6Ǭ4`- 7oeD(,sJ&ۃr;{mٵpZO'uuJ[sm,a4Q̺okM:Rk7ZhzEsx2evzw5 7N 4螢Ss=yzcpl"W >zvzBH_X{e Sɦpunk]gkr(a~pb}EZ"|Wp:7|ZS3%Z[CZw5^3C[UG6Mȭ`Né4m_57^+#h^ )Rx^k?&%̽ S_afYѬFDwwGv^ك?resm o#X9W z^:Cϴa Y@%V\y׸ @8vޝ;Qu] .D֭klXv(`N+TY^Oħ3w ћ6AWe!$'H[RZA^Z&mKIm1;-9 i_|g᧳;%x!~եN}kۺ;Y6oŴ۶wp'%l^#uLJ{H]r+zi6ܛ`=n=<*7϶YA'[P[@$u1UhQnbôK<<ݨ)?zcjj4ۿj[kQVCAxС&)[5dzy6h{TQhvp҉/Fs6O-0>'|X'$4rh}O4Z{.hqw\CeD3qw*lJilrpܮom*eX!y)OO 6}KV0QysAE1"s=KlF͞c)ӃA'f=xXϴ./ZvMm~.f'Yo/_quF8^uF Κ НTZ. 5Es[US[lzel7LhwHJEӞu wM\apI"^6j:՞tXǎCͬԉJ};Rid9|QF\QW}vA1;EF:lNY+}]i[D]͝S}:fAq[xr]fM/dFlBȅʑ&#S60/$^1P7r=$ño2ǜ=Dl,V-6}N/Z{lDj~C~]rfyup)ɍ,:T/==hƭЭ@1%uBI::1c 7t M]ˎaVUTI3-L8zaAgνOq.k #Ijy3?ηۼڛvWn Uͪ.Uݾ|IWGh?>tdIMfg L9_):L 3HoR)qil\bNՋNql.k&Z#zqB:>W9?Ǝ; ʎ_=MIsfݿ"?wœQJm՝CBԤYpAzIh㠝'bPCv٩o#?.se35rjs .T/ht޺F^ @ l;p*&k}ϝk_%ԏ~D۷:GL:c-7Nj}D=%dA- =BFb==8_bO{x>24ګY~_?3Gv}Łm!t58>=8c!z>UNoGxEh{z0$O 1w̷ܳD+UuL7ELͲ'HfΖT^1IJoRVMN)'%{rX\ï8^|3AgRń:N^8iࣷo7YXW'5+D/UjfyJN^tCW:ɭVJ^x[Q&ڠ!Ks#2LRY_ ӪKr&MHަ?3 1組H#B=W/޹Yтt_Αx-'J oTUߐ'lNɠR bisw$1"{5?hfjϝVA7yĆձLkJM| .JAAvnu 1 ܒh/\Tyl]*tږ)Φ3T5n9!㫟dtxzZ{wl%z9;' kN} z l;xx6DPE ?ޞRnQЎIIo] Gv]|jƶ?>wAS}c̢Atd3$R 3DFκGBgƧLdxyM$׻([CmTO^2ʶk|{<Y>hr.zlϏ~WB ;jOSg#w z?rAJ#yJ;#3^T.GHyω9:Ic$1~V"pc4`BvYP6|{x!ntciicATrCuCwNe5$Yâ.xB+2;P:ܚH#̵T \_YJ wKzVs?>쨀LWvZZ@lĚtǀKFKi=-F|F,CViD;xy]v=0.d5 ȥ#0ozߣYӺ* an}ѯ/{(K)xRN|h5vUy[A~\)}_Mkln9b 6 ?2MIQ3u'gA>JQͺ%hIh?)h  YuBe-#h5ԉz! }W8(Nоs)OJBQJ3U[SFJ$o>eǺ,~=hbȮ(PZ-%ŕ i(/.ܱgorҦN-0xYi^̛. MHjF =v׿q)e(:)QX3 2ESgn {\y'e?? !B""4|"!??7>'zG(?ˇ!Dd8yy99a0ypCW@4qh3-L iC#06J3DaqF0[kbI'-4C:rÐVXR4P,hs;aH)0}X뢧"6@&M[ `H3bEa\W

e$ hg5ԊD[PFeҼ.#4@'cɌqHG+- J̃ڀm I,M.5lWf!(`%gŶP@(䳀8:O t| AxH(+iffF*r@P̌$|0:SϗG67!Nme3xk4| R/1:Sk'FXD@(D%b{"L @vbf@Z`˒-!L'ang lV@1( #+7S (Tk|pQNtTZR8"X,DH D#.X좴*<%R`规lh{j0pH;k!}E2 bĂj <9%_* i H%5C% ؈ b)5+ XVyʠ 9,nz̟ydj_e>5J%/Ǡ&HE%JʢiT8QyN ]x a%K T'eQl( U%/JrJ,,Z4j$L"n1_ ,,fBV od,-BQUAj8+q7 A |q~B9`ou&x/GW Ri>u;vpSvb5̫8q#.7hDRXҪ5m4~:; *@ '>.(/nBqEAנ"P2 =@ߩ cfEi( x@@J+Cʃ󀄃my]`7,ѿ ,,K?&>q&j0kb"6:CaBs E8 >,* xЕNnk نжxeG7on:`\AaIp8/'J"%&Q%!F VE lMpfWr:tbPbA IC,ft$a Zh; 0RJ>jITTM #@K s0 yRpz4C6v{ /8&TJ-"QH(}J=EQ#40$FhŽ^ /G{<^ڱEX$X>-cjZxGf9W9F\CDc yQJ0jRn;.P ~mV !@_>(zBGD*'#T4ˢC. H-G0V̗FmPHG 4y 1v[J3*i/_Zug5` kAp䆃 l nGw i ?o,c¬{ChJ2=H>S7R&{}a 'b 8Zſ)P`N[ut$ Td5BTe,+(f+eS#Ì5EZ-cErI&(P# e\w+:(qC#_Un oJCeuc>RMj* yB1n+kjTHo N>(Xg @18 pPK(P 3:xD-Z@n8 4n\Bb) Sc,}.2,06;G1L$0^FZq!O_RdwX<ˏ1gHmؘk"@[>qvQT2ôlVv.0d-J-def%u _L%/Wd:4nYb?Ҕz"؟WVB q>#hh 2nEG#˄yfQ:ˏFw@3y@.95hI[68a*dwb4Gd qSI+qۛ JEeDS0ɺSCQ?cDTW}`xڒl2IpEYvdE-M( qIXP[Ɏ<e/GΚ}>k$27w@ցD 瓗y"Xf8 =w+5j-ܱ.()!T'P4aCؾ5>RTݲޘ?D"%<q6%G/@>Â$M C?LzxG`=SyÉVz,-J=IlX s%snG/A堁yƦHG#gA==6Мa0^Y`qVX)4 86 ы"A<8MzhQbBECZ1, _Y?@+טÐH` JyP`Hh^t` LƖ9D mkP@0 @%X;2}&fqЫ;|I8/!oe !921NŘ(q$m& IEзu6C,8TY` 9aɅF[$TCZ55O,0zP2-I(d\KI//( (׀XDbI3ML$cQ p{2u "$#!]tZ:/t&cg B1Hb[)&Z\[rN) pǟb M>^(t@N -Ҧ2bY ą8]Do!q8HʤD9`QDڠk"U@~!`fC(BAH`ok H X%DCyӣI2?1r 뿀 wѴCj?߼ߗO/*#Z)q@B9DL䑰E;sb @hL d(C{+ih1~e$YP!ƼN_lr ~/ȵAOU!'k("01&7l)%$://) JE7fuUuOBs6G'zwh"* ?=f/ =6]xIOc;{L2FO#3ot6tKym ߶Plc?qD???_/^~ _~|çg_ Q'|vIC ;|P׮\:qu-euOť۔Z>F~z2V?XO{miO!,7>TZ@{'ihV!e6HӸI{l /iNps tYk}>~z5@kG1&Eezʟ8ǰkekZCQN: f6#SmY.➪YsOVկzUU -OK(Xnm]P1@cCȑJ,:UxcGxkVx3X}-!ғM7l/? n_:{2nn?q-^5 wsu1?f"^J.ᇴ2g-m㍠,k_lhj Anݕ,cځZ\dڶ#ZN3lۛ IMm,׻ rz.n\ESu#BSoӉnNn;< _\QS*z`¯gS7kv,;5 k_Hr]C@ҩ~5=v\ws?3~!;uV>QgƦ_{ymxZ0w>/u|#SbĚ Mب3 &mn:OhW Ikߺ@]PHYOKѯ[RgJg~9Lu1uc2%Kro99_w^o3n^eB$<(!Q&579~곲&Coe<AG></|_#_P@俓ui& #?6ddApU~C7kqopxBrmCSv""^2(;^WѬ=uM9oK*8>#~zk,YJD],aҰ1~Y [!fͻn맯ߧUԥS}\fa'vW=|<['e6[;MuԉO荲1EV^dG͍N"Y|ibq`щ~(1ur=|ʺ%ߪ'lSթKCdZ4>(9.q{A|r7W\e}=/WX:ډ51XZ/2R.њWlkVaXv%?'d!+brh7*͏TvX-pQUڠnZ~6N=}'-&9-\?Zݡ]u4/=f'%_=TH ǔswn¦{C"MgS:J:Guiϙrɦ>aU;kv;o$?:a%i9 x~"J06xK=CC(cUߧH/.[|Z1oR\ez)mkwNx#:{bb$ʭl>yT*crAۚi&Zp,kyk֝5F_YK1D#mS k.pDZ>n91G&^f+~-Omʑ;[[-t? W1euV(^8C2ӧ};& _q!O^^]/̳k֫<4:f3N(;CUQF}{۞r:X]\5vڔHqvkW]ѩaŤ$tUk^ߝ%+`^kkp%e7ʾm0O[^9âOhp jcpg{uNVD-}2z>縇I8mjXvNӇm7`I:*o5T)c.mً9J\DracEr]Mo0*`>g|-w׋r{7Rk͕՗5-E/ڢ|#;GdpZ#}rYIp D"^0S]yIbe5o ~LABֿDE2wWhlh]ҕsؼvjDӽ 3֪ZH+Scr~uiq)  kԷo; :Pr',xæ1A~*ZOj}:0UNQs>%vT}vJ"ѠCZ-_"\[%&Ȼ_ pk >ϒ8陒W"MyeJ3g f]glQ SL[;m7Ird%,A -9@$I$I2-Ehr%罪3SW'*S}NY{{w G%$6'}hL"G*$Q}s$cYς]&Cjdl94!/cXCp]7ƣXO{4œ6υ|+(06 nՏCpa֖3rEA7oq5UWNG/?G[RKy7zb1WVla0&ʧFYv0^7[`/Yg}յbzT-k~L.e?߲RZ>Ao*MlO? VH.[ydf),;sRlZ/LB՚KmxK ]j9۷݉ѕy'q:VQ WvhzRImIRC*nRh|3H$}6~^[N8{q5mt@ɽz.ul-El=Fny/(إ4DnSV-›y+MmKx@`ˈi{^-~;hlm=[ɹYNDî=1kH ƗE0n8m& Uf^h#0MDM5 tZXa͝.U{UOLO(YT(>|(++,9 'y.lZ;xbܜ *75^;\ݙ2# ļqeDF$0tBNnpD2xB?ŞYuU}¹g+! if.*um.0/1[X-g-/u*2ۡy}>woQ eS_(I NHL5t&įe"& jCuU1Y3NKra7r8; LWB06X?z1[:H98zPf=MmIH?j75:5s'siV, >ݗǏTN3|¯RK(ˇ5WXth|(qIpP4-Xf]Xvc۝|!-?ާx)ݮ\%tr]q@z/Je. =SO@"6:GݳiƖ1h5[s}#hϝ 4"a#7U}$`է%?#xz4cnV%5 ] uoKc^a\sb}-M~jNTBn/@bC"`~d=6 "׺Uuy*mKl~w&%;KΏo)?=? 忂黬lġu:\Qҕ{ "7Hbz4(Ӎ~] Х>L颩wĀo|?.??vc-$ѳz6>8Mޡ] #~{/5/XBe_g%ȄFH\rBx&D$Q;Y1R(n>Y" )ht="h<[[3 $-w%(݃ܞ'fC 4Dd6uCPYPRZT'(03=nZLg^c"2Ds8 vٟ6qSW*R_ܨgp`hn+ŷy.ױ1eYǩ+dA1)7ZQ]2@3 ;>-=o{2} zWM]kͯ>ָ} yCj 0,62S]bPf]<p="H,Џ,*͡rxq/6aJs}Ͽ?eu~`]pG@E \`>zFG6孱C!SqF1j2:#/RdD毴M?<^1|]RrjB$s`6dF38WD~xelXHibF>ה]H8H5424IY`gXf&f{Sm3‟~zqїӟdao? $?@SOs_uClaث[s|RV`?Y,)6fh(CNiuYmqCbzAZ TSM@kj2c${Jا2injY Y:1bIw݌\K~z/v]Fu)\&-!l"۸Z"+P2 R=Cz1f'%?wy~ľk<7^&>VM}D2YT,HV?eI`=mX0^Me=WT4k(g)nFɍ͘$j"ʀAD.)b乥#J&ThùlB=JA<05˫0ì"YbSf֫jl4oUbd/ _T31 *z]ZSP^3׍h(M }mܯ-'a2 5Z!wO,d%VgJqU͆ *љ Ʋ3MHGVMAG9hqAg˔2n 1şCJѷ XXeIm:"raN ._> 0$B`voVP.|9?4R)qǙ:/R'B&@w$vމ۪矁ܶ7, /̱˹UIV;S4a &]iym.< 8LdD7csvřg~à(jVnJ_wR\2fg%Pn̔o01fɦnqLk:D W.a?9 ;54&NvnJ!6nsCJTlaId5drP8ݒ䅎[BN,*_vξ-ˮB%>bIxa 0{CDzv+WCwʇV0H?Yǒ 9D9!WI̿"]ޘL~}5S#/z"Ýܾh|TǟquNG#P-ClZ}m$%ztF^Ć{)MfwJL}7LrwJɍouc+$'M~R{T +EP)T@)U:X@ҫBo!4Ji*$N^]~-?g$gf޳gosfv&YOط1G^uܗ x ]>ZuB8 `+Au(q6.B=&J;=:3E itWXv67\i!iWs;|tiܠE{tٟ&MZp&h 9?EpdLwn赊[ܸ"g vÑD^ݡ^l/"t| gȚ(^sKc#a7-7lO44-G5&^'>!N,҈-7YGP"l՝o%,;t_toq>+NGnu N#9IНHm;[ЗNp/"9*2[>Jh|RJN0=syZRostJZ6[̟!M,mT",RKAxT55GF_1B>kIB`ΰ~wRz'+Gq۫Gi۫t~`ݑʟ$~TT;~O.^[KWb@M1Ï\WticL.JRUd"]y Du4;6 .o!uo3~O]޻uo<'֒si7A5t鍗6ɽ #FU*]^[}6СuY7amGQg;u37M@k53 ک#4 w0:yoVkeo]rQm/ԛ]{c#}C՛:NbBWW|nAi~&WQˬ Q>iA՛Gk륖!9MS=;wnKf%LrӕG]u{~ymZvq!|TjZ߉]]H1T]jk"wwyeV퐂*:㫢n>ܬ؛0iot#_4FP2nXRM[y| a]/["w;#]e# btD?ta` ibegM.#-XÖ+HVW8`>\YSs Xe}6Jm~3/;ﳰ} B-6&itєyQC ´cEO#|oBEf;&9SU_Zpͱ۬YE2!)cCg;;LJliRs?uMAxˈ>L崜%m͢"PzRBlS߻[X=TM܏a㕡2d&f"&T`lz=rZ&,-/ |bi`Uk'DC| Pe2u7Wsb{2| Сo%oP) 渄, e?f=Z@ %i=j+Yɻ}Xcp3VRAep%t9N1^MP|b漶 H(EA=)zcF y`z\)SNP7<5n\>*N4MB{eYO9FxQ"!(A6ߴTWg:5Y5Ƴ|>PuFk1Lj{Mj`x[h XuqF|vK5k;,yFAMv*Ý)R$2-RtfD2']֏DEOs\c}]fz.Ï4TN& rtyݻxɥq,|1SHQv4/n80U^65BF@( jxB}+r:Da'8_~,T̙yqlOe)F&OhTue/'wcF^<;> ۣ0 x^-r|avcNVS:4|tbFGhG^Q㿲 $66JBL/b|Yd_J-)izO@%vmQM\D7jbij{Ca1wmJ|?A>uNCXͱwėif=/1iv]ZF4xWܲsoI*Bc-8Xp>L(n1@{.X .N: =~(-YJRLӺ/ZIrCmn")Q`XNMK(/t ݩ0wSɫwY3Gv k2hzOd 1N' 7w|M^G`WHԮ4Nu 忾Y3 :`tbWaJvҊ3GZ+2 ;H}˙6GYgKRu)/_3} 3\q2nݙMiJ ѾvJ_ v3Gi+qJvuA'Ucgm8'bvo~Y =,k$Y|.ÅldYJj;׽+˽b\}{hvlx5ی+#3L">T8D_<e(XWYrWJR5Y~t?xOyvT5M+/d&MB67wSVtUDr1 Xk YQesZƈM,E#|bB嚺VM۽BՈMW|y3b|]hꧪ`v4nrϷVƩV>u瞷-@ۤmQ۟-S-E2eI*鴬P^h?Dw yC0A(udTN1X%l BbhNWigSwvL3/{¶8$fa 띞˞$@71Yw7SOFfu(ֵ Q_203NDבGN%b t<&@h WG iXjЏuB۳_Ib6D}_ 8&}D=eSQ_F^N!(H+:-W*M԰uM_9929bb*sERQ@T%hd`~j-.VzzRͥ'.ĩJ2:\~wQrkL=4GZU}7eߢyD ~8&gӿpo?"kO'R9$8_irŒ/<ͨj9{$Y: zbtW=M]R:_<#F`}ϹgŅOd*dTr4Z`6,2Y SObWz#}w$efڰT+Bkn%|'8=&LL㖭$a΅Rv՞ط+ߑuqA Uj5vtZ U$ g1AoI:[*@5]] as-pJeSjْO|WUQOs{DxÚJ]:.㖁("RNC.~LiS wpOjϮgI v;#;j6^&JLmYbXtvk {Jrc1Mγ:?k<'!gͬ]&)ɺHLGoqg=bG5p)sgE.GSc &tgc&.Zc S\.%R7Χlfbi۪\&:4~eM[վon~* RGNfhH~Bq*sQm/baқ՝6&Ot6I%nh)͖=;?t٬g2SZ$Re'ZlD4R3YU]12噟]sF'+W&'e<䲎olbV[v|G&Ǜu`Cg+N-GGB*^fp 橫"⑌T-C+"w۾m5@rMQP } gXôt3졸wNŏ'2 A[زe}/P--do0̧j0h]P/j6M^=93a^VX+Yxs:c6ƌG%Ҳ_%^Y~pe48I~!`58,jLYC[jwˏ:e}WUB 1Do_&jkRrm~zR;ό7/q C$ў~S*2ov |vn2w|i6>M6vX)s(A /۷UEr6:XĽWCZ77wkBql( 0tՖ0*[YTP#@&3guywS_TU(Eלb /wYdBrJYwo %(pL 74UR.BZES٧u %ѷM1 .D-~L)ٕaP]Ix5%j[Pjsݹ{޲}6GjtFޕwOw>*uNMB[!+1b#DF5uSͿSXQp&Gz -W SX`R~Uvu/j2q^mQ15\C %+tiL7٠,:bhCτ*/S |6+'t[ǃC.eYܺIu5{~9BO$*&'fn(^d}Ӧyq]yޒ`d{_ğoC ƪ}.MHoy}\~ u%/@4wLFYF \ʣt'i*d+ #QCЧ'e[O!W|qJ^rrYm nՊEQճ^d}j^ǛI\h]}h6>xS)R(X %)7Qiډn~obi,>?Sͫ cN~U**VtF͆{|GfOZv6Y17}лZ385󩎹-Hѯ]}El=s.p$dtvzbakQdOk~TVy Wnj}#pSy򽨘F@X:>.lKkDu^@ݟf3)lS/I\+tfW\l/]gu7ݱ8RIX{o)`tA\s6/2c+9$02VS޲'&5_L_F)M[t74xfVC)&&9GzꝱV8P oV;}sw>tlC"vekv]=0ړRoWY^A_$hI >:\J''*ż.evNu0.P뒭vᑤ{fnw:lhw꼏;l3z?䐉_@? um«av0ۊ^O%nh QI+Sľ[䭚Dג'U35J7\; +߶!\fs' *Xݑޢfӟg~AG9,8D4.=6h 8zmpt1Nff׸MkCVY"]6;WJ:%{(a jk<mIzV_UE;t\j*wOnIܯ#i%hߋ=%lI{hWҕO 疬*K換""ѷ*D1g5C13S~_`^~1kpUbNJ(T*w#2bZE(f,N创iKP7i3i:[(o '|GȄPL0akKvrW$.\r vիn<#UVH?bGb){ڷ% SaW9> KfiQd%h7(g2+v햍\v߆0L2Gl <DEM^Ш鐷 G4mR\዆UlV|H*N=[6[bU3q~$ -J?*cȾ\/sۊ. bc|%w[PN S_ʓ(^Kpo޷3`~u9)]HvCcqYA/Q]&'>N[n!1TYTv$FPxP\E`ûlIݯ/josN&RmH"yiZ2={GcټME+q"Nj2hX?Z읓aW6OruPwg W|+S67ymv!j 924+B—rFXsi)$ŗ H>%7 SAڰiMInSׂ MRb? >o;\flKFm,LΛ"_Rw=? ;dK{ ?7u3?X\RNf2\!KJ\8 ^?W%rI<2cq}.Ztww?\`1~5w;߸\].ٻ+Vݷ-_4xns{A߃9yL`ׄ'60wqwx/[u|<_ /r7]TK?Qonfo\Ջ|-_ FF\?8ɲq'bA\6uC+9Goj}傰=Z8 |z5'yKz?#W/Wso"V&E0(_+< egm,v/~aMs翈{`Ee_8#on;?{~ǒy($PTLË_EO0~ {f/nB?k_b"MXPb? <<<#kGsM5 fw(A '5oP6\+2E|u v'ܴioar+9fj{-ϴ֪#7O-4XJjrIεQyMNH),,{7 NE 2RR_e)6Fjm_~~4(~$b_-୴axDv [ӣX1fnV_O]oΩbDL.w'U)VP~DqN0n%3z' z$t܇FLdXB5 SV^-chHԻCB^wU]]]$٪g0 ~N0u_zuQR"b7dnj r&;16nCqup{l;9_kSB%W"wpQ2V+2C$cv>RV=QxrO!yj;(38xу͊VQ8yrvVϳv1_ (ƿ!=3G7zֺ,v7{1.KJ&/;X5gu˗4*%IQdlMcg7 gxW%N Nv́bDͤ;u wTrF*ls:MqWE&ޭ ַZnLzl,Tc')_-u`e >Yc}8t8ݟ)봍;7 Onľ뭶p 3Rzxl+%קr>A~w z8=&u2[yF=JAMK0t /_dzj4KPveQ>~|,=n7rE{Cz)$8j ɽ~|R1zuېqnhNoߪ6FBU7XKxHRٝ"6O=l f9ʡiW[̤';PM^;Z.g-ᅴ.@*]姝̮^/b͒y񩅩rz@Bʽw$S#O%VYMYo)"IG8҅gvmUtږ1lnEbR,!P/77Ɛ⊕8$=[aꑙuㆎѻ%"}׳N8*#=3P_LGX,ؼG&l3ͻ'bG0;Ycٟu73mcjMbA[GE rګKKq2_rK;6G}ҸP%sۆxHb/ K@wi-hˈ}TPR7i33^.|AWScCJn4EjzPMZFm`҆MY`6MJ8n̋VCΔ1vhgB2 )d Ht "-& πmsY`4`! t4`چņ.&QGVQb P0zG{E`T#7JvsIv'pNb r~qlvN'BhS /2׬rQtਂls1qRFg*?-&4D=G%Fŵ4+U/҂.~d3[=RZxӖ.,g"OnluDM;9z;i& wKM8vX[(J(W##u^_$  $ϊ>njIR<>x8S_ɇ^y?/$êD!N1svڽ" ;Fx*8nղ2&)Mn|(ܤvF34 K^ZkIi˲/,~ Xeܴ&:^.b\:ś'Hnm}bᷝ[і&9 5IhAs4n n{VDʗmo;UgZ5:W^wےEzG [/\߬~eH~q+EV/)Х 7g1o=Y[|~fGи3# g(z_/W,X2jwz9CfBK#[|/<'| ؕRnEFuR'c.x}%]l[)TA|VFtڮ3n?M,yc"y&"BYf,j|:jjQ﬽*\h7:R-{F3ZjCfnڼ$|@(k?""b ; ͸ѤW9Mj-uS$7Ep)IQJ!]P`5E#w\T=cIYMA΃eM2 ;%ޤ!jos-rǔAнtטR:f# )Yy rB`+v3gG4Nd3ю`k}E+YyS3burF؈0JQNQMcD>"?x^ܔ~===5}^arŬF̫U(9vm3^UZ{?W<ڐX[[ϐn7xҾ%}IrlU\@1~Mg˞$NjEiU^w1Y3]ހ6YÂZvYoɰ<m|^+`ưNsS\vG;6>Lo m  T%W/E9jIZlPb<94eoJL}Sof67rSa?jiRzelUF6k |EdMxȮt]NsQN*% AcħM|&9XDѾͤWYD%ISvE0 ɕne~tpѽ&y G9$jj8NJG#Z伎Ws&ґ,{z=:7 Y-:Eܟ|C.& [{]tƧkg.=4i}4͝Ww>IxtM&Bq0p4d|75u|Hڋxȁ.$ahl7!5P?. 2'yo:aBjr/u >:W&R+uNuyOnJBT XCYH'kb&LUT`4x,|VآhͅUIMɈXv(mJc2"Qɀ䕺|3fX,4Pk|42:<~.Vtc>&SXo(Wn5}%`ݭi(j pRmWWoC<ز/jJ,2/ {BkץX)c뙊C|+Rx^ |0m&qS1v=q^̅St?^825lLcXjȁ$ʮh\8V/k߿ *L$Z Ys1ށ-;4l D-aiUI#3Vkt+|܋[LWlf-M= {.Ily\U>0knE8KMck*Cf^A?UgkڈSs,>ȝҘ>W6tiA5l[*Orqyk E<'MQ)T|79ܗl^1ir 0 /<x()YN9ܘKb`^.u Œע5h6E18F֬ z=eŮ;LH#x&3[iKRuX+\ԡNrkmaݛX > |l!j~ /bTF"+(xZtX<<"H"~ghuN0Dz2+5Vѧ&V~i>a+X:T*ܔ6&&Hٚ5vsJ;ƝALAMr x)od f0MgDYpDPpgoCnWש~g(L{S; e[&.u}!=Xyy61o.5X- "=*'ShD̿彟S{[= SB֜Uۭ{m+>ah2iE#nG=Z~ nvh#C/c齣7K?gv=%H(Br󂥤N篙p{IͺG:lO*[ϊ|rOـlEeKT$5M Ja)W|=?hd;V}0UJ9BOagJS8ĉu+.| 3)t`viG]Ɩ>+ #l6ڴkҠA[fo2l-AC0y*%ݍN;1׼"vRjC?M>WN[}4'!\P зj(8,:7ue˝Y":d+;)? w sX{b˴9$f+Kb-K]. )|.1=q;%~EK|sF iVyuדFL_E}o_l1T|GK ȓn9gOywKhGMS+S2Iby<J٬H'=)W8 Σ}I?C۲8HϬzD 9k>&q!1%'5o#q R ;I$ v*s 3->x<9=G, {}VS6]}b?4@>ugXuH[<\|˹9a 0fU\ʖuj+>ca[asis֑WX MQ h.JZR8UqV4UA#ڣcL FH\ z&:wQ. WErǟ'U%KW֫`RҞogeo՗WȟV lpM]v{-IڧrH҄Ob<>QnTG/%LԲ`VKg2H*|_Jtle3zflwteYɨ#DfJ-I'tQj }ȹəlYoExFW5NGG?P {-(qcL݃¾G8+@tf;jXY3^7qHQ{gplzeJzZo cgunQy(7"| VihCJ j6L"%H_I!heMՑxH~-_95WN2v3X-&'=EW9&*>~kh]>`<Q,) 3-Y:+Lby=m(q>S/8lz1pNzQ'9h܋ZBfsBV>* RD1?)SKP6QMXYfثWQD{3ѿu`$~9y |AGBb"1ѢUSo.y#{M ɱ),ZfO ԴXZ%4Ŷqxu#%"ےQV>돷'dwk.EOb,&+r㫑!!ʣ ՉHܩ85 tJUy gM`Ө|QPDׂ#3G&;xfMoW.{JA\L} "MA[t椢7UUb2پlFjlAl )~Sh[(ۨBުY \Ϥֱ)=^UFwʷk)L1Psի rYӖ!gB Բ媙|0qr lK1NC-a-@%{4:6hu5-m {&|C8I$2l~PE w(Eoyz^&+~%w?bf|nֶJWN ꉝ~Z O ;aq_vrBU+[zGFثC2:+oL_8͖='qJ?ߙٯ:\ {(fWے{ ]s,QRpRolF;^ͬy~zmHռ3d:н$T,Վ `iVpWDبlV"zqܥWK>"478h[0K.͸0GqtʞBס7* i|M!li; [u1}2GL $ ]ުr1 aCRAu\ixD9ٶNf&_4ĸB@r[WS\g,o6ȑ7&"{EK~nA7>#>>IcE 3Y$ޞ1==˪%!Y >`<3 -<ѝqx:PLQ)nW 1p`}pPJdͰiarcT}VmLKm# Jk;qzqC`⾹& 4onu:4T4OV35 g; ohHӢNs孡}j9ѷ|E@8 ڣp#f=qTlp "Q[@ggZ3@SvS7 0afNx=ho |:xbVo3f " tpx+P4fMSGV(QMN g.wLڔd vݗs{^Ο ŽŴ6Kqr<*Q CH6ڵ xDP+ikp况APC6sϡ CV7:#tsZ=M7tG0Wǡ:r3~@QkPEmyK;܈xF,yx?:\T3nkbhG2j^AQ3]Z~G_l `^<ҕM<:Vsn|+߆&b?Mra_j}"`W!0U&cֈG]b܎9cy)UҲU# ZXNI?G#Xh$i4*i VȫxbR9-%b}4MB9LIFB]UP3eUZJ-hte<Bb*ő<}13㏞>1ܞ-Dbce"`NwwUNYʺTtG uҫ\ϐ* b.9mytʘڰA_a$[&fU9ԛWmpdLxw -op3wSE'f5]:=-q흑&V_{tS^!1?߾פ-ofH MJ:U8VN/s> y&Hykk$'hr؛}"#jz6y u׋ ӓ5ıwOiRbZu ]j7لԁ|ߛasȓܵ.b\M5L_H_o(lOoOU̼ {)lCψ&-tb]rzֹ">fjՑHAJoyNqJ^zAd^)Q*}S Ч[ك0=Q@J4(lWxȬ5MCL6K }۩z&OJyb."~`^>M(2ccV;. }eڄ9e}K;K͛''m S3 F+M|5Ew)asjB9HĩF-1݋*TR|SMhv5xj#kJt{o(W/-ꎳEf޹40&7l/;Xhf̈́!!CMTY*8e\)':'8=ٗEڱkyzꃤ@͂2 _Lq䉌ܲ|nMi_^4{*""bo$ KYS_-bIy#Z~82&lzcq&zb3Jgp{8mj$?m$M,FP.6ZOi>m=~x͍<ҺHB^.BWŒAYE^O̺6{g8 bNs $g-^k~Jq!=Vỏgu@4brQ1ک%Ҏ%='+BU{ ]q![PlIS+:*y[_=;_E@ \VX {G9i ͟7FD:_=+`$UL즑[}n9c;0A!&ōo(NEqeG ${zx ҖK!ZcjF5a?>SWMY(c\\g~NN=t5wȕv=q(Ǩj޸e:۹xĠ1u$&˺a FC{\|$<*E!܊nA'\gQTC>rV/?^N4g3^ ci> kD]U~Xfl^ߑ͏gHeuHD@Ͼwm mXf*YlfAEVtJ!Y7 /J4tW )qKvzdCjYJؗBU|i[}E8Cu{3| ?tp*hBrČ|.yȅkfuǤwWgd^<#35S:Hfk/\ƌU4A5ky~_w/fI*O놐`Jw#諶oӔ*4"$ӔӦ VL^FRh;—>}.7y7J>YDNy+lKosS{rԣ}_ZOt_>l_{ͳ d}Nfk혓V]lJ,%R^CX5߳q6IiAT9{~ہT=64W*cbg,$7cH^ݠxxHXoH7ac9$Af3Ɋ2-lu"dԑx^?ʭ/}EtL :!xEOn)d`\Q;)^AD/>",sw! 7ǻ+$ # vOw, px P.%F~Dc_3C%{}K,-(h)pΪ 9 7BVfItSٝ؂>g*L{p f_LMMiGۂ3]PSG .MI[J>R紟ō`dmȌ,{)8~j$0gPw$n ]CdNI}VOV> *!Rm [ԝڭ?RYSK W\2,/rT.VpLB&n8/U7n;=S.כ=^|@ljvrer!OL 뎆 +7J6#[Czۡ!Sǰ`u3:k7F?Wɨ^Il%'pkdBŗ#Ϝmȇ*n/U2 S@;&שa-x,2݀k'{X3,"`Y,-˄Ūs@]tQy~D33N@qMAY`;5>O A gvc֣ ?\H{\[#2;G-4ՠ:p *'##PK\OǘL 3ƦatEGjGCvE- S:upt/ l^ǩ!דHiwl3-\;zpC#)Aq|`6;L2D8A Vv^s[))NoR/#Hg}{'iշ$e5tW9ˈ MQ'N܀Vn_uI,Дo[QیZlKK6{Gq` !'8eyTC5P^cS 7O!sծ_2z_~t8y]HA([|hʨ!aUw5gu?LjVCkVP Z؂u#OQ\/k/YnS>.g/qoL2fdm5彔`m<=w&X|o1̸۩>jSD!_Ow12w#MOT}nGyGFY2A.g‹4&|gʀ]5vm?DAP켵9OFh6AFQS"@ߌܟu^X\MM;вù\1=WFE sr4VcǦӠhRI^`"K@,yycUHq+z)0./$BVDkB>O0!]3Qw}89;\<! k\<ΤM! {Tj}B=O4д-|<j #y^]Y5=.>v6+zO7ۚ؏ % NJx]&| \ċ|aNK'B[USbXp5A=}r%l&Ԁy$bK/Oq.YIKƂdlD#֤H̑R۩wrf>KcjJV yg;ߘ Rl YC7\{<]( Όe=%u8ģf$&Y&ޘՁ_b9}gJ kVe~B}@˗}z )lE<̲dF)ޭY?;O7o6DgxYd-yi\=;wZwEiԨ٦8Z{A%6cOrr$SN;Qn+wɔ*MYڧ~dOY1GբS&$NwuMYdYlI;Y|р%E%Y-/Kve''8 M<, 4yWsACf1˕\8ͷ6]6َ#'$Vwt`mZkVzQscD݌"kÛ#ˤ惲^VDN27$:8fgga2Qcl*Xdk;QBfa;NO[Փ8׷J?os]DOmk|Dso$8f<t 1{[ya;6U7~~(g?ୟ;e׶Z!˘?3p29hPՒ\/'VTܝ9/Őih}y尧#P/Q/biw[p{;]4?e2~&K'^jϣ;r8Gܠjc2yKȲ3GmBƈge@·?+6^{\:BE5s.r \:W>ZNcǝ< jU圈ÇT<FeO>(@D=,# L#1Aaj΁NE4mf̮7%+L,9^WG CNX18M4^Ki[qٝP>޺k\ټCgb0STF5hӜ ߪ:ʀ[Nse=JD.cQ#8::/f~qxy7>9Yc\J);#>3jfc*&@p ?PD~g<֯1|M-/O0ݲijDYņSbt#Y _f]&u7o]tƺ"D7:^)(>;^e ?U^y$ ΑWMY:5zofr*۪$Vȃ2~W鍽$K9rDX ޱ:뾟_ !?ѐġ^n$gf*?NL2 *[]kD_ a" +6r{=P&osFI\@0!I! xg.#$YȪoV۱`N|=j9H6BXV7xtn~o# ld >*KYY1_KvL\Oe5B?$7-l ؂Ei.CX/ We7-%zD.MyUŁsB,l7\8NН..mr}rLFrRˇ}.wL|h̀ZAiqd埧"/3&q#6>wtLyrcso#ڞM%8H 7!8'w iԕQ lY@W5&y>$'@~/>lVP)η*5 kͲ.حAc a tǸ VHNJiU-WX7Ԕ`׋nJ gӆ]7 ;ݞhIv\!>${Id^\umyfc{Bh $Kw#X6uX_ދ X Jۮ9L6z*df*VW} ~ޞ@pv=s*!z1Nzg|?_5F㻳Q&bV[*Q۹pl]Vn~"ne޲\OyR9W ։60uǂD,1fl3DwebHN_bsKt(1ß8ˢn|5㨇EtTگ~;MǮY rNx#ueeCy=rpYwۻF/X=52'cLEkRK_ٞJgEԱRkq(α@->4Ǘtd'/} ,Y9ߠbvzriTJņ'-ss,q=[#L'~###)*qk5ߝ$FgPb#]ǻ,='e2o[ VVSN}3(('v6`HQ[:䧒(.[[1A{59|Ԇ[[HGf.Y쓉:iسmW}0lV[ͨ&fxٳ*$qJ|[hb;HU *+v 1l!x1mX;N]k 5XI (FMm1ڳÀG$h^L8|JAx&6z(9sqAp'o_^HEށN|omofgic^+kܶlD-_'a?2o80dNW1:GVqŐ"boטrⰠ%2WJW|vӗ쩻'E}=TYup8p4YC)DROf##N=OS6Y7 *fLeGʬDe1Ō> } D.B`OE POU4*dG=eJ?9w~IGB8GO~oh9Y#0v l%9M{kx,#Jgߞ"(b5ܮQ-{8hdK^ڡƽ- l{~'?*+TtȭuρhlWO]!t@NQ逗f DAiuĺZ4( SjCvkdZym]Tx@zO @3,#\vPc\]эSDUwxQR*|f*cA=˄hRu(}K*s qX3͚3,L 3fnv=dY`j0&GX0 a[gD:\ܡ}QVDJ|mҘ?̷?,Pu}uy3(s<$e7r_+$ ƻXyiqYPI_G*ݙ$9Sy[qQCfgōy$~ /tt.vB:;`p@7.pu^}I4sog^.* Y,|zVU7x9:IH6t['#7bNL!3e[-8RrRCc3_Qh^RxHm]ުCG BX.T$jV8 ZtYg06OPQ +^=[~ԝ'%gIHp'KLSsg=ze)Vߓ,. Yl gӯI/AԪG02aWഔ=vܐMn5=c֗.](RxBomxK' 5(ح*ݲX OKPo gUvoAx5:$3YѿXQ@Ezbj`Fk^|Z;%y1B&S}9O[貮I~[G2rF:İ:lzg-֓rPL-TicV@ gLbr#_k y?'ɉՆSrҜnѣu%H(ύ^μGL>&2}+Hla Hv ?2o'eQ ~ty]O6܍@#_?,`>oH|JD譴9lv]o@叉?uyY(]P-QCZ":o#H\ /AMx/tZ3(6G)1Dg8vIEoGBLRN )+&*=GR4lY~`C㏋F[X|ZGHn}64XZWss8]62ީxb fX)|"k=o@$܌}AM|A]^ꯊsMݠ=F-xxY.uJu&f 1x0BxPǀtX${KT>~}2T 8ށwk:nAƵt׍8 JeJ% wā>0i« (4 }u| ɼcV x6h,:;՜ @e/ n?^8{mY뒛a$q . ;ח  eŒ̗h3DϧMI{,dZ &Is+eL'2Od]F_ͩ32hS5(f 8;İ氊,Pa- !! H) !%ݍJ#HwH ] }7gk9#~cָ{zlN`P!SS9;[Ҹsv˺RʉqF^&g@eQFN v9J vSoiSy7rgzˋ+[$b-knU JF^|jS-6!?aKC5Zlu0b9y"XUq鰱ipDԪo`(g:c8Z\z_ގy9h(7*\'\5q-]u8`9 VΊ)ݫo sV_iT΋u9L_|vHB[Hx6s27sq4룛JD _e?QJT a^se]hƟil5#~GsM>'un83BmfΜ`4Z3!>bA͇vAtSPN2,+h/ G<<< h^FdFz5F4L-~v@2NSѽ9}gJ/?o2yM;a%56s?łn;FqSW:1^t]o8%\ʳqSnݓLª\2+Q6{E/‹|>U2EkR XCՠb/kֱ̫8޵R3 L\D? lPD8-t=˾˹sd7iekgSd&t|O: 'mfk5 ^7)V5{­ڪ-J+vuR?WB})[3߬zLBো.V3Hg_l?./<]ZZxn*f <$yL H6k\۹DVE&*UUЭ Y4>3i1%EjM.5(͒ ۣyUDDqk&4p3x9w=g>~mUՍB8\W̐5tc>+@d[ P-Zdv@:;_2w/>YK\'?sz˾‡X |)͑/w/1Xύ4ׇ ޴N.9<ڠI ̮3d%ؖ;>pFj"Fo$~x|c9@qcM/ ҹ{DžҜEG]t@t0@[xEV][Yi`#=»V %@h! _t|1< _تہdL||7o)D{6*;":t[%t}v'㜐2NP$ e[O(6~PZczQmN6 oLPkAVS9x۰>>->Z^tŸt`נBQqxî0 IMf An~W2\_Tr`?PZ\ܮ8O?F["Sb &W%*a4<&1D&XK V+n1](ui Z/?}UC⸀!{C~$?WROg 61_*6?q'_]@X#>`, _Ϸ$pWa!iM6VcR_NfOƉ_"tހO1x`xYŪMdDtlnrA/`._p0ic6 pIIu̿?EJ pj`\&+<{ݿ/gnt%u:4т=+P-T0[ ( 7+KyOoOq/\+#?ݓLM>x( H Ç ;x*@=F7gy_yO:H0ivQ2мU=?!YXYw7>>$X0TDms(Ȑ^o Tpjݿ/- @KOުhsc(`Ge?)&T~F`ml8!C"ov fq 0 l&noo(:)l>988l_FC4$N]2{o4T B7hX23 & C;(>h/mͷYpBw TSB D!", {|+0[@UǃxՃrڟ*Zc%{41oFEjEM>@O{GabV]t@H'ֵ:) =a? ɪ]aۇ,INuMNJ{ ==,_>p JK]`>@b2lPfx!0F(B܀j̱_U )BMto{87ڀd2Ⱦ3s2{kF&&`9A o˙YYɁshfw # 3(L&FCcY+_N# P, V&}cN&!' %#w _݃=5l\׿x::z-iygcodHgbfLNϡsx_3r"%Ho_mBDf1Ps442!7segl )?xcWmΈ;:\=1 + Ѝ) ?OK' 4D4D+#bdb q7=;#9'?:beSyZ@#=  # 3P@ ;3Plf0QVz@+`xRY=z@󿨟+ 9ѿ}1q0PZGB#<(9X%ZgS@ _)9<)gJVzNf&zfVJ5տ%b`mh/`LoDX@EfYe_)فOF+KӿI?Ȉ<7JLhsOthM|oj󧫠j谱 ## oc-ƿ$DlsS${0ۛZ{!{E4"&t" FFvFVF7.ttDzS{ù@`bۛp|:N!$<7FC/4=x?;I𡛇Q;)l!$L?$1 \0}d" -Lnc)HC }2߃@\C ^!3 8|g4ZD_\X@ej= _9>`kk(-vV@(z4%*y59˂< 1l Z 6 t_1A11}:YdRkʎ^Rʃ[{[}/o,JsO9="J S⅊ÃoD"ည OЎP#_<,6xYPV(iǦ? {m{_CI]4k^LjK OyS0(3N>)5gs]_3V[C Vب3M\ۨz;xLJеQ+A۴5bV\/sbKI\XLB o^j9Rjg{ bYig0>; 9_ھ&i^`4r%tvY>Tۧ!bǯ^&ˀUTe#]@vaǾxR9v=@ak;8\o4wwxm "&~(krDp8gb^໿h:Nޟ}ʐ qJxr} ELw]7+yݔ3E=Or P9ȦzcS7.uA.}w~U')pnAUBoXD)L^0S![S4*Ǒ- w>qF7ݯq &TX(8#| x",BW`5f34 Γ>97IP)R{5CBGeoGk"4nw-h$=XJBKI4CBEB-2rK֙mo<=o:#Y8z jPʎqDRڻvXVUvԔ۽k/ӭNOj &2) ugc GH5ȺZ>͐}yATy zx{[en>U?v+X<ɐ.>G Is|u&>m8Ƥ²}V j` #݃J>dy#JuJ0?n_vBVUUyTo:\VYL!Z[g*(:MW-5܏K''O$B>}gۚv7xPӴHSqyЋJixcL7>G)\rFE~Kԇ1إwk#Tb^I-aۉ=ZP[qZ{:+`^7P LŶ{GSY1Ԫ%zn*a]ZpB=({}\zFy\"EdP,XMT)XLJMU7}zΊf;S9Ԓ z8wvI*rW{@}dzyQkl3VM M[;O/)?ۿST{ yz^}6`<ٗO?B1=k-_彬`8UUy@hmv|Pf%VT»s ~Ta;rR"mfTg1K)J }yw~#ޟv lkya'8Ý$8ux x>Ɯ&l6|k%Fa~'Ofٹ""L!YV%n޺?"/@f0*ɏX}@mm!HEr8jsɷjtqph /ٖo<DQMKܯy|5+mٞ*h:-{S>Q~^_j4.I_fwGŸ8v&GUOn/擻Kϛҷ d3p `r˜OZtX3(GPzkz" /\yyy/!K!AVdaIz*1}r>Ѫ:- iMMG~ e7hu.{RnC&Q >ພF@#p Y8& yDx\o}kmїw4 QE[w*:pޛn\Eٸeb"{d3)ZmxrG}c;A,ڐȥSyoE\0{oϩiXy&i5^y󫺩>8{#/7mLG8T4l&I!1pDwDxK8t͏b:3?l*GX_E7O'NYi!iZ.!'Κ)ݙ(XhJm3HD)GȈZ'Vŵ:H%RVᄄl8'ȩH@^}:j;[<`׌ř/x`/ Eu,u^ ,lYQB Km}YzD:l*r ,md^ X]q?bKJ#=Nݝ2 cm ]?-bW~IbC6?3d!F̣ N\N2& 8Y^Ӗ5J%U֕oEah2дnjf@!gW7l\uv/([޲-t<.WѶ g㽆Le :.LUgr+Z`0Dz΋o3+z EC8t ifV)Rp=`8s\ՠ\My:_y{}>_Diԍgxh~l-ϸ@X)=wv7d_Zu oONOO91Uާ|?';랕 96m]N/xZYLugjPthKss7a:U_]|Y8#iBCC9h/i3l&ȓWrzC(q/v\/8l&*Gdgʋ>#9]J9kyv~c(hȖHIu'ke<]{X0nӝOuon?^F I}D^fVsr8]\O%^U7]PF%g-z 3x,s^]]]bfm[h*Mj7Mk T'˭XjU'u2GqɢL]F<M+rwի^ НŮFI?'LTHkܲ%f5\@HxnĮF%333/L(hcSq97{HoqDZ#BC0 KUΑ@`}Mg:)&-3SΤsΙMվ +tY:GUB!o]fڴyP˼GR;kGFfj2z"nPFv dL=+Wݪr,{m▨gpBeѯPoDz3f.J-ǫ\%OCD4OdW`7B?2V]bn}T''?2qؚ3գRO6M׷ >wK¸Ejq0/벃lis)g:Ѵn:?^X=tQTa$Hv%fr._X579y0!n w҇ 0M$okiE 2?+.m*,ȺTЈiW"^U#NlݵNU+d/nS|QS M͖~0_ݧ}\^ǻ2<V[rkav0lgE`+';}]6P@mf[jₜb n F1:ņ|a)`6G.#vVN{FQ{".r{Imd d#zlW 77F[펦\D(ɺ.wǵepWP-չwK U!bޛnC{`EDT\,|ܗvJSXXy;;[N5"͞){)qk"( 6 o.u0J㬹Ivh3Khk/׾|[s n8F$@*z,n-חmy L<[No'[< B/z}M`"CAAQWE Ǣ~CKkwͰ ݪqR ߓ+YxL=_Z%kIy"Ó%kW$xBbˍϙZƦ<{q,`BV/uW*eɻy9r1r-cOxkZcF;$\d 1RׄK5n[.!mWib/Q,oo>yJĩ׷kʸnyƔ兩v-Ծȁ]CMn\rtbBVe|E}1unMS6V7Zez6.ăMw^z:+Yz%5<Vx@h81@Z֓^`_K(IA:-˹cPR3g#usY֬q?l; CwP Yqj ?^Lep?۩P*se.I/ AEqgw}r殺V@RyT]?NgzׯftR)bb_-@+*30AwVP4lV3@ &[a&to3.kb;t|Og>ZM ^^u26!B:S߯d,yo|"x$($ԞE$_WDgHɉCa\v(6Hb ❪^! ̢Uקg1Vqq hp_F4;B 񝕅"X7{^RU3Fz$If9zt{'mPd}Qq-)+3k?3oSQb23Ou=Y!S3 &)B H.בl;es(0}an <i I^E]a^}nShr3l y#n뽃ޣG^S e,/&w J =ۥ-^~,FRN֏6N't86TP\JKhX7~1/zAvRPDD+ /Zg &bj?Rx/Vؿ&7%U)K[Lx8tg'J; OUΈ<{>uo]6\?)` 5<&5u9}[U7$ө;dEos3Ħ8 pos+h A\l/&b)ASw@i\QCK-CZA}/qڛ#gx'^W4v>`ljsJpUw؟vdZ> ɟHCM@sS ҷPB>/*ZurnMvq*pą׽_.ޗ撋yn)^N^_STmD3-|L $M:3hVi1BӾu" 2Sg_$sѦ%s`)F^nj. eC!Wd9 ; y Ff"H-J߯Uɠ;q'> zmX^gTP/,ZWVVJѲ(V呑u vbkrŭIa~z^Y/bD5%SX+(Oﯶצ1S__oЅxʢG׸m '+ywLV0!:rGSKf6ud5wYFQAC _cdr"#,2=?Z훘룵lؒ^rvu{}޲WQbtNt/~P\0Z q,F! H48{)Ҝ9Eq A>r?e*~4_~ks1pPs7wPU=ݐˑ<˗2ϲ?H$a^7󧨶 >t qss;=Mh3J/@=$@kM.NZz*\<<f5=?q.Fl=4hwp*܊j_RnA߶5b 뾄;$Ȝ78}o-"hvaw'JK7 衡?qZc8y|Bf DO7S\`ziR%T:WCoMUR۴nk?[Z }N5· ILF.-Ruu7FOhYrC}Gne9Ga4⡖}wyW~FɊ!&м/!b x<U1{x$b^f_ d,gN&X rp:7(q en ͝737{{9GϷ.ܦ>)Q@}c|V.E3Í\'O#jQMC젯dAYMq"6_/bn8 y:xsC0N:@ @2[15z*[O.7"&6dv@1B4 Z[jj%4(\F1]t65?uK[5B<8$p?ߛ]h3]awa3eC7 ggZo_Mٯo sXF</<_ o dUo(av68=}; a}wO8kn]C(ώZVw}볋A7ѓM`㕑ÿ3WA|X{ ^;w"1dV[!Q;#" h^|zޕ0l>Wwhx~Zm6m.Lah^8?W-z-%C?ȟw|^+d]ws]^;>@zF "J ozo]`bjeμ;/lb%h]^O+i*7ݞ? sѪ2k׬ٚ;C'=4Ja/'5T K7ޗNn!U>ױБ'Vt qKT/$̿g~(@fxsNFs0U?:b*QS i,}||HfY$7ݛV.T Ln} .FX^MZmpvu޶h#{K]M&4\66^Vbꨋ|l8+HfQ\+e)d_-o طwO7x[?^T~z<м)pN)NOpYnVx*>b/A~$lp5<[zqk+j{m~,u.ppd5ĩ^K_ :xA5붵yqяS ٢ֽȾ<>uE86[] Slnvr]d@^\2u3.ˡ_Sk;&| k#SA}g>N?@Yln)՝|v/]Xz{qUH\i9³V%Wy:{{L#hAg'Gѩ*d.JL41(j߱ 6 f|Іħ.drkojS 6:?o-Hs;SWy @ByGB!p>Qn~Oy[ ˽?3Xi@-*Og,=nMc3ҍa@̐™3dȞj5PbM^xLGk#y{ iG;gg>H˖R."ȉVá-.9us(؋˜D:\XtfGYқvWW3 tj%kVpZMùYRtk[)\b̓'QA6Lt5{Z]dYF/1qlimm}^ԅ[gW &5VŤ84iuޝ:Dwellw73}c,\Yu~5 s*k2v.uLCLoៅw3{VZrei.O yΚ3øXⲻLx\Z"j&$ uV2AOi{QzU;I )ҹ MFczEC Wc^b(s^> :)ݞnEi%НVWO%}א$$z0Sa<ﻷ{z헴~DhZ~6EZoȯ/nzܶ %R%;䂞 sc64+sIhЈ^lefaf יl׿B.9L]пR)+tX ŢoIP$4'F,iDU1gFӸaD wY. >~i?/[w_\aJa#1#2WIZ-d.$£ljGqgW(x6tlocqKƤr%!zVݗ(Yjn[Ý/Jҷ(JFFpFH2&C#ޱ\rVk,랋Lnߖv%U0GJ9կm}ÑOܕ cXӲw->W?{T=j퐇cWEj@Zu4.2UMq͹糡֓"&+!J urnA݇~#b.ֿ6粁n;W$}CUō}B!W"¯Z,ḯkse>⹺OFdV8,Ts܃͋2XnA^OacWTR{+g>0U&8hJIr/߈>ǝL 8#*Dۭ(-.H@cpyD}sl$ǥEK4(:R+"cEw2i +v.zǽtUuZ7cd{988MQY j,#%89}uzjB毇}* [c|$<(" }Q0,oD)uG4SKVBC=<'蓎3S;r#;!jrAPڙUQĤ9MH'D yh(S/^G`c;0hSHH;bn (~*#l!RD0A:*ғ>= )7Z֢,# %M3&=RSIoJ^XY1ÀwҒsm;GʟzleۅKi‘f`Sy[wKf3k (9M_{y?aX;k4:R+g]$bxF+b_Fydtԣڿ '8v&M?Лx]cR0ɬ.{Nͭ8yR8\uq|SP|r`#3\' zPc-ڵYp|0T 222 J.gi9nX;ݍz fQ`?M>}_j=81tq֗}gb ]n )Nc"3 sk}6b$bc6wou K佑-GQU=1Y`EJך> ̜V7$Rvaų9芞|m69LRAoN)B=p,j >@xӆ0iزgF6!+FԠ"-bD5U[n+@uzw ΦkHHqqu3>g _[S "([99eF7S<;6И@4&s AC~U l5{Bc(p.\`;#y%ԇ4lQHS=x,!8<1&nl$oc&m*KDsJ7QOK`yAtV) ]PHD*Yy}bFͫ:f-9j*{C/U=)v.AGY1moN~ޣr}RAL(k\"7:[Cӄi\XJmay"CukҪrn&7Ph+/]/yv><: ]K>8k㫥 EFD"iDqQ0 B<gIgD9o)H+GPP3I.!#}/j=g}.@ibTP TSCxFbE"9Ϊ-ie-===ˀw6B%0"I- f&5efo^E%B]K<܏| mM!ç-Mk+Jfa!|O$P0eA aO+qvp2BNRO=n-gh[ :dJlM1Q\`O.3;@GXP>}?J}is l z6}1"C΋B۝pНХD<ΑiO'RYaΤ+/^2'ܮVEb ʂ,9+!α;f]~,?IG[p5G0}N\}k+6r|Eil Z(aPSN՝%]hE ɯ+RWhY"XSz%Z&XĽGXl~) ӆL1 -v;pDNRԻN{y}M3~s\J]bgt8[d*UTL68VIe2 Nv#̆" CEϏ8 x [v驑/ʵ;LߟQ|!Gqu!w(x*'.{L)A&jR|_J߳Yv5vfmgn̑T FQQDSUQ'/JoLLrUU71vGȤa,*-޽u0K(+I%ZIY2#ѕHpB2QBO8R9'HfNuSiZ2"4wCviEAju7&Ay^~ - gm?'3U1EP5EaTWN}r2, J-4R./*@(]F?o)OCLor޻d*xUI׻a(;>NHRpSptQNťp3/!$3W2a=Ar)n*K=dDAd)ᅉJ][cɆLꦎ:ʾ*SēzYodЌ\Ζ']6%idHȷݟX#,)yOgQ`FPHQ)S0:;՘|.7j3_a~cn=eWBXL !~ y_xv=XqvR:AS,pCۣ Lǘ tl3e%-EPu<z]="hS-/oNs׌ZD)ڱ)վYjˈT.wJ[{A*H"&()#YpQQE9MGW%~)yJ.zܤuf_14UQ0˒ΛKLQ} =}NnP,'Sͨ9-@)Nt"qs<)JH%~wJb{PXEK'𿹨=RfE8J{#Che-cAZޔfiEhyf{3,VUU}nN'KHDzuFyJխO?MC<0/_ح'M|KNNeȑB1^nG(ű#SRa.<5FvsUffˇ,(sٟ:7vseM7vvX O:L.BS%8,D GDz.H`> in9 О440܌!u~T @n6iE1JFYi<K}Ce;[u*C|ٙ|<\\tJk+dx81G.O`/vY%İu4EoE\f!5F!>yexv=`+r~iU?d$/)''GMT4!`!sԡ=V=]v9ݝit[LPŅ啋r+a+2 c;^(WH̗f*+8 9WYwь̗gA:kog@<&JIeLPWo2 +OAק q*%U os)ll  -, 200,Ƒ j{k 2@C48- RfnAb˧oSiRU?[\ @zi4E?1N,Po[!1g4h nbDSՋ9 m&DDW)ьϙ?4=9=嬶l/1vrj)nc@"3KLzwM{h^j߯-V0X!ei䤧E?:l#'¾#3]c~2/)`+`S%sBjExVZON3G j,Δ[se.{ʭ͝a5V %¡늟>gsqo(f ~ Hdr{:FLۮg =@9zdjzB $ʫǁ9-a!%f@c 9jPƂDƈ_e<-3|Ak f8T1@W 6 n!h"2ٔ"U `lpd4db\Q;C0E d:#\t X[ /P*ۀʮ7[j̣4!R{x# S$z ҡ[AXdCi]o-Oo*䉊#\l 7y2q%9]_5=C HW|QEc jQ+U~Ke8|d&֫6_zaU )I7䘫2"82sMї~эS9KĜ)L1 d_ӜB=n= 9t|6{1XF1#Y#2.@+`ƆH;Hp{{;PzϧPz?_a{@ Pa6huQ S22&fKqq]aFrD_JB@">)X}SFe<q}tp̣}+?a%Np=;z}TuG$g^sa^c3d'礑b@n /UdP&x# *I) 0%䘙3%|zgaѾ7 bo_Ri Si7pT ;n#>7VǦlv@4wkd`}C)Q_0{肟c1yAcbKSV)CrFX 69Pd7%JJ6:VGX5<,mh O;W=1Ś=& @߇g}n ZsS`U[fJw4݀ٓ)e^ N{#Q}uV1R2b/ugMMg47/Iq ]N$^o%qP՚Ԑ P/]Z"˵Qܴ8a+rcpzυVڕWf(js|S=vE]2hpkSYO(F;ЫSM /_{]aw+j \4kry!K5HdIޯZIE~\tIZ'5B<0"ALH&X pqj#4wr{ii2ھ(=CZ8 U7lhݾ"=?;S ]`PvMM$oE&"w7pwT["ɚRuAqʺțx&mYxwV00=HAN@&K @.bRۻ(bгΫC8ЉR^wg o# ֝lAOR}]ۚEIa'yZ5)=1N) GN%FG=ovDXx2ܶ M}Q;n9}լlTի <;^}ؕ1uwmq%,d]􊫲zP!Ng{uP.ܤ#Z(s+9 bMޤXbok?bXÑcmK:F[1/: 2A!mP ĒR4KKK.qeKl5xj>hz O H&lGɨM ]RM}SIL:bb.rPD>\ORB^:txOY&y8x䋚J}}#:$./0o2 dE ms= 2qM7UA.%Mɢd~aiP۾yJs7G|93^}@%Gbq7ˢw @Oo_Ң{vz*.=KB1j џ33` $3HZd{T]O|B[WUM@kTdd1b_}FWh/)DXYfнcU@LJ闕@idgc0 z{.wSO|zzJdP@.j10@/c%mjn> `S {EWmf5uo"*_ 1 a1/BU.ȧtu(/&CP{%^H!@ԩϔ,C~eTOڔKK49E9..5b9Aj5F:yV 0nĜ`ƍ=yqx(J!l<ʵk9U>Hu<~?H"\>HOUK?tGR#@Lb7ǐzP$0 8QJʈpB7~ 1*~Xg$;_Sp#R`_:EW! +qۊzH#|@)Qc b/+Itс-`acPCPF-=\7YR)8F5fu}}=jK|x_JWPHD\1,8Ćeb}}#UÖ[_oV?MNNps5#i`Ѿ'RTi]DTj<*,1{ܬCĔ `MoQ9 ľ6z26g94e6g98VNw" bPGfoXœ/`9Y[Tb#H mķr3uwqa>x7@B"<ą; ȏ^iQ+*b3)\f͎z|k_Qra-8(+(MR*Q_wDjLlxCz B+C0/T? *l$Fg=<IqL v2ĕr"(6/˰b~V /KЅԘش>@ϭ%[{Ѹ:8T$_lSzu^^]8 =JdffsEmcѕg؟"*#O4D-Ky&Fzm,SZr$M+9I~Ոj L8 /ǝ^|Ja^ ׿AYD@v( Vje"rb[JI฾~kD3Ҏ8O?z#>l^;>R {Ǧ?L( 1YcWoAW^>}6FKĀK1 bB!U';C)VYzVLB4Ng‚ЍUkߟB?\+nX$[ A;P>Gh:*7 ;]#|0^BQ(,RySZfѴִOX7^$1miaBEx8u@E^,8l pͧ0;=ð>0EEpQyMA7Sޓ8؃X@qKp?wJKObMT^sQ4@7ȇgIPm>1OVLζY~Zȏ<ʩܴ嘏7C;Gv QSQe?߀w13XXo`I/zߕ:ߘ& L D \L\Ll)GDEHOEHDE$jEF~`dgE$g`dLcdjfOihMd3Ti ,<C)XE xH+kDgٛO89]:4)?Pdꂝ.߯&^Y``e? q x8lw]O 99~w' !Џ1b^GQ&dԤBSȷӢ{$%}@HYkw؅1OHq Bb/( .8̯7Sn6Rj[QB069_'{M|z&A"ts)yGDΥCO.LP)5d)]Lp46Qow$O;=> Ng.1=Bo 40зLk2st&ڬ̂fDY[M"t^+y#RE&Xl{5jɠ*}j?I .V|TG>gYB<<\cĎ}]MI[iGbSʬГw8q˵8쇎נ.Ygc䫗r~>[@NװTnƀW4A~Abi^Qz@CK38@ N} . z>[=H $$fo{%`69܄_R~oqg!y9hVo"t 暉3olqwQ: QL޴DlO>=⵳?*n'3 MH et)|7~7*7 W0Dj <_rafjCY[ޟ^^hz Bٝo߮;o|ad{08[q̎i;'swyEAAM's6ʹ1s ˑ^=m<53n*x}zүܧ7\$![O&^LO! K_;FLN0xN4_n_5&j|f7Hw8SbU;P|^hkDϥtdY(dK=qEav˧j~%TFGK/=q=:a` {{B?5?Drz88%fk)Vźr$`zRn/̻/se!)K>YAP$f(,gÂ/w'??W*U^aTu_Nj+5͸H=H1 ; E!*S:M7hO3%1s6g \{2?h≏iwIwn:$iX~)ªy4I ||]?8d|-^iH(RWeq i^/s gZ4cqsɊܼ5LI̪Q<`g0 VHQ?JfSuz6¢ktuIZ p{q?M C1i¬v?^j2Z67T\ٛEzvcv MczTy[vW˾xںm9˴’)nhF {\:`p2+Kɋ)ݚzKHGc$5z+Q}, J/&; &nikФrhvG^~"lagG!Nz#6td'anr7``S f8oCQna NAmFS ~n|UO#XĥUS)NP)ԕ/'>wOY /6Lx{D7Tc0gLRQm[F>. /ZT|-77"ۨ&n4F;q.‚>lԉ@j#J?(”I&kMF|}tzrJF&amb0w9v#k~v}su'I>q:s!ѵ="/esR%ZI9HN!Q-. ن80ns_\} ߽m?T^YxRD{5;=3"9BdpYv}z3uPgr2mAB%XuVSFW._Bo>;4\az_v1A镨Q.g%V~d0Ѝy-aOYC L:f[D!+˕~ltPTK2B̍sbsTdg ҫdNGKm9ZlHoP']5o^[I-on[3ΜxuX+Q_Mkrob540B$.BBP^9D_ޟmbSS`1Q-w4D =E6uV8qGi2ŬlMLuqW|O ߉ CdH<&6q;\#,/({Ji{;wo =²-eYYU?nv&3+zՑ8_+()40dk6KͽKjr,h9B7GթmY>>qƥSv0N$\)̟Gq{0> K+o`:Tw^t%:-0 ЩIV!᪒PgXmdA 4 /.IIP9Tp6QwQDA9G})xK7_[~ I5'M) O3^G /< .5y޿RD 12xz>{MA<0w_ OCg&;X| |o~٭&_6)ε?[aʾѸ9H~p}_"sߤdh럈1xFOX=K]B3xxIW>idb:?0ƑTlO0ooP:愜crZG +3G;[ݙAʎLvxLדt8Pw9U;9ZjGef'8E}3iR7' _qxڒ*G]xnz"\65[-L4n25m:6@3QːI(ێsxuH3IGЗq~)K&c_Ysm';JX `Z;,5T"KW:3-'iKM>}^ SX}(Tc3:EgZ\ϝQMwNF ?pxlH> +h= | 1iݰy'Ԁ}P:`M(VZL)]Drq{.GC VIw#Ζ)AnjR|;tE75({-Ӣ{ U&vV/\\_wnEw{ N_-)M`/{e$S)!Y‚E{v:(U} YP/;~!ji+9!yP5>߁_̻A.e5<_kL޶7MU1GAnF3 )t!г+(Z?ly߶iBGoѠt|^LQ'G= ,yd0\qmyWZ .t[ɱg'yͭHY9٬Gaw&MYbYrBaM]KqT hKt=~&;u {|5< ̆S;nߌ0\v<5 i[_I(ʤ4sLJY]_[1B*1<5x}ΜVhभe&ɧxO`$"i ( !{J2F'E7.1{ B Z=ҌqH$,8ϕm c+! 8m?yLJ%DA1Ddi)_! jxfYeM{(6SͳF0,㹠O.bLJB]S쬵H04N Ӛu ՟NC6iXU3Zd_D+Is1E܋K;7Z]"ꮲ28{Z4P3HtK1ykQ䰡 ZUo/RpO= 엯߂aÎ/LyJ@yJ2&BVոwP7nU%Ӄ{XYGg(32ۇ]ӗ "t%X92M`ލ4iL[rGIt#!O+.leyo N#*~_הU3͡6 .0D](sjHp)i&ݳᬾw~ۏ7HC^w,ˆȰ'5k~,s8IuQ<i_/պ4n!rG@5mPIfGL/@ߒ `xۤ96o+4؝ Qƙ־֦9J`b>:' B"XTlA1hlJY֣+(w*4V-;mZo0 zz'τN uJRĉqZߧ-d*^of# e}sP\ z~Ԛw>Fzg=z;]}(@naMe ):[%diTJaE$$I٢d)ٳQ mRT*EyBy.{ss89R}[y/ tR9oلnF杲DSxC91Gvw_uu;##{+cV[wJ|)"N{傻̿W88P{ $s1pWcWF_]uP6E'[[ĺeS_bϭy"璋WB4v}׳yfw]fm[T\66$@Fj 3^}sw}`dErI蚳' ~u[:n"ʎB΋n41';Bz*ۿcYSg+>kwg{ǾExumV;1T^qg5hY kob-ϞT#YY8q#v9܍vJ'xI0/8k7s{-!v,;L3l;_A] ;n]r/ Zj3TOδ<4[F汿nI>qO+jq oBP=$Q2E/]^N%y* NKc^.#₽=o *wpkyno|G_^X5AٞO)eDO,zUtqƗ݋k(d[ni;I][tCtuN s[ZbL{fKԮ[F>}gS}$̯1z^1"MvS f8bI*)х",E}G]ƟB^VjrRaCEmeqv276S8k%ZmU%Yrձb|I@kţgj#y< 2f+t`vڬZ6J>Evr?tPsKHLu=׼3Of3uXkrrz:E>|cQ: QWr{8HJXՈi*ʻy.\}Qp09xÞQs%\fr}cvnm8uKx+@y,I/io?  b C?H2 3a~`ttGc} 4,%ُEU8ܡ}6K[]*nMI3ɮoAFQ#˶CG3==!%wǑldĮ 5Bg}1L#=o$8 tU֒N~boLVwԸy\W"o+Y[f&7fcCfFK+<8ZAZWvѸ+mPvs=GcZTT’)ͷyy6+.2I_Fk\̓S^T{|I9ߒ/Ak-MNnSsI,.-MO28eǻ \?v}qW8k6l*jUȁ{** m?~u>}keQF~㫨Wnt}bצ]j<9uL s▶m=6vgv>u!NVT쯕Ү ^Y1Ƶ]K}ggoJi;.0ﵻ劐Z&Sێ>2+E {|P^wFb79kvKT]zLlKí6>BdOaGTO4ϫFl.)һUQ~K缺MI%ėLn GjQ Mr/߱%o1~EbcCla^sm}wn N]*^Z~=eo&!\M*[!ݜWjWf\m405DVKx\X-&.ꑐTRf۪K'|Y^"$$hym~N>voFhWY;̴EZ$a[x-]q[1]_.f6?R5|dD̓=ynj[6-V}^ʚ3ׇ п0N'G=w2iuCu)kagԋsD[\Uܭw8^qv}=&_J.סgpZ;5ڡuCt8}=_%ǝW"uhc E*3͇ ԊXsaskuhG}]bQ쬢AȤ^9$ˎEWJ?]W*tor;]|"A* 3e ̺,ۇ+u=1ъ{|AOދbU8#{%sCp@zg`!|)ה'_+yb.[訏+@)Y--_[u[wܲsUwgԚΰ[Κ8}'ö]x\Ĭ|u`K"G+x47ً7:Y9"OhKr2ֵ>_ O=[2@~uUw AR4xIPZK٭Ԯ|N> `b9nm6ز>m>j%XgMce_4NrlI jO/q[X;z1m-Ӊ.$J^}9zT|N0+Tʼį6/xWNh]ZLߦ}*+.xU4SB g;Tm &'z{:R\'Ö2\r\;:jc=-JTVhњΠ4uC^|gt=q2Z45}vYanwOR^ '0K԰?~>QّǾҸg?Ui}q7V. >r٦*a#'|I[0=ct>y}G, 5RJηo߷rYn^AI1Y+v^(vvث.NM{ܲn^|Tj6XYw¿-V7%.Wo"kШ}{H_<mNm}3qRծbʑd[$B6%~;t3mo7=*"{tW`qyAR_<ڷ>Kʹ g>6TeoySZͬC˥Ө)rvY~h~X@nۙTRM3bKZk_>^LFY4!f <^01&wSGy߅Eצw8hmڕw/y$d{{K9Oۆ,#:{hO_3\q!;KɬmVk[#P hy}7v:ei+8u+J#{o6nWiJ9[V+UhsǓ:.{>íE>z$>K:nNhhWړZ<ںq8‘^R=!+٩場S>vNgT–dQs}lSN?R$zFfRJJ^mD_*tyXvu@>XJfQG$Q8'JLߗL9;hMlHi;$tiڑp֦omHNpY?;?#tv9Eńӗ*ϗ5t_,6w33ڒWc\`,ꦩCE9حk/YO,gkN=×IؤUNъD{_uG{G;.Y.{8l~M޿+`罨DՔlGϓji=&ƉGi7~>2֔柖Hu,ߙaOzߑ_ T97-- |8Nq5:BbkhA3{/S؀m4iZey^c3֏ێQfQ8GG^G_p8U*աb NE3sx]MF^oMnl2ymS@E_{bڕsC7OakD 9]y9j,= k6U_B(~\+;FgG񎌔\ C[RL\TfFf,:Wʛ/skݽo^m\r)#6Y7s۟/}uSh-ncفӳ,vq8(MH2]CՑyL͇"&OF >UvTx8po vm{}|z' F3S>3(ȑu7Q5WtL+Sϯ:˕#}3?e^})v]d+գO&3)iH9k F[.<q"wQ7fgE@BFdۂ% kfwc.Z{fAWiQl>A{InDj^.2}bvlo!l={lw.Ֆʄ2#ˈe[;gf-n};WCb*o.ˇ:&d9eU^πywcՋ%tP*plJi*{.2)$t*Ta)%*3>*z t_64:fQ~~cy13uX# 129GBspEut8';ЀPW" 0&z &47]H$oEH5 14 DS (8&SS 2YdomprL``U͜X2ӛD6BNďL`$h"&P,3[A3vx',2BR΃q4{shD)Lv;x*N# ,ehc/g<F51<4`\hP: lNp B'"H'GH?BPJ%p}eitjNn"1̩.T|iB2 . 35zQ ?~ I7#8?Xr0- <P'P.ࢥ@"h0v:(<dPL _^f(%g1c3I!Ndʛ5IT1pq< [H8&]KdɚNScbA,^ D&$kj22 цp:XPmh!ӆFAAh y+ &,2$Cہ b@OP @m@rud@S5`O 0ɞ,78 8"!{s[ n0?LK R+ P+NQx:E,l̄oVI]rqtp$k c?o:hOi6/c 2!P~I0cpo7(8< a*l? G7x/P0PydB tlZz$S{Z-)L0(X2TVCƤ@փ g4<5AqS_187zF?Ha?pXzcod5(ƬHсv6HKGp^'gpZPEK"ϩM^SMgXU@Kb,ƁnvOK l6'x@=pm@o͜@JOE0>S T9/@*$ ĭ5X4t.'֘ÓףּE=VK@c 8W\A2"x|ԜI,6Ԡ8g0DDM,L4 ?Gx9 `[p X&w8)*hY5u'}Ƿec R6Q49FЉ$u5cG!I8j7&JHe`  ]* EU<֚6Ąo6P q6U1:எÙҫI3/@q\esq%h2 ud`v,dR *eSWU~Pa'jG}*zjphx0I DVvŃc2ARu;Iq4G'i+b5g6^9|ur*GH p01h`X=7'-lnp09 hF2A Y0r-}pfGD@5aMcKTBG pAKΚm*L%k ZZ ZMf9,Թ"V{)Ȍ1u-q-Ơ-8'P8=] _t ~8OpD wkH=;#H hA&-hs$<|*$jh,Ӽ EEbyAFE :^od).^neHd)?kD2nH ]P@!cy{pɨ GH@#8<n!3H~v Ֆ6`UhTh@h頀ԡ, LB>y84\"s(PxPӛn=&Pd{'GL:LrKQdP,n3&n&n qO(ND9Po]?ʡwuGneB p. A?կ5|"$67[Ofy w@:r㤀{ƘLA"jO8YO<. ZkДoI8 1PPo`/<ۋ]&@1gl aR~N?K,Ps}ȶ v$b˜P9wNL)$/[3=~{],#865TO )B< BH!yhDM!_û"ojc_ΦSmS%ɿB;$xc{\ =Dr_F^' (P. q +(G 8(J 1{Cؓj,"~s+::[HDraNj=i_k݉ {A"(~Yp|/< L}lpg2as4T"Pl*9ބ8pgs a2I76i[ @e{ I!F XsNBV挄`3I$3G3q@жGI'1QN-4$4'h p.b'Ԙ=4Jxk\?Orfzs[`r! s$:zq p{oI@q=C=FWk52azzgOgIa>9 U65ɴGGτu+o|Q`a QNB< +^ YBq 0e|? y[zcخ2ӫ{hC UUfjGd[k{],)6yJ}o WsH_XC@;eCYD?!~ ?[5Au^tK"OBG]A CI@PNBBBB""""41i$!qKE-CH #xR<|R E<|~)eeYw!6ܬ2CO6(tq]+M @~KenL`\:9+{#jU9~'A^Xa!BIO 'r[;9HDۛS_^=J5ɴ%Ag}s1v`f3u&< i4.7'&}1*SfѾaK¬Hۤۊlc)&W&^飢:&Ӛ`EҜkR편]!l0TLӎ+ՔKcQ:(Ɨ_Xy:CTwCǚ͜9H1로Ӎox,UtMB?ӂ_aZ̑#]{cf=`È5s{i:eQZ˪+X9%Qh`|>6=.XX,+~ `:U9IpR_Ot_(Z}LS` 2͛$n+:ۇek0}WT`E'C"YˊSWu_a~ktqvk\JU¾$#FKB*\ _٥/}APRRq=ZߡRɺA%g_rc^]6zLOϩ2Bbk_jM,+ai='J UUw3D/ rc%ncpSo>0q֚ǫQ7k-!l=@mVrͨA-7n ghtx"]ܐQhyxV^]PT{nzEw*%B W>< 4\ĮH);`:iQ%մ,{ %-Ula[\fu@#p*"t~= }4`q:GVv58뵭Nc,@<*l^KN+ђ sn44vN{UΛkdg*h[{>\{)c {bv[}v;oZΩh9ЦzaQ>_MlVtaHj ApgP_H{C_LDD_>-[IGšunzӺVW>U1^r7/ĊIqw 8l_vM+3RP_4fks܏HRxA(`FZCm7,r웒tgU1MNyc&Y&KЩ!9Xk-68{$`,-99E`Ǫ6_"czxϮ!G/4l9x-k"4x] TkJg cFDt}ꔍ4N1btpJZUk֦eߟs݉K|k{ؔpWF1.gAυg?bn'SR"ƃT:qnvg*w Ժp|ha׶S++Ѫ~BC !sգUޮsنL(C&-/~jW-=ʷ'FBCxwpzP%^_0SuIR+PszLȫɾ鴫I杚h-fm\p#FB'z5-UkZ[f$Єq 7dtmRl3!NmNu/q0[#ilj^d[qʟ=sF\I2&mSo۰@+W_^9u=atQ{'VCu?y{RG )ܢsDϞ]Fua.歄+b \%AYJrgTOdM8o?Y?7sF_GD48#h@?c@#G;ibA&$2]bt5Z?Beo6&4D`$@O_.-9~RHXYD9NFv`_R1ι%@;Mp < X)кAHI/:mUFYʩ;?3{ݴW+w6ٮޟ}8h25''\?@ߢqmuB^- vM5$=mU]OVݼXv$Ռ7h.,}[ >'m52v״:gsxXR79[u''3Mr]mi]ۜ3a$fyPUrqݪ'~OϘOqͷDܣyy"Q^]m!G 9j֯卮W} ׽)9fΊ{ -QQq"`ECB¦URQQ z@$$MRqqm R ^Vj ]w].4ޜeΙ9sf朙Z+AލPvxc~lqGrSϾ³Dewim+ 領g.D{nl]zn(>}_놉81cuYB*;Ǵ}W9P7ZZ\enSrg(qןڥ9ī,v^=cOtwq]Nw֍6~L\-Ȃɂ/k}~FEuڋ9CGC7[' T;{钓Z™\i'h\hq&jqz냇Oi~Ãɋg-{\d?mZ*' 2>ʩa-{1/%^8U;[toYs٥Ǣn@m 7=|lqg_iU9!G= J7\^k5klmYÇ'VZqCdurms^o7tγ҉;nxK߲={IE*93_޼=(AnCv=|%hmxwzԅ/{>.->w94Mɒʭ)(+`fBtZ6iLƋ{/fN]}u8sDY%lA?f '5l3lڊ<!Ko! kViZ%ALo?Vec՛V(>ڭ͏UZ (1R=yfܹ&w9̸ʿ}c2ɾq 3` pYcL+gwWݒ+C{d4-.%t-TLu}3m;(zƍWg)xф9&,]{5ML9wg̢Y];vMfVv4B2 8kꮻ nՎ8ܟG?n3_cQf[`OwB-?2:G =f 0Ub#OksE].'=bے5~=u PǍ` ;Me{#D.)uQ9=qaL`Sw2M-'8?E'J8Q]qT.Wnxf/J4&?yQ-ƩԗӂN^u oIJ%OW}zكF.d`~|loqGT pɱYs[lժe۷f}١zdSǦmںnu_zPGMGO6wŸ6 G87 ݹQ|TχV a %[rb8xv%hpKfЬ]NNm5? Ɇr"vCkC}nTվl* o b-?h;\hi\xoJaw=5t#[jɁ7 2C[}eVo K˙MحX s7շLQ"AM^D󥢸>Ac"(YY4"oHqo,uN3hĤ SN~ 6@93l0K A 2#G?G~;3!ub£:0?T[nR QH$>ED6X=ӆ ͑\$eO$6?GtЯX< ioXhlQM0~qπFF\V ZZ"J)/8'V) =Mrǩ& HFNB YR0e`Ʀ-Mְ2@QR} M?0p`wWpU&ǭ o["3 P)0'8IDά-k]ЧWgf;FF M ʼnR^`h[1U(8 2)'1R1C=0A:b(C|YTp) 6֔|r:;}řn9 49oܽo]gh<Iu;sD bm-J;d:s[}ݝk_Qq:[ֻo/ݻZ1xLLfGڋ^BR1pmuT(S m; ޵d)9%F+Ў"n:.#HjUʜn}.mN4aSNꂹW4lu>4gs`0jyku&%AZS,h Vl ,@x o,$ѷX-d R82E$m"M'b1  ~c\"љ4£9(1jxxmr !/eL#)SF M!iѝx0V T&4!&5l2.; $^kH}%`7*c.o| (Er1@UTٍ6LFC]"*?݁!YM5A~&%/?b!$?E`nW(f)!RgG*Md*Z{%'2!#QXJ%b9j_mL32xG{hFt$O'6ͨ+0@g&Խa)mLNf,Fl3o)cx~,B4-~$7(KojZ7Y&?)gnQ W! :ƚpިEǘ.Z B( 6UqS5:"`g`]9Q:5}"4 @) %d45AXS%Q:D* A(~ށuo@' rEd,[x@~Lk3YGlm"UQXUU*UQ0ҪV9V%@VjlZ+ofGe?|G@N03]kb O־! &0'OE,t$g`5q+snuZl=ElNܴu4E6|FLy^_bpZ}[!IeW@6FI=$D*!Iq'0!ƃzACTѿ4 l|lرNqUnT@#fsx~QaC t C>]D#SPK%&DmV: Q#($%R\PʤӌCooxյߴo^^AۣgtRcgWjoa.!, }B&~ &Ǔ^#;~Ճ фINNg=ϻ&k]3&M|bgM]{=|"."dj_{NrsY\%{? }љ"qO w{!M1QM\nE?$~0Vv-԰3kuf-X;^t)MO)07zWΝBseXu8}RZeR)!CuYntʔ*}6jKoE\lpʚ5vN; ;bo_&̼a36g"@6 ǏJ ƳaSסCR|6&jJ w46^Yf]Ԡ*BCM@m^wKsΖW.{q}rm4ǖ"ɓ|phZ\·6տf/, !"vĤd@l}u7S!0@CdC̭/m,-͆=a Y+,f8LfsE7*ihm *-) ;s볒_*/k>lam~oV'm@mM[y ~0/ (4{u HHA*U@[G3Rμ$6qU޵3kGBRܴ҇K^(4BHpI7 NEvJ.(9} eA` F `bAFuQhc 5Nd5rOC'-w0Fa‹\i JnX=é9: *vR.ϋy {3!,w?>{ا5IN!؞V3niiz1)Ɂ*WiH%St?,0@wCO <#ԏgfK-H> ^k ׃ݖg8X0|ږq{[4#7b&!A3sh8;4,GA?I5B`zGJ!I:Ճ(҂"OPP,5_ LDP&9#}Ҩ mw0Te zS4OQ= =lzS. H:_V$cVODwʔtIOvw(:Ӫzawk=Džާ! _n,Z/Y̅=&)6DiK(!6S "hO8ڬP|QS䄷<{@o\3NvgF/(3>߸cns''ppλͶBc ={NrB|BPyKQg7Hgs-|N,W8"3.:YnEt>׶Q؁^g@MeZ5zʭi:k7Ync +ҧIժ3W ޳'=PEYQXk )>%^956<\n{Pl)9߁~tldCuTV2xD`5ugyOx ͧp谼3YUǡGhwjLZoG+26~  'tzDf'rO'[hM+NiBu:^B!jcZRT=|䂉֋a)PDԙyw *~̶kF' fėhSks> &g p}OPz~ Z܂WG+-%8%V'@Vx7`ZQ1 vl6s ˇá 3=cM\aj䯏v$~(33* wP~Q+ܮcq ]I4Z(j98P-XL 'a$ o#زniQާ/(~4.ɒ.qImc,ёwR%9yeps u\l1Jm[(fx׺-7].'x>G\X =pfAK65y%tPxG:mvR"t?亷؁@3Mӊs"Ԇ;&hk{gp^,|t`\i+n{q}PW㼣K&\xuy4K: ^,(  OΦ[u 4䙋{N4R:Wigek'LӨopx&ѝWu21P)ž{_M:B T) o D2Sz+G^ k{o9U,ͮ׶>_)6_;cߝݘ`꧎aﺣض>E (5*Bҥ (j@QHT. (MzSt 5@w %H{r-O{׺}{csΞ眳~;g 3;G9[[@XKT:p{E,bmQ<^wio)`-!׋loW&;1C?&o+}YIC?aˮFS{<}w:¸tC#Z3Ihq ;ūDRrI%X|@qsoh"SR}s}17N$> +<í,"< KJ-ʫ#NPm̶~t#]Qf7re{['ը$8:-/1~!_W6;.; ')X~RjDi ^&^k_r{fX "krtoT@@L8X"q%%N%); zbӑMTnq:#EEpɟ` A2A2nSBm1 8b"7 ]?QD__$ l[P ЉS v7##F3M173LKgHc_`B, DGo?]V~~ Sg_kYe@'y=w} PMǎ9Zf]jkav:OWȠD֑\7^۠XxyAPEeUM/gnv^[/ ɾ5?EwGo"0|k)/ťO#o^C'oƴon/y[:9?n~=U:T=3L9n˴;>.'7wuͥsW Ն`Tr4Y-<`|yRuU~Ti?&VFDgID *Ɓg:­b|\[<1GL׿K:X뿗J>"^bb㋥aqLRL!$Y &NhyEq\tR TIz硃!xr uRaU-D^\ U\ǚ 2oWz}0DfϔP낢{|*#\ Cn U׭%7,6CDo%Z? tM#Ӻ=I5\$m kwAT|ec!jt$ b컛ڏ6&]D#̫5j!hܶť6'k@2萐C̻o*U;r0UaO/QH]]x_񢄳Dtנl\!W{,ARsRۘzh+j<^4(ΓbIEsVBwVu^J 򘤉60F괻{K+. ]~OZ7ؼWt[iy0f;N^C Ћ*԰}dCa V?{11Xl+iC+\(D/hE/i#tEĿ٬ V5xCv']_+M ]Wm>%vGiwmK<[ť}Ukӎg󚜭rMy=T(fFwFUx)9 *XVM292 l|pi$0.hzj td P%ӮC≉A U(]knսpJə.$ಂJyivQ3K[oscl/c-wʚ$?\ikepm1ziz0[u[r((rT(ob[GdjJZs@zxpZuBWM[w(ɐ9l"@O󎂹^ͬ:G tG#}(qJ}.|T5h!z]7E4>QL/_= IB[IQ}:;4_#I6}R51nZ  ̦3wgn<*tw>zBR#L(HPI#끰! NyaC=^F?1Pou7PRBR:W%Kj^u>|m"1&9|t8 y(<׹IYqMWEis-?+zGB61?Fˊtɼ#T++$ k!Nr5J; )g)/vyg- c3(Y+ 5g[ˎC?\̸pBn>H V+>Yzs4=U"}uQfuKJt;FbR_Ы؀>,Z=E eaqQ>3l-bc1Y)EMY[>t+syvUY0pv7,Af)BM*m5W 6ƭxT8m>xRIH\w(oG:-+Hx'CL-7q*v8a- l3!* o(ۇl wp5HE]a30Gg-+Z4hx>60vYp4ܲNHGEY9=lϢnQgwR eyD2WMt0֯&R U]a(tqssY5lx=v O?ml<<-Ghzdu&I#b s>qzcK{'[ɪG9bI㫇C/*9 gsmNφذ Dj:F2rjG($w>L'_ r7v؂dSd8WP,p@&)Lq/EH<`06B%9p߼' @:> "c;MΧ,v62JՒ1[uDRU31.Cf暦$!Իfi; 9%6qI{-EyIRT7D#yᯏsђ8bbc{>QF8blRrF:NiJԀi1lƐlѾ>Ѣk.LˀOQqUtO;ҼBXNt׶>'~^X_lA9JP|tjk};X%M$Եya5 }e\wx7Z,Ri%]‡\d& YV3u 0P:Q1ȉ%cgN)Ul3' i=s5ww4Tz2sfhɕG Dn1ϣRih* K& "& 20 zҪ8. ͍ssM8>tτW fh9:/Qk?eQ&I&Tƿ‚\$aPDE2 AM4ń3Y{{T=wVўj7?&d{MhɗyW㹅LFf"6Ëu8XG(+"&M\?afj*f&)YR|K>Ve_|c0cQg-+HsJtI/*DFdT"t>}ڌ?󦺾eAxyƼts9Wb'U'YI.'x\*\zS/АjMOMP} I5EvÚ_W/o>/8?[5uJzy/Q>cF~lE6<R^//J=HoJoLv!+b㍢<J68'X ʡ#bFuX'.PZ+r*cۣv['v u2{;E^,-M]rr*b O_MVPP CX(saޞkך{/̼ԕ3~Aߥ;J5iC}߫s2PR))D1`~DtYhsXHs!•4ҝ]#G#enbW419ep(~PJ.PO|csBnYxiK"ZZa< Κy#RVMeGL Q%AZuJtϹ@9mhǝaMʝ 搀;OqmZ|9$,׻bۦ2,_1>:Nzq d ҨO];F`~YDeqp=#6o.,G3Qmz+i)#Vd9,cJ+Dm4& MfNlW%`jJgԛr=e]mIIZ|.{G=Q{&ȅ{h (b˂_`L6-nV[NF}G<1`+9.SS vbbڣ_ڜk.Y^ƣVof,;yep.y^$Ǐ HN:rr8UӴ(U8MR %:.\4z*c !4YD4t.K-+'p4W(//-3\Vb2' F 횶<4GÁ'l pNek>z1Y'W55}' unzCg"4_o&EEP5`4cIZ?hI&"AHtZѡ0i% K~D˄ 3i%^h.-.!Ρ}/2V}+$5KS[ŀڻFD^33e5'"S&0HIp_ks` V,f%W=9`$gn/{ *L _6)$\4]đmW~>sJ񝹫Nz JSd"s≙Vw:&홌gT0=i!9BId'&0xs֛(wѠ[R`*_g>ǀy+pRMQe]1g%~FEfN\P_0X26Z+3 ӈl+DJ'L2A_=Ez)cR^71ⴷNS ]ucjʐJ’ F'k_e ϩɦkjR 8?'vNBǻA;7[Ugz踉R˱LYMT+I<grBmTgƙA)6{wOwvˆ_9m|^?+#8gaF9xAEf^קՅ)–"WP z}D4ʱ;ζoo7]:֯A rԅ_'DKQ\ZZv+&2{S%PAܲOvƀf"Ro]*?~[J]M-?~iZ5M1>O=GOAHa:^& Z"^Nj57żH0;2Fų+obM+1ڮ5ڭ{NFhI# \"PsHhE>M%e52N|0U)<ܬ  .`)1****j[:){Z|TbZXXWWVVrsC+I@@3}{,c׭J2#fOt^O?&$nyĝ/7OjIͱ)EGYh=/wP 9^xpa;CE1{sTu_ׅ ?jy5i, RA7^|~V rl'ŰYu 9yEUnH7(NzD!X~OBm+ǔil'rreú\ ƺ{a>?zz&kΚ_dEZ\-p-uP*b,XD,=>8) "BޚܶV3VxMA9Uke=ap'"" ?$}"22(X}!룳 l 4ށKk69z\TC35{k"zXsh;]bD̉U0?៩yRqIB4P[ae#РQ_e-M۰g= 03$=Q{[Z 0 ;](K2=})4cwK-LO)! %?~752bhJǥ'ǁ \\ LG}F43ܡG4ubcňX0JL%cM]t-GNPۣ[L֎"k\(/ 9%%%2M^`kFf9Ĉ<%LF]aOjM\\Sw%1e@_,.-u(Sp)8;웴_#jgxbJ9#u [Cٽ=tGWajaЂ,hQ#D*,CMhRs81K!fK{D(.1)Nǐ$C՘Ÿ"K  񖰅Zu1Y{r@%W;ƹZ#t3CQ|=BX%d%KgV!ƒvg]uŜƣj  ,`KJ6pΧa3ېw"D€ r[,&Ac'B^93J*FVPDxJ2WĢ47WYIiӴ G_ }u,w+ݳt/nMQ+YO?A/N?_%|g yA(O#mdz>}ǹ/_lll-///..~ADI9fk?[a 6?cShĮsR7!k@ K=]sasvp@A`/3H0 qB?)Гdf=b52"tI'џ>~¸6`~8ޮ^8Ͽfl rCps%-7')S;pvN0  ~0YI mi )(' dgҐkj()"5P,͖( . C,ّJ>ʅbdPcwCŁ/󞘧LM!8[ lD vl5#/ Mt?<KZ:Pyo.ڂ1oQ oKdm00.D p6ugwME (~rv''/,"wݨtwc'u0vqLQ{ (=p3.~Ĩnmi]4B12#38LLNNpw~,DOq9-ME H60Gw6vrA@pԔ 6Ѷ!azd3=ߢ(wΦ[[飶TuwWqoԢn*e9n0Ѽ>P)"طT{(3-}vl( P(ZA1n%a7)gJ`}T~'Za?{m)OVw:n U8RZ4lCvm,MMQn12jgbJLV0ĸ3#XDUt_",JuDι:f]!R%~M_P5]ݮid`&vn߯Cv7 k*:.29xcTf`0_n sdC|_+[U5]E;ϾX:m(Ύy@hG‿n1 _71^w.[oY&.6,8~ѧkfCL.F?m-\ 8Nȁ UAu}p*ak_xhGzqZ|pgٹ->fp!^ TȌ`OؖnԖV--~gi=2C;KMHG |$Mz7;XݯWGHg0Wۮ];xsi_;%=wIڽ"t_B;'GҞ"T?/q-i:S@ UѧRSG3kV`$nR]|x"L/@o `ԱN!'ou @ y:rOXy -! 3ȯ2A&/=W+bd@ ~;o?fv­ix?=;yn)( c=REG/Լ솀U弛Zz?>5557wM yؗōO+?}\^8?7;333?7}fhKk &K\*\+oTzܨ,/^_Q?:na@n$A4a X+\5c`haKNP篇7V>5DBMI=+ .,FSK eх %Ndk\[yڛ~sX{-Ob3+Z6Xek /˩j󸻧(v#Ii|ۋϋO˟OLߦӍdԏ0otQŜw/%B%VUYta''EO^^#O X'%6w~P>MK*p`~iψlAnai&Ph)O(PE+Z3> e9BvL,<KJTUV*|4tH!漦.X>{e)S #^ŧ|s Q[~;Y./VXX~Bه!FP/ RrRY[m-WU=I1u38΋2񮅾RYYx3?H$D@Jap`p8IG8i1=MסYZ3r| g#?jA ?ljT$_ѺwŽcu 0Xv5#G^dͽ!%iҦ1:ci7DljU2-mMޑI|55a2EhNJ[WUēij kJ_n|k/f]M=([cM$]2R*MH)ZWl|EU2kPT!>LLϜB5苙Q&9Ixʖ_NX?F 797 --~]q/ZLw"ѷXso'j;m ̂ZC;eLo^ C)bmop=f :ݒ{TmZ55z9%U)fi;yh5`cVVk"7νYGn&;7?~w¿rC[wڲX8cIw"23# {'fOw- ? ||֚|Hci4g'7S]] T2OvZ6Ub?YYvCͰ{ @CbpQ6)>uPX8@C |Wx߶my@xI<'+LXQJv7o7_dmNo677W6777k'P/_X ƯFelKTPyn^\{=SM>fTZ&~(T2z ;^8*P ?׿>(A^?w_ @^ 90'V=P珁ti0g` DW#oM)qOݹgf"=OhmnOwVX'3`d@ғmwH_n]Z/xnԪ;Wb/yNyEȥR!{fY>'sJg@$[yJ7Y_Zb_<>K6C6?mشn^V٬JC#>(y?ak?*bg&FQT:@uT!okg{88>-/nԺ=; T{1Ζ&`}"GAW1niyȏAiEP<0t[\w}LPy8":>v?"e%monvv6w9.Do8[! ?8o_\~K_q! @@h m4 : !H\v‰C+^Et`E/694  :1l?Iܚf,'FZhJ|tlsjyde_hfj$+M2䟅"Ύ=)7ޣ,U7C8xzpYvP[e_|*Sc?-:(G ͞a{7y d1T U7y&ܗj"H4uU%Bb.aPQ hFC{ D  i/,MeeaMXȟg%LFCn%jawd?_+jW *B?Wlqf QV%a*Ys*ixXJטK/C)⼏C="21 @jRh܀7AdmlS @ \ Rr.e7qN,JlEe-SyT'DJ>ßMpNy*[8K[7$RK jĮ{iJMv8)@ N J$Q>[qwwwwwwwF;;;fv}n@fEUfFUJDeɈx<_#$$'TS1dZNY6%L%G Cyj{塦G踰? Xiw;/uM]νhm:lIkXm-LK׵7UI%hBʢ[fn 541532ܝ"wQ{! KK|Vf2Eru%ˣ XgO5i65{>rW :-'jU ZdOcKIh0v;ZZ4FhKW.r'ks[ŧ sr@;ٳ kNpeej:aWF !Q(āp3mImbق1!y>q _!PHdhϷ#(@ݻ{4)}${Q\jx~^bv;c ƒE?tj?+yhuYSǠKPkKΆ:|poYC+MD\atTd^+jq)&|Aaj1XerRԭ-]Ε4 `b5--̱ę]>f&/wGO}uWi3m L.GFr MNӿBj}z2 ''7JY}_3vJ,Kr s o!A|h-v,%C߭:]O\ Zh$kv^uGr8N +m*DV B+//:(0q|VDDBP= Ö5ZN 1fg{GsuIZ˽USդ[Wde؍hh?ݹ6odœX\-QX"oPvf֕}_g8-du'BciËf4pYMyJt1% 'Lֲiթ!]ׁ8k bxy 'SI*DWgE]4ml_hw?Z ,Z< w+/q}O#ՠ8fbRYv%說PJR;,4J3G%5 4ifi\74.,eHZ^-JeeS];Ny׮e~KlYRh2ҙTҦn[^on#鹙d^GqT";-nʡxMn~~`_FM5dOKLB`=Z^zڲQj'ݼ,34u <,6mO^=65uqׯy%gi( {735'vyVuN^{daO:Wu5To7ӫE؊5ԛ|*q|t^)v7?&7X hy"\'ssFRf_WU:yG7X=UUb m#,o 0[>L`>y/=,F(-Nc}n*=~iPD g#񿯂TvQ~y]򑱮x, 둈,Q-LA9Hi8PG% 2981ab8Vs(8k!_ &h1+֖-:CZ/y4]ԉMƏW?SN=풖*y#V6N;OhRjthT?B&6T. 8.S&^\Kg!Dx:VU?6UN7P_~Éz*I]&CTY A EzLyZy/* D[ONUE'c] yo-X%6cfB)\`:~h)dy{Yp~}׺pT*x e/"(漉HV+g WlXlR FD9a/] D/鯅EZ!76j 3]reMd]ind 7\Kͦ4á' q2<nYa߷(2t12et&b}%z~=pnwwVTm(J'/3(S4ԍ;~㼼SȠK2`4cx󼪬TyF 9;KxB1gb a}/T-rAV>M M MMUjLBC=y=GW' I" u*'r(*aR- qHZԗ>"uTO ڴ=w2Elj&vz5-#]y<9!$_P_2KblkU$OyjG*}֎/鍓ڏv&YR|GMJ^>|zNMOTs8 ߨ:YUT8z m7SOy?I[_Ue}-(NsRP»ݮ 66a.ia}d&K.=N씩E[\9GYDmdk bQGdo#_g \"VB#P!F(U $Vp*4۟8O:>f4ݴcTk; :B&RddXLX)H_Gaqk{S ǟ<}i[yKBe RӐoL5琁JAXE )M8!!F75P"ۙzzPX# 1~1ᄠ@/44/\=s<׳jk@bMy}4֝JθE矕B)$6鮩YH#=A"`bR42PUrӥ t/aK\3^cJL6ڽ树Ϊ[}(+ pɚfm%ЄL >-)oX$牫ХwQS[3Mw%a3dl)7z%aDL(D9N K4SD*7ikjyhje#}XtsRW7٥-av[ [Na8l& TT1j:".wp%kg2͠.mIӋn۫vMW L`ג!0dyCgu惣Nn 0TuPP2BQL\X.j4_ZzD)O(D`wa3u<<} &!~ 6mSB ,}Y'|#we8ρq_=Z=w߽T,u,"u"^wnl YdxưN*uojUl'  :)8!wr:NDɅXH_ gvQM},vסg~-WeoFJ.)2x_gCQ00zLkleX;y[$wL7!q˴Ϲo h i&T >uxkgyu4T'kiǧ'g c9MT #jJ Z&f?6 .|Jo;8:>:9U.OXޏ-6 \mqvv<~qq`\$It#m}y`gU6]T0l*A 6qp 5r>HˋWï BIp"N!!!!f1VꮫEx𹉀Xj4 H$PȪ`.ۚc? O`t{kD&Ʀ]vq0˟)$5OP;_=<<ϟ?`vvC|SOr {CD=jyה*s>exYVvRZqsVgY*=?׾SMLkt&_X*gT$7PM,{BK k~I%A|~ZOo)toA[x彜˒>YIl< /`;B[QK< =}4j4:0cZp&h\$Ւ/jj9w"aet89u.r)ժhAlY7J ER|kU ewU$?%tl3tϵ.>94CF9a4|B"Y Ǫd47i%W|~Pyu(vJu+ =$Ӎ'+Xb=w777;@W+6 #BEC>>E ^_&lBc{q?exL/iy\w v, ݚD<{nʠ͒L! XژuDWR0Q,AлP? ˠ6?c mӲ7AaŶ r.i_$IdI~kEA}k;~y )Oj@ >!0(4h |>}p@r)3 :kVh=LwdFDi}{ \NȍA!R~(~óB"1]+"#\ O6Q׭^ )֧!{!;aGBBE NDf#DBDBlDn\H3"ҧ+ZD"KB'ճGGM܉N|2LJ+,Gbt8T4.YG7CWM ԋQ1!d~tP/ ! ?z0}d+4HvFx% K+bdtLzoW:O=M/fWB~QHKH a.Ebҋ7e4Ej9kqH|&m0Վ,h3kw#atӗ C;km QdSQ Ҏ_bvG$-;֘3t7(YL'-'GL'GL'GL'GL'GL'GLՇ'{H#f #'WQ##T#%V#!U#&WxEHE>U2|7 t4GE" KCA6C(tDVse 47'R5^ G53F!kʄJ-I#&)X!_-jZ1HDi8dZ}")l!_-"cZ><>'d_Ltk˓{{ v3"3"2P"@CFB`Dn? Ň-PʽLrM(Ȍa/(= OFv)C*.I#ptQxL_&9~ߝRR1sssIۘ+Z;:}|-k#s0sspг00UC/Ax߱ʿĿ^\7mLR@{_H4W|/hn`jW1K[ڸћKGs"sЩ[;۽xe@mg{"։bW@+@7d O'hp04 ȿ 9VNTE7@`Hs ;) @`pxBq @L[ 1D@ P(T̀@/`0,[})p x|dP  h 8 , \  r * F A@\A#, ,%,?l l w8888n8e8 /\ \ 6=<*< (+||1|*w !(R~/wh ҈f>MsH0HHHHHIHHHg0Ȣȭ7(h(((((e((GPfQQQwHDL !)ѥѭ+ѧ/1P1040>`ctblaaR`Jc`Fca.a>`` ccbUaa6î^~!DZƉiǥUu=C3 ë[ŧW?C@ ;wVu;&D# &va  ]KO?PTl’rZ&^ɓyÐs['Фh8Ħ,ܠBrʣZ梶ΤaIfAq- ӻїo3`21343\01023bbga*`ZgFeagncfa1eIgY`E``amabe3gb[aGcgfcp8$4\BRqKk[{GgWOo̟˿%@$`$#%H,h,'#D*d&T$t(L-l%\!|!$$(SGKG LLR,LlJU\] $ =Rbq[ZZ^Zڸ@( "]qD}=v %}J6HƆFFFeFOƊy7&&&ɦfBf fqqGǿ?L<ӀWVRVV?_l4mmml[PuurrqLttu*rqwnqALMRn ݹk@`a#Џ?~$6!+[g7#@33gϻAANA!!P_afacLIOc̑)/QQSY11v1K%q(qqM D a ? Gؒa]RRZRRcR,Eҫ33B3~fee eUedg?sVr%s(! 4 Kp‹튷JTJJ9KKˢA]ˏ+*f**[r1k55'FKuu}\U haM M.-Z[f[eZxڋ;;;1:`>wt{t83[_Pmko`hdljniZ^_TX]R_ZY[Z1[9ZYZs[{\݀DL%+-m՝]=罧IG,G3''gA(T ߄M\j]_9]\G|^Gߍ͗[ǟaw8w%\OPO)mdmؾD o@A@A^4  44,4,,,ۯP0Hoo^ᡡ$c:ug!@!A^,zY ~@@3 C(C;΢b`8_b֒]Ra:c$k"듼V(`̨$h,"~4WTl>%c#R50r#6|>k(!{"[lz|T- b-v]bךds0iHgFJ."m軬M[@LY7Z>9[vErHO zA "[>;vx]ycQM@ÝyT{c^ȢV`pJMv,& ;VvKsj ح-]BA,|ܭ9DX8HyxltV'R仜u fڏg>z&0w{>염CRC(_4(Z RΫ^QeSptp84I.Io/KFI C@康-ZNp:#gz,;:-/&ղz)= WZ/.KhI̓wYDc{@c^ק(i,C% 'Slpڑ yCAXݲw]ˬ@D(0ʄ@m3I2K`g@el9L ?&*㗴Cl :K~{hD&6xpR246uV.Q,[xC:̮VC0wTg˲3nݼKyܭȵ6$PXFM|ŃC,d@qfe@k[seT2:Z!&#~g* mlcg A *e2+mnG&R?7M,H6*c ^[C?yD v,Z̹:S".G1ZTD25l~dIc=E{JxVRqBT}Ew ͞,'m4BA2?QƋ:NPh8qHK.Pn!/HɄ碩,}́AEz|6SbiyVD#︊dfy 8 C+uT222O9Bc0|)2&WXPQ(㤾5'2O8x*z2ھ HjESUfcj]A:q+srmkAiJy4fJ(IW#͆>"CH;\1půY2>IW5o,  VhVɖ ORm`J\{)uUӕTW>AҼweW`VHJ"602#x \y>gR9,HsA|sͭNELՄTc-~,SYCtY4ˍpO2'Уۮ&xH95uGْLf!ZMhœ"1$ #T*6TῤiЊ~9E.ZK &HZWAU6#63Nsf`+_XAuI,H lRN< #Ȥ2 !K ԏEL ^y)؟%?\ $ ua23H:ٻ~zԐ=sng\4=>0BNf OfB/vujV~n/r+1ǃM)T?G>,?܇XW{'eD-3cq3:O0*{/4y\f֑4!%@0lKܭMqsWpED߿"Mjs+)Y>xtcwTGu x@FpV%PU˂XZgt)q-"@h-$UD޴ Ho7r2?pfY;~YȦ`lMVטga j-mᗞcʘ#nO`N ١I]Қÿ9/?1=xv+2έWw}i?,[2qPa9e*l^@6''9 t0UIQ2kE׬zdĽx76T>YzY ~yn\dyAnEX0\ChUJSp`S&l+z6zq|nVd"o9uCM"9bߵiȖ8w]&8?L dkc{WpH]LK-Œ%+Ʀjz02 xacKpvThfӷ1y+i{C)Qs˔UIRRV~!JMy|!a #-Oyzܨ2+gb7>}iLU\KMEd&/wCt葐]՗zKe~Kn@K-0V蹵l3 ΫƢ宣:nbZ$^L;~kո7_U~&1;u 86ueHU%GJKT虧2I:Kwo1gݚY?YI i4T?ǔrtԾMD8;v ƭ<N,< al`5`y˰bi!DMHUH S6A'cf=0u3R0 = aMLkc= hؔEלt4922-<]"i0< Y0nPŠ_x:Q#p/̭<1Ws|f)!C`jv*ϹLF\B2nȇU f۶cm 2-)A]MCqVӂ'M UXtm!:/ d0X63r<%6]ᔸGˉҟ9$lu$(lCj6}uWu|3p[zR8>$نׄkd^0`3nj$Msm

XJ[l[Ì~k)b`ʧ#xc+:0~+.{_aUzp+fө΍}«dC6:UsFs=k9VXڶee>ln lUj,EBuI惕E Bu=BҥipC%(b&D>%{%C86y6~3: "c惘ĶZ! h/F,Vn {- 6tfe5G BI%1 #}KxL lGsbH):PkSuY ЋlQ*IlǛm_ 5ד-\d֕$Jl}Cd0-LDN9驢ML&d3DM3d&w_ގ5`T{$/twGӥeez>_-RÒPZtC%<[Euo&٨\1 vNS3*L|ER3O,v8D#$n̎? 3Po3mbG2\u|t_jMl{a㙡/*˳(T*r0,'K6eͤTl`sn('?Uh\UxvCp#D#e9ǛfX. |UVFeN&:dt&KC뱀5m3s,omkE˪B,Ӳjӫ#XkCNg)217h6/IQkX=);٭Z˒}_hꓷ׏<:uw%>;T֐1!plY!+Ӗ j /M ve abnE,ӧ)ٛn Ibή*qCIm5!-dd=gw2ε){^ا{muNI2,͌tgp #GeTPFQ́d+U6@X*kW GX,VVf".B->3* 51(LgBB*C5 J{+WVhԤc rL$ }J:QJf\Z.0UHi )V7RPR 1b4@, )}ԉ4 G^名9-d#*'jλyBP#g^>CNs;~tU5G]_A6Ty~P W&Y|:VCɃ(gG#H.WUR__ _SzP X9h>Ux7Z_x[q2g0z 9LD彊o&y%8 >)ƗuFwvRP؜ 0׶BoWzbxʨN '[9*Z ~4)JM(.V[xmIX jho 2ʐ̔ 6ؾIG.`u0n͕7l+#oE|>*踡k&nԔ/0P@mZ=5L%p n# FNlcj* [XGRKoL1gPԡHhlBP0 uOjq31|3w*L_T؃Ch*%r2OR:AN&Ζf Qy"CW&/ePmvT?Һ}h1s=[[W 9yy|Ӥu;,Ir/Zmvvx١s0y[〲?ls?]59hОfNZEw\1Mj,Uwm?WPF`lu.A}OZYkߧY⮞]F4Q宫,f- dIKdO;9:OS2uEãu;2CLAqftWsf,"عnZ86rB5xU6m//j^0YxFrPh`QaraBR)'ƼNF6v1=-b֮D'=, Q=A2Qh;0 e Ц ba(kؐ$SIpGY-96-ĦtC$=)|Hd7DR͖brFj1'!ڴcyr)K5RS?sСЬ*E$GVb)pĠɯY*?4&fTX(Vv5 7mjZ'S8h4~K4U*!sQ!#':v a6fvt-yVw< bя/̂ƏˋZוڰ(H+oWh75@Dyn`uFXԷp MC;RaӍ.hB<&xrU9Zʳbj0V w) l6wÏѫWLq[å7[9wޣwR/x ]kGnOG/Ua!27fF09;7#eOf ~-MkYoElI. F1[)/&E}JVv_ t).>+K3{%&dE_]W9}:ƠV^J[_;BkFTT&>B[M)x X?OO?UT S%J*J*JY/"7  a``aa`fw?8 $$  + !鿮PO738``uPQ! HcrgYDHY1T1kMzTDɰ/P^ɂ(M;7(`h$~,V1{OKVw5厠γS? \ wC-uЦ= ósPE5U8R1В}7MR41kf =@_7&^D,.<qٯ7 qC57?y?TrfR#*ס=8PMKv"i7b*2HGT :p6 XVg6ށQqtЧCm#XKgVBA/p$q&8_r7}Ml|kudlBA/\\&[&s -iK6nwمj4zӶ d]U &PDH[IyHs?o$cWts2tz\ ZmMpG *[lYPqyq>pcl*A w{AY`pL#Ud` ̊jk V36$Ȍ$">٪~R b" K4@g]})NI/aE0^7[9RT 4dUnljڑgֲ|pRi< }=s&K9ZO’Y'R)7~YWn6 TԚxT Ujg#9"(Lk['fW(I8Id_(E9C  8XРIw$!DSTA՞ɬlУcp ¦!ڸ1HA@XEɂ ?.eb6S^`zAƹrLh4tde% ywSGIByt= K { ?fR !g}qqJ!xlQ&x 0{YنB wJQ%PDI89 C%ɄV -䉩`y/>s:fA "L&HM!JGW B~= dJW\ pl~9x[J RZ4Z`d\jӂqU1ht[%ؗLc#?2VLIc.9'=5~ |Z:ez 8nc8㾖UVx~#d+n$3L}z),ÙP+deLy$_ KGvӲ$RxO-A:|)yZ$EV,E-r?]9"׼;1D3E1־SFM< YY(7ԇ\+߁ЪfҖK,M-o29@u#Ye]T}gcayd`)%e1O< jthlH\p왩Rx<9/N}9JمDSvǹ}14x0z2JS % D"PP@,ռ!ȅ*BEJaR3 !p&\ Djcpv@@d2YK>ݔ[SxQ Wi6#w%XBW\\MSDpƒEKD2ylb;85 MMd?z:YiH['6SG;ØYr)fj4)d¢jk}5ue9ghIݡTpYt$RСK?Q`3M(s4q$_O>˞SCE y8P &ѷ)xqdIuJٶb{erE{iD6աvsY`IW$"@򛪨 Wߟ+R4E `$IBo9U9U{=𚚁rZ 1iٞf@1\ZF?6(ǁ,s"|$ Vɭ?^9ۮj%4}KZzh`w"%/?>'.nvʛPD[{h :OiGtN<۰]kpBT~c ,vLwf48ǚk30MXth",e9˿і A }я6wuwk~k=H T֎gJd_ dP7S*W¾_&rc rd9)Lߪƪ?t(:nn"Yl -k s-RJ<Ɠ[T VuViJӸ{ 9ri!ȰBjJGm$prFn$YOz]VeHZ13yMGP~<]nx)$@> YS-wbJdzյx S}Bﰮ^焒ЯhGhjh@9_YN5g+/zSޠX1X Ez|it#9^n#p- | K\%y jm\.נw;A/rS6Ux2v Ѐ }ff^r. ju4 XuH R%LlI왒Ó *kfW>$m36m,gE̖WVCqO>y &f8V$XlTVZFLF"+2_}h(y+.,10d !aBC~B ?fvi6jFce3 vY1 ޯ 7W lLpTj?W$D0Y!G"PUs`G3'd \_PEʒ`./Y( }2RS|)g#u^]PtwG2JpܬT= aJ[tVQ?)R}yRc9?\k,ҽ >J}ZM /xacL`[qxlW=0g_{|kЁ/xx}թE{ -IBs}ePD^Skmu 3=[)+5!DGkgOR:`I`\ zK.gzy7'ޯWpŹtEv֗)-c=htՠFo4[ H_3':%|S 6XgwL4P7|aÈ5[ɥl9O%2N3zB8 nk,y3r2(g1lCu`Û fN0Y y(Tf VcA`öCRg:RŲe5 :> ÷rpśLwi`|HtF\׉m&# +Wy}٭ҵ\e=b+ظd]W};ҩIтM Q:߾6ywK~{,*JIڗ>X**d Y/E[[*1xu-_ZWG2_׭n-^Ե^YAg?,tt,ONj'|&H~)dbN%O,2cC]sMiw&}Q,_YUMeT`)&`RE(c ɱBzK/% 51 5ͶTq<͇+9).㽾 'M7{[[Gjg6#,y6p{hoG=ADwG?YY"` ot]ALw܁A/޹56kF~lqجo\i˗$d+ܸ:eUT>P(wj)iist?:Bj{{>7{yrk#)v0Pۡz4^k*ޤ cÄF']&z\ι-Gw_ }_UL>zU>y ӵ'nXgk;XFΒr@pHh510 uer.ʝ@ܨ5m\Q ¦l2:XOt"?7ΗU4s deEY'q\I&Pw(,{omtyѻžɮzxzGM Q\ r?QmNUIi˃OBR?Q˙ݝtryMU@1C͉4 mR6ݳrцpʨUo~񎝽YrLʭZG\;EF70npz~ rG[H>6n8}͡5*{= ܞ7̎CJ,6ODTUX525t]k a !E4Lu_M} Dt犐D&LP(r?,r\Mbl~6{dOsp ʧ i˥,6p tTЄ&y~cK%TI_%a{έVWi)+u"SG7.һu)k3b_fG|dhQa ̚x :gLE|R U(vIGVX$~rJSreM{sS$$VMBٯUM mgCƀ]=Lnkl5x$`slGrUk|Y]}G,ON(Z)k]ڬk\ e4 22pKq-S%(Y'w]H}&/|zQX_X/P2ĕ;w6r(ٗrUohAt"/;IB׼JT֎4`?XK>xI`m}0z~ ?%-HMp.||"|^PZ 'ַC=tGlK##$˜l}=*>9ZO}[`. -B1Q\+|+F/ZOnfV1Pk:'tF􋬄<%Ry dJ'KD=Vj+H+ZYʳpZˡ99$pGYT&ŵU#|d"d'w@91MVgUjc2!E&i g]~Fh[CE&TKK((|oag{U; 3VM ,4)?:m\ldY`rhEfm)\( ؝ݽcxcs#bf`!babddbd$baa zoobL$ VP FdjcO="' o~'JOAƜHPh953q1&}+)KDwudkzR$[עKM΅?$Go~+t9ؿ76utwg;?fW1']K77t`АP0y8)`p(dv]H(Hp^oz+Ǣ@;:֣cp7's  /DAP^I C0+;\n̢i]KD9S\dv T;q򙷧ldrqFKVA)CԄ&%fI+f,W93;x73v]DB\~. uZJqpڣ (x1猬vHVp~F`Gw4{ !@ e;t! <2QOz]sn]CZ[Co`y<ynYax(!I~&4Cq-evԕL9/' O[HOiyd ;^Bϴe#Rr1ucޜƎV_4wd>9]zqlv0QI$lfesOɥSOS1vΠ,ߓlk+Qkgo-=ۙ;` ZF5nXPoN Q-ʲi-.w/_)[ڰ{0g_9LQi$t6%ha6D&vgqMu<%2Q1le(;KpD(Ω _${ E@rj597O|.jwTm/򸦄8n<xUԪ& xNq…;SY_n>a+2 A~, _sq cFcC+[Gs7{&f5:2p?e햓Ao;6i {R9 2(\" %"|2,hNC"3gf~ZJѭӗ($Vσwo.oz,9lN!*R:N njbk.\3[wN8Q'Mѳ]B4x滍 @%q9nzIS ̹bhej۞.\QgVkۇ1t"veDW+ne]= @Ò1Y]^TCROdKd] P @ֱCG7]j0 h5V}Bޔ땫,1#MĘpTzXIf+*hWѱ*>B %2VP򥉤s=8on۔>'֯SOZڛ;h\ӿz?ymXUCgă}i5]rUv{ luV7gd;zuiC{3:gXniǠ [ߪ?%wP o " &2s)+Ul(aK BB5lPQWZ"]&)OTrt(Y ~f!lLQ&ؒBGfVi)Ih(Ş{rz"nJ91ת> $ӳHӵ8h7Pa{6^p2 y c>7&\faNrLd2BʜFҖsy&=xY0*!rs42q sC$a|xpnNxy.i{wXejmJ4TLNy[87ɂ.M g5LM! G`N <%[B%٬pEL M"cXTN6J]gKgKcN΁)8YNrD̀K[zi2B iW*@Vֲ}ʔڈae!VMeZj^A=wXvԻè0\A,Y̽BE>3+0ԚvR,?) B alouT\vT*إ2!يXsbsX7Guj4. F2CR5:_lHDn)5E p 4q_/sLҪxa)g2ާ/GXtfOO,k͚ͯuQ~) n.AHXߋ$YSp;N $ r2ΉQ"1 *(&:f'|ՌuGP~|4Am(m^&̺V,_Ox<"+jR \`+r@}eD^(-׼%F ,jUa_#2Q9Ӱ%l|IVx:=y;LMR%u<pY\+ 䩿8 b*zv*6>@k(&59u+GWjEco|Mԣ`.,8NJ8S씍dLeuVɦ$0Y𳬒iT-GٯM@? zdR;:R d uK9loz PO=U ":,pLx6h PX'f)n<9+^V8+| %l)bHYCc6UP6D@ {]B[RW X/Uts [R=YvB@Z3yRi &ɾM!ǙJ!ʜxwc'?YPyLU\ta.LQ)bib {XU=B: U:+Ifۗlz{UI0\`CQ\JVwqƝNޒPxe;%UF>t"9ӕR PI+<ܼ2UlAq`i`قntUihQmQK= Ȇ+c>/3J\Cְf#*4ZY9J^Ee#ˎtL^ձ1՟_ SԐώm@(jmG7IRʞ2y҃ }A3^;b.;9p"؂\%R1: u#Pkv3Ì9fRb %(oREqhIh8/l*¼g%? ˱_Q$A=Z%9H{6A E "zpz.h^<=쏼4&z E7A}JyTlE13&1ss[B<[#/ E"fQ|2`ӃeVD7ou SjIh7E6蹑K79?Jڄ1kv"4ȴyZXc<WVCSΨW%B6\tԡ9xorIgX 2Z Zf GRPV5Y ZԻay 89f A*CT-g@bbE\MP: PԜ+'3˱UY w͋5=.EįqU$SɠQĥ{7*l쾓WJ$zPp/a !ܫɺq mޢ+- pF ,(cw2YyhLeRA#0SSS̬%U ,U9 z --84#祩)DasctRU˜PI54( RArI|+㍻Ŧ +&V0U1rTm[G >kyƄ_{u!lmL8μ<;̛ D5? E-8(iԠRQ&M6QjiU!}ٽݻ3K̛}}c}/ols<}o|>_}z;z%+~讝;On}gv}Ͽ2zѩ{.~癱|}Bn3Ho|_*;=?#>?>42?;c[a|vD慩`?vמǏXJO&;5z=nn&? ܀C[c;9G|t/mxs_ bEu6 /<Ğf.\ jPghFΞZPBfGAT^\,s >U9{D& Җ W"Jr/dbg)CmYTfL8!>_\ (_ܻ,M0@sv;ӫa_?d&"P 8_- Эcss̘`RV".` joZ-ei f uqyJa[oxiW .=ÒYb}  ;1.d ert'TV*ѥ@"XAl/ǬXf~YKKq/dA2kUOh k侗!Di",홛;0G*d2b^ +fvc.@?0Z V0*Qa8Y:޹4c`6Dq?MO6mWl2^`y{\yÐ:5UOY{HU>cH_P GraIZp*i^vr{GHikC0K4wu$&;W\xA]&\4i$-3y/^(raxشrҥkäiXU"n- ysh,ͥӏ9oK> *1!˜Yb8lLwkl{L7 D%~bGCa `h!= pމQ7z\f-|P( {R.N@2R0Ї^ےw W>iĈcf,MVjWw0\S=Wlڄ᳜W oj+|Ȗ]}ۮv]o߱s5ۮvpM7txo?7_y[oܾm|ϮSOo2~a[o盜/We?wp>y O˱=/~SD^u WLɧ~ޕ읿xGn板噧4@A˻ʯ->2<>]l?zmu_ߏGoy}= Zߞ9hl6[tn(!Vx{<tO˱'g::kE/DZ"13AǺOmo7l.˳l/ Y愮O#k۾d4=kJ@~,^8}@ԫR'[vA:ϰ䑑dZ[r X+hˆz7.]Ś ``H \YHa&eI#5awef, k|p\Jcm2KSѩH`\H__7oK(r|k\y|X^|'0c嬬nIg,S,r*vۏf?hS5)v+ ٯf:pcq'<}iv"cXX/ p12 w&fVϣ)L# ![HqW AO|r-VquApj aD'9`Sp߹k<^3d{[vI CnepH#`b#)Tw©Gl+x ٶX~#Pv]BQص pNV[UZIQZ*DQ)2 /Qh.("@ۃ(%κNQ-L&{ ;uB, tx1x7BS;` 0Dz#R~֝JYm@\D(ٙ "')!HmO\W }I QT qe}f!]Q";wSTcOK G b(9 *2 r:AIVӜ؂P lv+*,TB!fA#cƹ[Y"Af悃G!Ak."ƒ4ht,?n\AFicP`#4*B}@m BigUƞ )0jN0% Ab>m Fᬎrw4Շfค:{3E>T%(+EQ{\Jgxh)# XB=k# ivx czYa 0m uF^ %ѕDqYDaMXnu8 l *x&BQ/txvdGs0^Y.9bq;/.>Ʌ]2/ aL;͆s#&"t/a"mc mR<ZA3j4Ԅƣ \kУ3}ʐ9N.ՂPQ!`ϭ >90*=AW:?j 4T1BH ~ tA19hB(ZJCW'c2hl ӫh6ܻ0=/ShLM"3SP8aPibN֗KdiN{fקAP?$Γ&U";*~rHĐq\Nxt85̕C5!\r)T%-8v( :^‚G Q 'Ba0Y]r~db M%6ZF5Av3-5ڹ , LYrDC6 (T=#9 RA>+XP\ĸ*` ƙkl{,BDN'd}]f;WKh(]0T>=v(@l5QZE֕sEatrvx tjsA_ij{(v.POYVR YAstй/<5C8VU]>$`d_.ʐ, P2I*U##ފvur BJjaЬL!q' Q4}F4 kqQh`q)LUↇ$/SZQ;0~Kh3оDK VZ$M'$ﯢzkIH %vd`TQ.=A3< @)ε(OjDI$@BL&uFnJ $!KAEm(A\0hQxM;Jɹ \#ت]X'l͕uc`J U$.ɸnIinQ,bi]6DS6!(SU{c/A@^אKviR&,g;FHTR'.#ɥ1gjG2@ }mf[a4F<1W/*,N7)&:\lN gڒd-, t=yOJ >- 4.Y0Ch}M7|Se;V1Gjsqkȣ] 7A0 ~tFQW+w&/V\]DiYRa M1ЬWwة;gQ\lXsTN2Bp'vzXQé \;ICp"R'U=&XsKQyq֖ h{m~kvT㵌лD<S`ZbBokUi0>>ܛK5תUk҈$ K;l2WHbI"k{GE={ȗ֦> 2u+ȲB6jk~Moz cmaX!~@5"hf%uKK\ sԾ4kNcҰq]*5c#Fsڶ]nWh?ԶEt!,?#8n߲]w>\RE߳VJ%w͵m`fOkIuzx%VTFe~tJ%-daWk*:n oƮ$k }gO]ksNYP`OY-79>'v''~Kۿ;uʹ?<>nx~ nΛEA$Vzu8 V}x=xiyF_11npVdSm{K~#qfrB]R08tfҡX 8iصlvڋ(h,o vThm64DioT֏PvԮ&[|ZțT&") j h' ߣ܎ͳXZ[ #\8oRhM\j8!coGԩvmvꬮvvVqT{C\ GAwAP&x`VWw۝$17$8]@.Zd.0}57Vg~n6h{kƉ NS9ň^=%츔$~\WW+Ҙx0CMLeҌWn4&VT~X]'a^P=hf y6,%Cխ%=z`ˁe|v$}KH dr$f p{~!5xC-G淼!1>ӛWf+lJAH'hQ:ӁT;ߴ 7ןZv@R7~ (h2vulצ.K]Ø37,zD_,JkG'{F%4CEʺ걣Od:'-DXEffH" _m`^d.Dό 2ݹA3r1.jvъEE2d -u|u!m~ m"N0#*h]GB^LJDڗu'UN~BV2oZVWBK`}do_My˒$9G>Bm"슮vV p+GHyAy6~nI.,_x::+bR yx7>Dq3ZiBl: /HrA#",E Jh1~ZC,vF GJ1`}f& 4q*_FӬ]NXK6feSv۬Ŧc/̓&=sMBCr")f24AZ\)Lx F[+5vٰOSq}?[6=:8vzڨ豥~M#?Oh}餤u3s/hmZgXI9#?dS|wx29_X)hnqmYG))XᛤKϓeR=J;I4g$fow<^7]5ٚ1'uYf]qx E9ch\ѸWǥ'D;OLuiZCJ '$H¾{GRP _J<}կ$-jL;7(UdU5N6t\㻚Nj=5cOfpLz'*  )Q9n'Ƅ*kk6kG9qm|?neD˄A {R `۾uڹ-iCT "B1?GP1ٺ6޲ Fh228x`8$ |Uol sZ0I}[OVue6/ "+ڨ6mDNu#x=msj-h_=`Xe}kj|f`6͹E^,>UuQ)J:MKZvcb3Šzǥd"F]ȚFlHyr20T47%fudScܭĨLgvJHvSc"~6F̼._KbITs?:ŎQ{Ƒrގ ;Ƒv#CJ cjb ؉y{f|~.t,-"psQ_-=lZ,wnNڅp_ B]TZULmi^gmO.,+ܔE}Rzԕ3~?W@^lH T|&[|IcnMD1T^~MsِT:qm,H̥~j$ JԒGIr6aKw|bb|fcnn /i葌d*#Y8w:.MOӴI6iʇ8-7iVI~i?u?i,3XJF+W'$L@V9'\W?(Wosþgx(g5{@-_7XmxmHkWk$ʣ4 z9.mD\6T RoNش*TAdo`<֊2*tIg4ĩ*2aLH0DVHzeM8~ȁ(B\; 6 ֭5'Hlyd,Rnɚ @J.5e#LRN쮧LII9=^p^(je(8'!R㱅c%n?Nq#--[tW"]Zl-?G 50a%'EQ^T Vj nE6j3[O^kkSɐwzA2?$I^l-L&I,jO 9u9  Đ^Y.5Wp 1xoΒ7bV^,` Ϧ7y4ou\ 9Ct*t/[t]VWGZ(3bHpIzpNO{Yz1N]j֫/xEEnSD)'YVJ}/yy1l+eӋ:2A˻ 7qkzНV#)=6,]IS;4wr(ʋ^q1DV'MI<C^9ڒp22c,¶ :iզuďI-vHAt^8];ly@-;޾Sgco|͉a `qn>xi:cU[CwmB gEv>bD9 >)v3o!n\`,' VZ DJU I-OH˳ڐ b@:N3BH֩P^x )"Ln$\-:$QMm%J_Pʫ3"=ڣ}SJ^3W؃fb*3kYbugp~w󋋵Sxޮգv| VI?nŧ}!]_7{Ng,S S=D6,wGkK}H_Y'`MO0s\ 0zm^ܣK/ I|,kFAh}KڗxKM8<ў}Xxp6VWOШnDhvާ[@!ł3>p. W6Zϩ[*9*:U$._&6eZ/XlUpBzf!vj)}[ʾI[)zPiU6K쓎)<@q}laő- EV # vs܌,כ#cshl ßFa"~nԞ}c<36Oÿ1jQ26ccc6z Mzal6ͺY56όzxglƦgTj&0¨~¿-f“"z#hnnz~ٍ ),Z˟Dk(κUR9xxa76؛ lSnBmjFʧ3Y"բ2[\BI.?nkvDe=\feiO dF`yQ$6hJ-PV?=ogR z^Szn Vʁw z_%̈y޼iWFGlGNu즹Gq|gq+Do0{*U+7څσQVʮK>"hy|W;>}P~]v^կ6-gl伂$4vCqrHŒ3$p#Qz$tӐ9˥>%DFbIḆӉ| @=+й}c0;m ԍc"M&M[By| ]KᗷSE4c]F@PG&Ӡh#N( +-6$OCO67S!!g`) 5qWH^p|H.līeַ~_NpNҸAAgvLb畧8 h B6XТ6!/i.^بs9;lʳTd3?{:vS8I0#[$(_>\oX{]ХY"B' Sy ]X3Hpd7YB. e!jEpm DIiOF+=w#i'7ߌ|*|g !Ep'@u؄>[OĠbmd\P6^d'B| AncbjWXD _њ/6$o'R܉ Quox/GHOi܆3dSS+NjCTI_TԚ.L4lr/ 49f @^JǤ b? Xv:Ws=AG@XCѢ N1mc+GrFr |h*aCI#F}M`mwƐG?jn8Mqz-^|^ LqMe*o~rjO~ -:7Ǯq[6HcPt*z'G72tWM#vŸTppf3se|f|Zփmk5 +KgyHcwKk5{MrI7=H9{B9!BmImMs13>I%I姿Y3hd_Unt|BQMJ$1ڷsCy'HRa Oҽ2%@P Ⱦ$A΋>{l:nc?53 c7vz־]z`9H[smx׭>V;kahR^jPl>?hv[pn5#MCRk[߈4iW?w'ct P&ꕝmِ ҐHLRw:kkCcª Njȭ4:4 xeH? Y\'׀Yme,;lm[]HJ-b03Pp q`ơePW):ԓbfm "c׭ьNiQx9NF5 sb|RcKvTm  ޮR"%'6 ނ]]+,1 |Wjli G2Q~" Qc싵ϣ[^gzpk]5Vy.xJ`ր'Yr`A1 0ǒ;13 ??^9Mp]> 1Ǿ|FX!ַO[orDd@-1i5JA=VÆkI A6ZZSmn^{G6So[">P?޲+ۡǒ6%?q8~`-rW]„o)}j}>C'f$x#T0 !؆*͠W.6f_k-9k6琏Hqs:R͑8xg|d}Ul*\iq[}>:)N3nw䛡̦G^lnmO(|׻>Y^U =,-H\Ip+kb ܅YYY,\(OgQUs_ cHkln^#ȷ!(KvͩFedwdUo:Gm,N6yͻv2K*YѨw 8@ P h Nz;kX}}V{~N_DED3qA|N4Z _'F2I[۽n=U!I&r}jХV*pR3+TZKz!{|ɨiUr{xrqmm 2Cfxb]ȑ}i`k'͡Qy G,:N:^Z W"%?:gщ@)J8cE)-j˒uզtACp\HjMc-0׼g?N+v+Oljs=;1#Ypӷ] q& s,N×!0oϮSwʏ\FPu,x_~G pK̡7V7|ܹ0=䘤{D,U(5FV+516v^Ha=͔[鎾w}N UkЮ@]{oq @l}RL/`'[qiz]r|JnD8eTdB9=-Z_ !TWlnM/'`nJj~]AlGw-mUj84g-ErhBBe[Ԛ{ja*kX(%V`S[H[vn*l![CNT^Og|r~? ,cy,Ee[ O/T߈A$<[e 5%8PA.Mv 2?Ɯ*{UTT%, E,׍ҩT+Mg c-8lj~Gvr#{SlEx˧*kV3ZWxmiȠo5Fiφ9m%eg`X87ۭAs7Ө6Nyo}ukHsc?)KD&w,7dP" ]*#LB"UV~z!WN;-đ1(a08r^f~NSm.@Hrh`S>jbƢ-J~V$i;iZiUFeu(Idi$r%'-rLp8_ 4b"L;=z\lP?j fzpR'ۏKDk/K'ډ{\ʝVvXf{gՀZm;QxR:oæko!M}njSzM ;.55 WSgdSEIo_=ةֆnPmuq{_o˧T{* %~/GsU%ċ[N)\D,$Oz$?i2GI88L)%4_Oydt9$̀@j2GpcHl0$LWx3.z.z$&\l6=gt\G*錣dN)-I(J~tgzd:ǽNy< KO,h3 z{Y<Ͻ$i/f^hd?lOgggADS M`כ'8ӑIBiA81|Qz2 ]  )dvF`mZi4Hhy2>ʻɣ%ᓀ Z>9!$9U>㗜^#Zar:w )wgt#t^;J(u40Z)|iѨǧ4l(N%B?M&YHX׹ EFX|xMUp$]3<@iDu<x) \ GFC.@=̇~M #Z aLxx|9O@hLxϦ\s g7.0o]WQ50eLTDlhw͟ƴMdDpbԽ5:g^ !m_|ZV&Ko׿PXJXc&PA3PCMU%+I oJ6uǽg*C{ gLMpZ(~)FNy8h ?0%>{zs3ӊR=Tp(s ]#E#1C{J@sjB;*_Mho357Y|Е{x@{bTiicZDagg0Zcvh7Lxu΁ä/\7v qc;SC&]|:1Nɠ"}j}=ا`w/0E=6!T%'p~TLp"M%+VZQ!! UPc{Y ޝO3 \ϙdr1! $G3A'4dv;bgK-Rx/LmYEx%hL]PD*{سe{zCO{Ydd N/GI,B;xFK ' GcV->X] hd4+l;,i| 4c#4چi.yZ!S@PM4oh6T*1ֲgfRgGb ў(F83zD|W&R]\+} m5,۸Uu u]LFҨ9wE Z `q3ewgcDj̠ޡ5b.,QOj띯 0)bՏCaj8 Hzׁ:gg:֓qsX5KN4:*4پPD8=֍hiHUS@81HEY1Jn:2 K9PX*rm|s#G9 7ƛ} o ^ivy s5|[~륚SػTxH!>e { HKMD=oBLd3UD&pbFiHB'Gk,6D8_|\tI3 f0_s͝( :ZLm4z"}#r3Wb75+¦8x`Ku2{Q<<6Mu>{&r< Re;a]2Zi1RVh|FIr0f|C2̧,n\Z㰱$FXJ(I,nh#U Tb .0͔E%,GG8* @M]ֆے U풽DLo dӠRju3f53W4Jd2tdoBȈUJ~(٭R/q3<3BCd(r 9E3Q p81.'W4XvuqRYh9ʗRa8JVrņE۝,YI4J:hp|<čɵzGv!c)KSJM@$l *#0*˚. #u%&\XO b,"'uϥ] "}bш"ğpqi|5% b;޲ cѐQ vd.pС8]_@D{t:6C Mr3v*7~J. I3Vi{P Ƣ|;un_~ͯ TҌJ>']#|bq=fʰhN<:M$!T#k-i],G=̲ϿߙiG?1iH]7ٴu*d !^t!oWCoJ$Q$B\$:=JZ?eޮHvH'ifJiw37'k. xh,PL9OKRZYf(MV{94C}ej#< @w=bsdR]aaTBB뷴x>fYv0 fJwy+Dq<7 1nKx'_h(H:V9m̩SX#h(Gp8kIŸo˃gJpF.kWB/®u%DŽo++c~=I^F#,O<B[Yjg=*|?ǔ-B ,E,X/r,Y$Ga$`0atcf\j 2Rx H&W +|& *I<М}ڭZ#64p$I#s>)ۚ ae^jUU°)qu ˪ܐS+=OJQ-w$t?^GFh0nUm{ cѬm5+[08TG4O1$E`您O3U!ұ [banSVgP$rcvb%ɟ'31 #X`ݧ6O%cI\IѬG94 l!䰣LRaƅӈI6+L>c"L0#8\HRGh̑1M/WP 8DA0#3f%3Zm `}bDaS$S zB$N 3 Mh,Ёt0aDO@Qs-Bh7Ŗ,ݕn-Ldnmr&Lmr*5Gu0l9&i]qG\ K#-AB1fS͌C(5ۨrgN3빰Qs[Ήcϲ3ͩY>^̈3EE8l3>TS`mhH[).[0O&g[E]ÉPO8JI2 BQy2 J?V!5% i)]ⳁ%A }j 8TQaR tGqCxڴ7QQzW.'48BT鴬lGXKTXM\:~9y/2Q!~tr0ލ'k6CO 7T*9|QYB+CP: DKƶssl l8h?}lc3R*nݪQ7*BT\UӨmKshlƗkAk[̕irD36m 8?x&Y:^,fO(f'sI菤W^ VLO z?gV!skΧ!LZCg,eʒE iF'Udt'͵N2Ŷ{JG'RH, JywXqUr/]IML?_Sa~r9xjPl .B, En~_r=Ej 85O[zwCny3 9QKwk!Q DMtc-Tح&?!Xtp/블3Bg@Zi>ua4eXHJuڮ:nfIw[K3TkKPza<=kZ~#_^U?oW/>Xj- 22OYp r-QwQz=>-t,~JpR(R{ǩB’ Gf!vJS7AAf$ߑ*,UJOr C8t8MAv/'VsB3/!Yziɳ*z؊Ƚjfs/p^ ^Ɖȝ93~JeC`{ 7\p;, e5U֎W9lHJ&Kr [V:'\nY < $]I^U(#QZikPܭfzЩtn?Րi追qGqN(PT:/UR<}?_͑C뗑{C^ ˠT0eM8Q(3,d^+׌.4~Sk6g1\vq#ac\FR"`VOk&3TWw, Dr^Jgp4 >m-Cbd#tP2TkۨC)݀ h;E,@[: )5SefՖ)8C2U!fq) N2FXwR2-B`ߓFbvxDL,cd+57w~@5f2 >b]}Jk;N H;t灥Ι{(r$| idgi.kIsq\A[gmʤz^[0+C(_sc 4' 3@ɍ6&RysV&F}(_)rj͔k42ђ[rM=3Gh> DH|nIF|;.Y<9=>R+;a,#!&X844o`ff\)h>yP'VG٤x<^tѼ?g# c 6 U~AoXeW0Qk Y겋?M6}J}_w,M} 뮲'CA8wƜdZ_+jRyߣHʫ'.u]6L+8"J2/mq%dm[s&ϻ3z)#.$S8%;B&O3u1C&?{R`\rjPm.U`g1B#U;,WǦ1Q;x?N[M95\1GXՉ8F9޽šlشBwJj]*^l 27*~S($!fR{WiZD1 $Aft:l@>8ģ*^.!|<çݛr_V5UΧF=L#Nm1yN_)MɇxlF6QW 갅ٮf#2jyz73 O9~A@µޕʢ<t9)Fw{ɠdc:ϗA#*dk5QRAٲYXhʿ8wFX . oD{V2Lw,ay|j"sX-19H:4L`?AGcw- MQQ+Oo]~xw)Qqԏg"N"Ks Ff]CXnlrd.B{'Xe(/} v?+MLC W> y.Ktp-f|W̢gӻ>#|z ˽)'>xrgkd^~H"5w90Kq>brLh7}ɭ. [d@/m|J]6[#wX: ?͑vG~F|/qeCwSt,Pҵ~f3MCG$lh}+Z";i0V4U H" yECOfdn^ > 7㻤 { 2 ;%ٴM:ˆgxVq `/_lȏrt6P`.xΒbW}v_J6iQ 4 C#r l_]]8p1u*6sq(`;4?v~97, Lб!VypP^r_t&]n}X38_4uΙtSD T[3Dٕ7P4 sOE#7'dBk49UEp'NOSaz~&Z{M7?xMuӣû+g^F ZλE;;2E0`IDpp 2xg2 )\-*F͔ kI=aKˊmHfG9*zWHz?X'ն1<@X/7bUAb8ˁu4NNPO7q~,oW,eR:;!"#v:tبs#hmdpTt.O5;”ZEcz#+gTL߀tY,$xz`d~R==rȮn6T~qE"bw,'^5-z("k?kSH0|_!4y@eIelI ! $8U & unddw:uNjKK")UFV(_RE$JГJˋE(Jdy<)ù>:YR1.F&]K& !"VHgeU+S-uk1̢u\]=6NJ@T*emᵈ9,?ѤDhnH̙н׉:ǓXh@uE;.Yɇ+ s,x7"\wb^1 K?SE J҂,.'-(1Xひ;p87l="CR\ٓ='JDZin]Y\,ZMLWI,j+؜d])hQ)u,c,4d_RJu_LHk NSzW(`Ow;θJ#pSWNqI,-PHoQIl`lo9[&@12ݷa20Yr&g?-@O*bPyJ/EQR,^Ď.R RĤ҅+_Pz|t.II|B,&%;2NjwX1 7G+5LFljD#g F dv4Q.> lǼed&V;^*S$,%8el=(+sK / Pvy5E~yf貯TJh&E&`;UA_҉Q29B# ގBMqiqu=H,%I1GtZJaXL?v Lj$`V @b`z@DF M7т\H)әR42K01}L2&j]шxc.e‹4S^?c8՛ո6WpWS/qKc'eU!)r _t~goANm56TyݧhUQ> |.SM?eh2eQRW% g\`44\L+>$W =xҽ1_8OJ{57+}޷D^kE%eSM~Gzc( * ,2Kpx>w$(ambMpM1q;Y[,֖'&(Zދ[&cy̐#jt@< |3Ah< 55c:nD+ڨKs9cXfB]ruDxddA Qݥ2jjs9yB}>$3K+It$B/9<=K}jp)DW9N]$ovc72EUȖF> nStnEQ]eb8 88H 0vèҝ6O.N}I4Ik" 9ԈdXK<Q~BzĤI%Ee B'NqpyZ)KX@jSX~WNѕ4nJH[YH!.h _TcrhRXfXh8B;= jK*0{?n\ RkNv.!I5I.$jY9xסv,aݗdidץ@|n`JӧM3 Gύ~?- U*NTC.)^ aR B]"^տ(vr/]CDz/!弢r3Cd xOs x, >xBW,9Ɍ%7I$&4x*/mYbLSf\IZl"kR}Mn[IT7%j)6_'QlX1k~Y4Un L{~3Û~%Xa/7yWY82+>w ;U)Hߟ6⛊IGWIoumY%_mTXg[$}}2iIBF7RWp;&/biW#Poݴ^SC3p^BĤ1wWK ikB q81Q8EC\  Xb/¥q3I< ]UYSM~kU$ #Zp ,I}&zϒ4)aRX MRi_ƿ=GjE(vz-oGЖY+",+JV봕RFh$&bDixDR"ۿEPD$scLygxvVԴP2(ٍ|,M\_]%DdRNBm#AڽbfFV2bЌA NLpt!AsN]sR*I  @XZ&L/OcDDv$M:-]]!r.zQqdxdVrb0eUswŇΖ'-oGqIcOo,q%s~+H& ΗmybZZӯ[H_~cqC8l[U*t [ɾ%=(J%/|:-@q#AI#25Wcq糴 *e=Ae-&jxKjH7NU$]HyyУqkN_Kՠ"dliDH;\Lǩ}DXjI!P[60nbC]\Z 6m^SK~fYLtMyv3Q Hv,h;C6.t,K<4m҂Ӑ= !<'pJ, (vSw2ޔ#ngin/Mr]6HC7P爫RwO fugLy(@~csՂt] b@V^z)!ol8 Wv"_n40Qja(1 u U"]ͲFD o 4MgSl`EϤ$3T1#W5Ar4p7Ebag+(8{^7D. q^7"3 ,=`k}g[IIL 1rM:'ƁiF)9'/Q!1; W"Hқy )S{کq;~C_ :O[#}mxJ>`ߋp[I$?{I~* w ߔ#h"nSѨtᓸ|nǴ>l;Yv,umXa:qm)R?}^w.s_تYHU~fXBt͋rQre]n\(TϾHؐupeeCʿ ro[iĒ41Rn;Vi6h:*]bX;=0]?E1nOGa=2 wM_CY"X*j%s,oj3RQ~ q`^u9`PV)#_@-(H5Kvr9ɲH#PY0Ȝ$zG}0>檋t{ PKajG"dP[MTI Q$$+}Ѣ 8>di7λW 6O$ B[dBdC-Qib_d_ec W4 g 4^|Øc2/kO\ 7?lN E([re:s[1DP5(G{8?w oqB7h4?\V/Q"NꋭմxS2Tk;h̭fsXƵ57[LGFŀHAՓgL΁NrːGryK"/OM]v g*WwޅFaxWv}3J[/cP ֫A8bcx6|Kl D5Lg;p1;d9u-ni_juc8oGc=j:\I>H:C(]"Fm#t}a8 (#2"{=wQ١92&yxmz]4d1$Q8W[pc)0FXg\`ڗ0`Nֿh3NjD,j]By}>W8R4:6yt+In$ZiX|}E~&cr|wgw#)BDŚ4|8 |XR ܯ1$y>kΖBԗC eLMlfDr- լLW7F }_DBj癀;Z]a>4Oқv%(Iɣ|o4Ʈ\HF%%s2Qv66?W`qr] ^r|'Jw7eN*:,YQ-"FI5=o7scog ̄``2圯$}JT+2ڌ۫I#+I,rӇ&E~:$'j. {Ubf:f;uSr0r647gyj)r%zAdXaeG7!6~¬Kc$V+vYCYvp|Y"gdapB=w"Ou2D9_0b轼Q'3ȳm[)VX}dVÂ2$VKAYUwPlD*,uT"{F4kH:KUsbc 08l蜏Rg`nOlh,uPœP}%0 +ߕbӬП<8/|4:3CEM*&}4P*XɸZ(R!Q,RSQPlcBKh*R೮:K(h F AG]$J}XɈG9dHqcأ EWX L-4:o. T,[(-P2WAH]>u)ꆬ\Xl[[΋A_kN' $Yh\tlI ?rc=%#[AxEꦐ'WAU_I}x%>+Z8FoUNhmjxSshNI']vIjT:q6Sk*9jIXԐo4\ؐ>"4ZI 3owB1Eq9>8=K)w](i}K]FF(cEwS I\.KcQʯ #,O}IOga{8BͰ[py-ȯQ[Rn9v 9GZ'/$q˻] pyG9 }ޅiv] Q*tZ-TaXn*+L;u-RS ,X%ɉҎeׇq-['[2^kη_ZQEeg-q09XJd P֊Aa:TBWs@kRhNoxR\"-%WFzWٝ/W[wS7$*0 -U%LVׄ 3K æS1НP%9a`XLb cA薺!"'> 74xf}Q%8Wwhué`X̞fؙ*HeNVɜ)#FLDk:hAQvUd6 #?.˅q:^|{5)Wv S ^-veX ݋Xr~׋^ðR |~Pe_O1uwwGM|Rȡ=n0_27Alh"Fe()y=P_Q.@]MbVۦڍw)Q KPMΗ@$/ăcÂ? 0EXiޝRe- b:_/_B#^\_7x~?P6CTRR"c7Ɖst؍Ȣ.֪]Kȴ m9jGT?btp"dӵ9îɐZAٶg~ZY[K$ G2z=j݌.tϵ˨0{dlΚ5]nmVkGެ cMt: lj=:kgkC%jɛfvwrNp [D[+vZ2\/tK2Q[fiӱ{a7h>6Ʒ꟧S>ϯauі[,F[ utzT۱:WCN-t"k˻0:QdoGeul(^] {kC/v8҄&jXe2M,cDklYz9gќuǂa`Tn$aCjɕn;sJwUTveoUםRbLT,a#nlAB)pQz7wgNL9jU)z8Z]mz VrBA6̝szߣTa#4.V5L&8m\N8u&xط?YmfqjCK5 }'pZNgؑ3Cgt??]Nt^] :0v }eXňMpZ #^>6Aòx,n k8[YÉq.%ͪLeJT\ď ,,W T8WS[a,/%6q aCUQC S84Xx>}o,ȾbߩvGd̔}.\N=RKtj;w m0#ƿ;Akƛߵ oM\σjwnk/lo{Xw] ؑ< 薇ց3PôksLڌ fyDa'a&,XVh1ll7WpGCy {~@?Yޭüt-ڶE`~uix CRՔ>Oc6ϸSB-ئ[ngWKz$38'r{Sy=lU-p`7Zk݃=ހSsՕAl[ *q0`xnsD-B:dPSvĄӵ:UBHq WVbb}I+3!fcELPC:l/MnvEx%xJ~|wϴR)UWoZ?cT`tڏ8;n9=}]z9} W{1gB 8s;w7L)ޱz ʼc;hA.7xko"z?Gg&ی"#w$ЭiGRk@ @xPx{"we:t1Nɬ&([}GE݂ы^Vn-t-[5TǎOw3h*b*a"oq#Fas4[Rȡk?BZF=_7K~ Kv(# k||G(N@PWi#8WīBbտCn <WW;jrW;y r9tjAPo43dt;@-dʶ3攁pJ#Sމ ?RZyW^Niմ#_.hZ-o b=$69;G$2h0Qtl#$ᠮzRs"Đ'Ȳ#&V0c#/XV4hF'Q.W3hB)elBF `ktIB5T-AR gCVj¶"NfnlB@?z7 u_?ֶ=K*1AjP'u؝ƒ{||\"lb%ar,:-v#|q0seMϬvg;Tb2-Od~5m0~oW/_iw#zH0}v]Sa5^nXy $c% J϶uA_ (QQd 9d#v97I"@.) ~"zq^b= ^:2Q.cMRߣ.X_7> <ɩ*s 9su_Σsr>5>wJ|G,1qvǙ7wAd6ɺiY;`R Y޴؛xgZkeއ#'[ܥKeHt[>y WR*fI*91NE-X~mb$p\񏨐iclXD3ʡYT D)ow- C MğMMg3IyyZ(;\gJ;5+h%ڨ'jI_TnktYз: v ;+6Oщ#xnd8z,Z?.ݜUnjk4,"e7p70&+ 8u،M`'Ⱥ1l7$p }3?琥g:]o ~7L_5Q 0aU)ŠG$A(%A po7x_*g"$DpX!/Td?ݣ~cJgxmrL=*:ffB%t-Ae)V@L`,Nc, ^-osކ&S^Ix†H* ,LMT+R"TT7$IR}O% )Oxf]TSV_,=ʴA;B5Q{-&bC:]UZY uJP:)CGP0GkBY~e6o32i+"JrZu^a&pIkAI_'U; pY~>4;8kaL&DH\`P5-=S4u^_ NQJEaUi4:dVw2 )wu*  [SG$<NtrgalJǎ8U~jVRO+b;S~~r1 -( Q JVEYFTŴQ LJgʢ*Q$ Fl.} S2:|/Kދ1jR{t}UGܓdeZP}vndZ A3 UL+i*J)LL 9-?L1KbbdRJ=͉,~"\eAk8t\=a([6 ߬ӆm0AvB}#(2UO>AM'8eMEӬx%):%1g-gXtCV GUbd 1Kg(&}wdmCYFC1$9~NDEڄQ$Np(@ ܰ'&il!N G#|Ev [[SmX'NĶG صN4n,i\GUt0{mWӦCO$ЇVe>Q v5 Ee֝OfЯ]01ߜY8[_'6 r H1N 5I*VW/gRv2?L66cnZ3^dl rEϘcu0"qc3?D xx@0[ayfIb\~v:r&d̻5uFg.M5˜"E!7Vb?Qfb'mz$TAagщk#AuCDbl|Mxyô<]szf5AQQ M:Z߱@T1( %ynVY*mF03"<#roP6f(]C9%xKHv yJk6`3IP%e&@|\-w"uX<Nk@j0\]o--HF@@,xo07 H,}ud$DdgeBGՓٖ[wU7-TYWjKaw-wX* Q\O6k$<ӾA5 &}P-[V2@s#6)Pƹr~Fq$X_~?I\&w  b^SMU+?S|'] +|(ևDM?isY<29E(M*@o l6EB1Rsдy^T@OXܜyN Xd*Цל m`b>T-,Cv~DSwu @uxx$X6MyT "IˢskG\FX]jE$-kZxTRBcr`P"B#F :=XD2;|kS꘹^άL&PİllqT9 Gl HybЂxረ;MR&‚2%EͦGQo8ou5DQ;#{ɲyi=6S8h潕gx.[ȱ&_%G[lp}X4 iQ;=K[Zuj+BtlTiY.†WԲ?wxt7hc{"`?(hz|dl ?Y9i6a2a3Ūe#Z9R[uY ;޾SBY3GpIk3T{ jr9lzڏ=$P yzv[RP'Q-dd:)/Wp 09hN^[ 0 Z8Y%.OW-O-61:NeN<ۏ>D_g3ZORHs %I)H`d^=;у=Gu IIwN iG\`l;yx<9 ׵^9jh }ʏ 7?X[7mY=g:l;_6YRҏС?4ry<;j |{'hvP-fTatN֚R/MY+"M0!0R=t-&g.25hΛڣ,NKW$lֵϹ[-mWQaD\{am 5\]=C@r ßez OϏ80(GD8A)uMX2G+4ZWT{& 3&(ԋV kktNP>;hk7gz׼oK=da{>jC;<'ixJ,}g!p/[A`Ͱ+jgFtYkreT. lT.bh:F(moT΍ʙQ3 5*-T1=Q4*Q)Q;(CJ&MaJ )'k|ͨ~kԁGuXOp~~Gÿsu`-le늯J|g/^qqGl/ u(߮;P fG-YB@*ur$r}$J@ԴqΣO]ȼ$o[iF J@A/!C"=,Ũ J^yh 𶍏ط횪 8R8=UUɯX-tԨvl b̓V7J%X]`/s&y$ 2KݥB{C`Pzetʇ -_Kz[\_?(z0&Y"R$$m;->r񿰫%' $(b.[KuYQŮtNև o!:@R~9YKSYZˈv_'> da°ăQbtZ;/naaI\70JN??W>^ӵ`t=P%mMG[#/.%/<̍ |~tUD PeAmaW;lɦj@$=!QQHIB@SuG@<2~xYyd+].Aez/M:8΃W846$z,UaTR"(Y9d}e1 3"{'r9ZL*& z`PTxo뀿f I(nL`,tjJZIi2aMy9@g:Վ㽓{LI18=_B&{$ET,Ф}]>۵s\_eMZ!B 7Rs膻a]ʒ9Q(E 7~ k>mt-?@g@HV' `7z;L<ܒ2h]B|J: 1(ЪzBkqn,epbop 8vWf ćć=3`vdӔ@NTFWPGyUn# C1?fS>/t{xg)#)~=e;7ݮw]e@ǀ<ʼn\{c7x{GnNww;:R7Xcwuu7˅'<g?9/9 |є۶9Y2M캹jV: oŌ 5j:ҋ רO3?x(%N>ڏg4+0d{9a협;˙蹃W:x^5R(y_\ y25aWɟܬd礲 ~?D$PӏNڣJ=,X0U;Ф17 /tˈfB=P`&E5T/ "EjQq2*x'S#_] Z㳿Ϥ}_ir%;vC-vh pP|I+~HEaX|&+T<RHُW(QpyGsD?CmGFx-7WF8̸mӮܪ?YvDRWaa_A|-)<_6OdUghnCLjF\2Qz_a;qJ8R_ +;l֓ǜѪd;@Ϯ !] Y*N>-WtX~P+i@&GP袕v15p(V'Yw,;FUٖ^Xmԑt ϱ {H`H.AD贌Xą8lf靉>)AHƶ[څ3ZQĚlFAl6ֻ`y6/*Y?~nF+M3;M@>o| }S~F::!5B+b>.zaw;,j/,F-B0"[:P?ޓ*ُ%JL sLޏZmR-Eؗ)H2sf Gl Z|l$lcbM 3G/=5'sLn0M ݂&RTWnz]IE/쀏ouk/1 7ȣLA .{/,r= \Āc?`^ge>vy|*JotH:U|]FqjS^rm?/_]?աMG[;:x]DD E>uB'x"+ɩT=[l'lz,._FO6їS٦?tc'Wn0w0Cݻ"n,ʃT}e@oY6kOVn-hqHn"@ՄȝxUu]\[4y"L/BC{ ZG)pMN2Ŭb)CP_4z{~bU{a刭;^jC#gB#.33 n RIK>H&n}Oom#JK EL|9OFzMj=}h|&@ʪmr}NJyqv^4-x2Һ8i{YG)I\I%$Nypb=/V+\=U|}X΋x1uϋbn,c,PkM|'Cn̍nR;#jʅVK{Jϋ-yEAj|z<^V_W־_W㵁/Âtw{:]>SEIo_j}QةNEUҲǷSj}d8ɵjUaS Evó?Ja?B |(  $ U %<ѡ>K;#ʇgI2p(^$N"R.l͓ ǜM'I8g#\l6pq R|"a,P$d en|:cfMDPDE "6\$ -uU"ң+.I4$,Y0^c@ Ðs Fn0<}$ P2__MҎqp!(J8dB-8A?)A= l$J&"fh, x &圃'oo:M8#$k"$CD=R3As'ypc~~C ]jQd6.I`fZfU(žSm㘹 !nz4V~3"1Frq:8v:`I?=UDDZ, ;b@h8@u"0[ic+ !Ԍf5x&_&j b71Č}92ynE0Hjp4 0ICclcSCbDQ0i9aoe.SlwvG܉i0#Ax*fqMdɌ9!1 *q!q4D"񦒿: 4B0uH]D+Qj/$h", T$*Q}yz|A..VJ#f7qh .8^4[y C"ls`0%x^eAMxX'wTQ34Bc& 4KZ~d&_&u*p b4a(3E:MFL1TKb hH0dž :b6l1"uiU2HS"EIuTa /9#8XјKn<I``GAB "j^j0 eP #$3hKFO3H;Q t7=TyI|3bfC-Rd'LMY"O<4sP*Nłډs-M!I( "T/'2I@,؟$x$y"!E# d%y䔨[& XJI>Ano1B"%3]UuVȴ\MI#(-1,b(ƺ!$L i(&銄$Z f6'J3c~f$< KQТ£bSĂ&T'E!T W;ϽIXxPWD - nc#("VH'\Gݯ40E')+gQ? j&} F 9bFf9ky D-Dž +3K ЋK )I./:=fƟ5{a<""b MgS 3*d5}:%TƊkW5D~P ߞ:A ֑9)J=&?8#LG56.~4@lL$ N[C@J%yMƉxdAZr0Eˮv}BTh?fC7"6 H0MYk< +R+$)A+e ѵ߄Y|w u?3xevg3,%-^+ı$U7-vUz9`^S c;lRDf͘YbUE&ɋS64F&NԊ^.!ͣ䰟0ӂ xHO!ɾ SSQe{>~eˡZⲭ@QѣQ \ Iœt.1ubrU Fb 0YQ~u ~Dde8AtjX3VALM'32QtةbeWp%򶎸\s\i\炆ĸ3-nentI8 52tQo_[802c,S;CLBz 1)Q,h26r`E"y_w@ZCeXrA@ҟ^$> 8cS3FUDZirDf4-P2!tqSƴ `kdz"-` `@5~1D,G 5BP4,LֆG BqǬiޡKuZq=:''bߺL})E DM#I܊07GVX΀UL)lmN\re416u p!0P 0` J',Ktk5=\ e=RDi>:b0+VY"54did/PMYײ4ӯ/# /rpy~.zM<ݫ_ZoCJ'^~ gi? I1.'HltMLY1F˹"xS1N@[/:}94 1:H[ Fd93,CL02f\)(J{ ("9mR(ex e0NdA濺͚2 daFa*| 0ME>,%Eu]ƌ! ;Mzgkn9E(Hwx3](_tII /z&G5%>#X.cx>H?{FNXØ&INC:㍊\kl6(Jl-^#q ᛼έ Ju 9~~_同-w_u`,e}}õ }tI)EY(ʖ ;m` z jXZyɟS,)|~jJ n`<񆅂4_ZRՍ!ԩ ]*BcubN͂,nٜ[c$UFXK(^"FL"\,(G%E,yO'kq1. ,r7wE 3t=fzg-r\!Δ^lIG=EH˗.<~Dya`g!} Pr,V%YP[NQr2rvؐ%`eחфX*8Ζ뢜W#t|>3u%H_ŇIӋcSAmEihtFٍzvޣL)n"XjQ+E ( %ha,xfqBPI_:@ʞ xm ş Xhz?&sOs!+a'x5d?W rw[&I!`$,.®^hkX3lc:,JJyϷ4[2Q:#`wEeVip=^@5P8I|g[X(waki<ԊF七Nwy0 ok-~HguQsr񨈔u¥lM3QM^&dUD :S io@XǷE"8P| $5GI9zkׄ$;`%iJis@%e\b($JunIEldJ>ja pa#"`D}zX/+ެᄽ́ Af->)ߚafԖiS-+$S&Ueߢ9Z[W|3Zn݅3x YRC#Dj? ܪlnV[ ޥ, Vd:SL)yo&Dbȧ 9)'Ñ= y󍪪Z|lWzaߟ#4-Dݸ~Xb_3 ei ::|eS 7L|:R6p<-UjZiU W CN*\DY_͜'_S׶rG6/-ᖔ$ck+s sKJتsBW˿d3~l4g] ^G WnhH(f^_BԜ˰NsӴX y=,R6Tafrgjv(J%>|LN&vΖ=f񱠂7Q}BNJyx:惠Ǣ ;_ˤL( , L2^CF2p͎.ÿf|8F([ WDlE%8D8B1Yi!׫ wI|I暲y[|%ա˞ gR(G'IJG?Rï!G|A*_nvGNn2GLgig)ؙ\X!Z'2&7U/e`S0Utn>I CVSKRs_ )S%1 4Mdžd&Qe|fj̪wo*xbڄډz_1ZW̷mT7͈<"U N;;yOt/wnPM-ԭS&|fd~Ho6U<ʽ)y*La_͓xɑf^*E,1G 'F[>> Re8kr##9D?׈z@C׊}Z>Y5߿V6+TfMH|SAjؐ qOoϫ "Dx+\:"R«eet!GgkTd7|<ؿJ`7p:E9neXc{M d3慓h6C:V)mx<= MQv?,4Flᢅx*ުGnwv_z_qw豻dm*Vj5k?\׭W^EWsk_%<؆24_wrIF8XnUwr nAx 1okZ6݊Y\jz9a%x` t̻bX˸Kdaҟ᳃7'΂Ln~0G]K [ @R/{5í<6rVI9V3[zc>O^MO *_Ȟ#z̺&eb+2SGo+,d=zRgzWvVWՆeþQ,f nݲ1W.[ZW~'/bu΁?j|wjQ5g)2T[,?82csjr2koxؗ$~+jv܉})LX+=:>x[?M@oe7Tt_LcI}D#-A>f8[?KO. (BK#<'e&jmNx9OiX\Y` Әǖ]O}/X '#%^^ʬHZI>`i \j? Û?fcSۂ4Чv W8M;g}ipP <8zAuiYXNÓjnAtO]׮xGeT]C.2l{\i57.3(# 9?3-[lH],U鍍{=`zv^?iV58Č.3gz-T N!\ DR9ݨвGq~9f^k#⋇ƙTr[n?|?z7ϳt>!HF͡, &$-q)nQ!Qn8I5%Y$lnn&4+B #5F#įpЇ#X% a3!Bи|7 ; %04XԝKJ4Hn#z]3>v)3C4fQf"LvZq\2ngyV{aڏ8ivxN˺%np ]YōuWEsa/ꍳ3aS=-XRs";i A29$[“9ꍭhW-hkMT`_Be@n,U>aYU $ߙSDGƹގKqjmBנxCd}j_IJk/ HC  ۯV Өne}&Caymn|j2C򻜿M؍֏n}l?-n%mxl/l ;-h{, $$ $2Ƚ ]*ppE̖$/\쾔0^K|@}ƥ\ψ '3_A/$Jw=,_#vAx`Nu0ݹ:& zDB{tvZ)ޚ5_rE!}ҺKHGl!1o!ݭ[=>zd/!SY't:ſB]h lwĔcVu0n1u Ģ?,g(IEtTPKX~?恵*]W A_9nkH>פCF9y<4rRWdgBMՉ*fBChj[DC4C2kbnqa6t&G 0/8NK}.`;$ya>*P\ڒyszߒϯCBt_8gR }lm4EUǓ`TԽ:XDDoYP/HP"i?+.bzv>O[~\ËUQB!-R>E*]#bA 1mw%u_ @ m 3v4mV+?+",B *&x־!^DE $eqgDqDh m|*zD'1[*hxPpԷGWDH/PjUs53!ǜA<{[$B"%pU-VR{% bF|t6b4?1oF6Zȿ.K@X/Q ? 읽 2"sV}]etNO8oˆ 4)3 8K37"P.u7_8V;{X[09 6MvpXG`JAԀCYz3 {.NM) -:",);>Ie֟iz]& )uK g7TGNmu OJU;Hq@`}">)^cRB.I;/Z%.SO3Xf!8Ceq9,8׽ko=adOI"&ͽ$xT'I#A[ev[$<2O 鋚 w2s n8Xvᱷg4 y [ޓ:<.9C?6o%8 &Sɏ\Z('9{j{f5+KHVT:t$_ͷLǮ+=TwBR ưu R#"r"iNid3N<{\٥.THb2 K99L?WZ._Yb m?bRx"dJx`4-5,Tc^B et-Ƞ'`( pBDt+E#77"3E"&'-H!šӨ_@U/J-NPB5Ņ9+CdR̅NsBj<9Mxg[EKh^09-Hd%tvޓ* TZ>R#8mjpL̏Ć.U}c駾 c݃쟟?G\q% v ̐9"Ɏh}ӽ G̀CM?L7خIZ R4@s[@*v'q0f'(;/DFF],IԽ,YMm/̖8=/edHcW >$2N|j\&aޅx%Hk**95 *Kx>$>U8)qj{'\(iS4C掅 jPbxi#:*i)4o }v lxS LďaRJ~=^#ۼRgJo ᆿ=E=n Jv v>6 -=Ef[;QBDw:;{]itms\';߭ qa4|[4k{_[[ hmy/om}H٧ڪyON4JVBsitiѬdlQ #uW@Yw&$v}`yBnz3?;_w" K;b)!wJj'A 1vYzQI{/__|?]׿KlQ{O7Xys@} f9QK0,Vp>䰬* b7{ T_t5}.c"֬ەROO2͞/ґ1|v U2x\y^Gr^Jlg^d-eU+AK>6^,ctwL> գ?ײP wA2 |qzQyDWCЇ괕ȑj:NtVw98wZ]kBٳ}$E9OmWx˷/krFMg/۷z=h;4 c,] 4JJȷi\1mp,>4LNj\Y4NJ , e}Uv:C;^eWs>qY}J $sde4(M;JfiLU,TߧiNk^#MӼXyq)]avx{I6b)] "_6%Erqa7[[-V:+TMy~ޱGB[.f<2;~}4弸[ ళfyseS!˧K*AnO>VQeZ?]wv:Dw6.@ywXJ"g; g,lYSn>[lH yB^ZS{m!oagf9=a>]0a;S HI_Ɇs^=8PG{z7_$7iZmoCCgՅG}bwZUg凌[0(NKԾX֜ DDolh՛;F H$kkH d^.‡Hf2:dquؾc &p[*px/[K]`'?^N* YFW3Ыm_dd!a |5.V C#rҠnX=;y ZxSqxShsɃ%Np;ްǸ6w5#ߩy2Ӭ+j99Omp>;gp&&Klmj)]l+323BwX8/V:/.΋v-V\a荰 \avs?:9Q`ZH) aYUGnYz)Q@G:)ЈmQ}G&`!C}L4JR(d-s ,tNjG }IUi1)#RbY|ZBCGr7%5&F}`7.? .P➸ϑ[( о%{,vcٍ ޏHbBxBEt4nq,mz9?%cdcjZL.־`|?5 s]/H*?/;,*@T(zfeI 51ɸ9?/%ek@@,}MxK3I)A42}F0N"7](ݵr}zww%i6:=Q~!6+iyHnM7W*̺T 糷 9ht! Pqb}:),z ܉nxa!o}~)O%CYx$S(K`1$ rmlӊ&jux7oEkgÉlUbo^[p/Px `M]PVq+K/M0ʦ,cqMxR؁FAaKessB揥Cc.nOl.SW6oU^ei6!!0Wk4/hCܼv)$e8G$/@yl/g/ge3#4ؼ ON+/|rܗhqGΩjN>wYzzwj7ݝ @U+lU;yWܲw [S1[}mM/mI9YXcjB zAH[3k-?.Ⱦ$U[Eak`v:T\޿)+s5Kg{fV,5!K6ewֺCi:ك:vQd{H&3Iyg^:}WrbKr\`:J ݣhھcdaxtx"1&N}Di-r Nk+U4S*ɾM5D xu?XD)UKV7xͺTcb]9p /.qLqqɅЗXx%.OfI3ox'%8UDa֥jǞYbvCY N_agkx_nM~iks(dӗuOBmHZw>/| \ˋܣ.C/i|kEChuG:]ZX0\{Z=jsssm|, ?I|z Y([{ޣK ]vom)7FvOVMmƿmL2@}gXvm l:TJٳXmmL*uއMsmui=jl1@1duJL)qYQznQ =amO[#cQkh\c{hۗ6[c;2F}h6j_0kF#4WFomvjh4 Cam԰x>6|yȤP::Op>?s홏?4?iWPzCuū:r=쵯f\{m9.5Էfִq;{xAH(ǃR#Ǐ kO|W:o/KxZ1:ixEM"vD,V^k#`;hQc)ěے'oj^bYjzWԻ]-RfdyѺ󚭻ڌyWNsHlONw_y3[Pl;N8-8NxP-e!V/=`T`zuk>h|o-|nԛ1|<>(umv^j 4Uj[uIbD||(G^r摤-veײ)2zijwqHzNYUXbY7cl-e{>v퍳3ΰ>&dnRewLq0"\wJ%ST${>/y^?^<{o{_v#W`~#Ҥ/rF޷սv4npew xZ)8cGǼ&kQЇnHҿ4 UzC WwB*gw ^Bc5'+O.u14?wҼJTڠikը]H'NSw/sz;hʽUg~v_ח4) J 3qb,4?vjoڭ]l>wvTho^<=קvnCyu^gK.+L= 4ֳh(C=]m bTaėl@i'U;k4>Vlf$sE9'Ms uz4 z2*[ת ő]5 2vHW).I2" Aa#NO 췤`B}>J+ |gBICksͭ߷S;ccwؖ;~"Mchߙվt:x!\-:F070;:2u>(m 'xIc8KS~>þI+tr"׉oq}n"fl]M}Hw ՚ݴZ+]ޅn8)4_vܷOOAO~(| zR}G@0w A'CS+ 1k(}ݗCF?[T Q䯀KwF75*-Pmΐc[;mUVaܔunPKZgv ?pSkTp-9h(U#ŝs .صV֣?ࣩ0`lrZ؎m@?1~kn&t~LV/70chs3v7bEoj-dzӃsή|_7Ko-jz-lZ]鍝ڻ$)4Rw4pD&?ͪ9B%G~paiRꔮ/I0ZwG);?^I ~4(prT7n LXG~v?al2zm9ȯ>lddzA~ |NXJndӪ}l)ࡷ 3tL_Zꡂ3ޔ ܞqA!oP4RbQ:93K-,fPE[fOOmF9[e3/w<F"֎> K2 Pp=ҨaDT8Â27FX%X (}dݍI g~/]opN3, V zֿX=|k`Sum≽Z9S{JvAPoe\PV~tPͶiAFP3oZ/$U4qp߂kQۋT pڵMl iL?VTNL-4G,,+ʈiP Fo .2DZb|Ƭk­ξpwX,;RپӼ2{X##-~`Ph u`¡epgɉ9ѝl}V6=~H=РЃq2Z/+'lŗl 3]*C*n[8[ vIU>Xx ute4Zqh2H!#sFw1])vϠG2SvB<S ygzp+]2{yx`eIEA-a˺YcJO6fB PpIlf>k`YmoF|{x獅mfO?ߕ$bjAvL jI5UUȴ)J׶:w^ߒ><͖7`ޛ9dMp<]L8D? ZG~TaF˷osG|jJG|uDDeFta>|mA TaW|B_m9k>瀏\]l"puUj]5 :/TP7|<%lAKuowţ⛡ -H96>eUx׻;I=}DžB`AdiJrmpTN &ƈo8]Hűű1B@yڋC| `Hkno^#ȷ!SS+A22]sҨw.{E6y{m줗 e`hT2ܮ7q`\>@%zZv~H/k$Κ^\:dnuڠ߻Î{f/uٵl0g_Qkg(,/Dzggq&*$L.B 5r;ժV!LI ԔVOA*W^WwC%޶Ykjp'=c,+b0./2hW96-7QN|[58,qtYm.cᅩr54سd{~7Ytb0>Sk #!v㔼6eۖzu:ץLL.$5Q eZBێ*ӵCjj~dӝ8㱱yp܃ӷ_ L 621f:uC_'D%7"vmo5%}΂V8-$xKuu.[|۩7ӬQ1V_pnr؞vfnYzq&3{=ERhF޻d,{;ܜ=7k˳Jn= J],dŢ@muf{U1~Tkz!jwX7sRXw/eS\0?\A 3uܖbwa F r6ys;0M盛'9rBB@2HKIJB^6(bIUl ~S`u/Fo8Oj9lU瞴"K@ xh䬰]h|efvzw#{A*i'm/C-mZii!)>@Hi`SM/|@^YQI;/΋ϑVcRFZg:m/K4 =󯤃Di_4.´SjV;X`㶊Oyr_/":yv{^,ueŸWj qH:;1 Zj<"|^{$[MjsTZr5:]ByE?6^MWOǫJ;]j}6pEcXynz@gH#)|K/ ;ݩUWZ|J`WR'6Bm*l*aHnxvcDɿ7':XH"!=< $I &xX +%<ѡN(#ʇȱI2ћ`(^$ȗ\ɔCƜM' %W'(I "}%YH$g^'xm?AD&:D7h&͆%*IxI "hqD]{ǭ$ AF­so<3/Q?H s[# w&Q+MDf)L_J%.YOCjZDG2HX@oZHd8Q'D<qpŠ,|J҄KNdg^aBe#F!RtDEf4OdA=cXrMx7x~A۳T[G6(:r4'euCgĐFz`FB}2P~4@lL$IYRGlbDK& CS.eqOh."a !\S5hDAd܈۔4)Z_gp3lLKk`njP&D*+gE%T?H)[9]JV+wxx&X F B1"ER(L0@4D4nb(R\@``4***p<`ef.B)Nfd S,&Eˮ #.Kmq>qh՛_. _?$qgc[6(p:Okd7脣8#|"1 %X`D1B#H0rRMr}j9fao}4[f}cE ọ4FLr}[Lhti.eN95mE,eU+m#D7Cʄ5&I9 mνr'1uBxKO<iwa8~j$*R9{H!`. ΜApq)j"#6uX4yOyG`KT1T'h{jBpPMπ;'[Xs[Juc}|"L[4 lrPhV3)I9 CrߔA&ꑤcnESw#+,Ymg 6'w_9p2PsBϺ Uh0žw(U{"H=Gv$x.,+۷tu$/Oe@/̫<=AGT9 r!"Tp&_GUbrI[IK](M(tLbC )_G j4-Nhn 4rL BHyp_) K>50+`2 Kp8zH)AP\:;!ŏue-MU!v*&`(+AUG7̛]J= baPT"DjV+b^!'!լcЪT Y! g9.U*Pc ((Cx#S,k3sE:WgsU-w!iГhb ,})C1Ѣ'j*ԡ)1J SZGRС =e:YN%olSJc.E"PIq1+,_uN42[iUwkpWvޑQm9Zc?=ȦD\-J7}xljq|߳4P`Mi׌S6zcB,Lf\N &" "S: .}Rjz cu|JR=s5DR ͢jb~ύXqI a./v$$yDŽ]rb,1ZTM>$֔,KeKƊXoW5auC)>J z?D֧?=x*un2j}`!C)Q m]D5-r1[.x&+UR8ˆnQ•dc5%WVa0_xBAKOzT.!PHY a^k7&>E,GiT c VYڿrdGk[)a&AAO{=X//R^&:Lm.EB /A bGb/u+Ʀ$xpK(@M2[q<<4Mm!}wm:mJyՑ%4J-܄uLqDm9h |DÛaMLa&;Q:cuLfACV7l-Y*#%e/nx#UTd.qʣ"w58 I͊ ŻS:E33ȖAP_gje|x6Iɤ#ꞿ [F̢Y$KFjQ|E?<0אdqx9VI-f9Dp [c9al0ԲhBoI gKQuQzϫI> ڂYͤGű)wҶ4Vb:FcZQ7M,"Y\0Bs8 !/eA eeO6lg ,u4=NjZwdڧ90nw< Gs|d+-ِh0 Y@JjaNw/Yk4v5aP@o6ձso\%Wczֿ[-^G0"O2Z\w4yuQ\k~?i|z~ozPq-z,׻ְ54[ojE#c`rA;iIՄVU|yX{3:VL99LIxTD:RͦQ&/*"{i7kH ,"IH`(>W$|f[kj{kBE^4 Iq _}QɃK.1C*U/s9-)AH (MV.GOCޓUepdnw{slRaJj!ZB4{~3x3cְfJ;2T|;DQI6x'_X(HVKsڕ3 0'PUY\ ۃpp(-D]_XSyum/xOz8)/=ޯ'^JXvjsYEPV"ӯÈ>dz  AUmGVQUX&o͟ց^f\j :ޤ"~6@2%k0zc^0IľhfSop DUc~}oM0UjQ崩)qqêoH֭S+>OS-™I~,! p'~0wmNꍵ`?ur)yi;)]LfG,]l-@~ߧÐ7-М*I5T3ʭzJtFa^;L٦R*WOW6nQet9jGICtz#j];o̪wKO}UA |A@vZ2,g۸_!kb7S{Ԝ1JkRK0y-k<)5qƒ锨N3&鸚D2, ܯh.*牨fό"aG]/#N,mlIvx"'e9Ȑ3ЩYzq@"YvBi+,w,z(9FA7便͔QĶ}J1 9Jr$)Mg[mtK n9,8{5 e+0Qߑ~mo&8r1˘ hwTK5n+wToJ/`/i+/'- yQlɡ౲rz;Dz/t2'~7bާPIxFf&{_L U !'dnSWF%$+$Dy&׌(@`zֿơhp5/#bIj)@uf_֔bZYiΪSĢϨ80l2GnlWw=4L%Xrjև|- lBfP.\?ɕ]0l7׾˷}sPn1{8rD h7uƥ˅\f.q.er1p>'NEiZ~\Vo v7CÉՑ;[PB| 4|xwVm~{_*6yhZbe{ih>m"*moFuܙbTw t|S>-;NzWiv%p׏Va2z"&kB_[ݪuN!PnF@-!"Rk#fc /LD2=`3dAHaey,,!vR $G4J߲ػicsޡk!48NzRvix[=۟#񔆵̅E :yl4*ծ_ 2y2QB,fEJ"m3.L+ \bW P|0uĤFS7~wb†2%uЇ>ͷi`ߩ>sMJy L0ڗ4|rV+pp {xv4;]=.. tt`9`JwDh)ieCBttd _}JolӳIee7O)8$fv9+kZp q䂷W$JiF8 [1G\_<4݈秒rջy !F2j`x7d螷( |FUBFk SA$ t~>`+ȧvduKZ4N; XUԏ0 2A8b$F m@Fu,|(`t$S`Rw. *w@ӴF"BFue^I @F0.die͡=, 8 {hPӈLhto"j> <(%i 9-1v6L-@薃xQ/Th2r9qMRq?DȸZFk?I9.Jwۗ2hvf77]Fu7Δo+GCLy2`Kjωp9dz8g ,l Oh:37V]ַ$5cP} = ^#VVe!8W+|g6N z;/Ʃ9 ]"O ysi}%K4>G([d. aB3Jd~lZ!B.O";=] ]z;JC|br6պ⧞_,1ү}FlpbcEPV^ݺFʪw ٻP][ ĺlnzPmoxװyPNdՈ$IɯEneQ®k\߻F4seTq zb7^[?ʻaIi5wcHТ3C62>Bp?/ 6tt1[ sRx!/Ir9<#,D"n#|+|3˺c9s;,Jv~@'h'P ]hxk|ɅCpI/! bCļnw>oXƒgTjLe" veUZ'DC#SYYZ¸@t/hZ`B&K$98v2SA-aG*t%vf^E4;+4;C}E|0#1x_J䘳BH˾[񫺾J] A6!T' oMleҠu/梮Ņ|jG3lh8!.rX#ȪBqiKcB ϝ)R#x~K> ~nKmdzY!4 UMDvON`>'CRQ`4eA APH?m!r /VV+W}F e4LJO` v("b- D}UjH6G\:Pa$.Hջ&:}leZ̯& _ % v=[xM2l}oٮ!PJa,񍪨~]l@A ZQ_e~$#z `CUa$ΩhΨ^ gsF@m0 $R68UaZJZ&.ѩW*FTWļqpkhY#)WiF8"G%ėxI $?h!X{ƾ$cWxrx_w\Fm#4.вkJϩ#}A╺٪Ojanğ"kNO2~U( F:uCRFGc 1K1:! 4eDf}n-QnzAt  yOXRmkUl@H=%O11?׺T Q.u2\~nv.[[pŕ*%0Cf$;-M'x55b0;`&i=J4Hq,m5e(>|@ Jv$xS^²d7W0[sg!᎝v_-";9ry╗l#@dp WX4.]abWĩpN ;0ewB)Is鲎T먤=Vbм~{D6ٝ6d^M.$3?qIW+exltJ=I+5DlF`N@ZI/+)عz08: r!lD yw-;ӥS2w[;Ms|:$[ G-bn Ь}mm5[*pon"Efj^k>;qҬ[*[v )WֆGBG]eݙ0A<9 [ϔ\R~މ7.tƧ+ ޝf+]1کgUDy$Q/_j#|~]RbtS^.AVE?`}}),D-[qò08%/Pi~^@ @^AȰ^߷ ;׿[nA曗ݝOI/&MomJv~>E4sv RrS_g'H\Y}:0'Ƣ32>z37'n =]8{jRޮr0D*w /79SJ k}ĺ<<~M'̤f'QXf,Ǩ,$XnWbDJK==Ul4{pKG]J-2RV^tdV sŲZzy+yÒ$W-D1V^^]Kـ\tUEh}e<>s.zF<헻-0zT/sϱZoLvf'HҳRDx~ԩ?|,CŮ>*&xI̲bwh1i4T 6 \@*xA*󙆏ǝEBSZ^ F"CV"G 8YߍS0 ܍gkiwuZ- yfG;IS|W?]ђoG.Zɕ59ozn,lBwi/(+U &HXZ rd2 YI1bL0fxw%0+tj1OYM}&\Z8kz+Ռ~Y^Pv|NX2L[<-NȞoT(ngY{T,1yt5fﻯgǗ} cXBZX8/}>CXgp7f3pED`G]i!(He-VIZhͻfmsFec@#39G%) 1e(I̚n5/L1߸D @{>Gn,Bf ؍1g7.x?"/ ūmپӸiӳ鱏&JYKe֫j1FZmLrgWsR!̹Ծg^)P;K/lQ8Il.%e 0?]ާ-(+e_D%Ig^~)?9-7>u]# Yۇ36ԲrQ kY&}ԖG$]NA07I/)$RV:ap8Lt],Nǣtײ; ߕ W {\plD8\d⳧!5qj^B$V0S-+YӅ&TrB99ȧl1Rcp'Qe#< }7f}N/ګL4yZO+ݼ5 'UA;a{Kl%pB%55wAY!,H4+.k\|ucI7mnJG`r' ˆ. ?V 9>O]ؼMWYzیwjH 8^[Ѡ0 sإxL@\,Rj{mS ԟݗLb ,?9:>\fP2/Ԗݡ[djG}{#}ț̰&x6v_g ˉ-q|j+-w%Gj I O@óቜ c :=Ne<= 28U LPPTpN$6/Kb}C,W.YQ4P͊]v1l'"1!>Ff'vB_b̓_X>}'I,㝔T=M.wo[շ{fٽRg-l;~a*+ɻ51&֯QϡM_= M"ah}a.Ps#owx./r8ȧOI}"PrwZkas9Rk}K0l &){vd*dly.1tn]Nfa;u>Y6q"tV1= 6ai۵1'gvP)eςzkc1Yzf7ͽuץ9#я.~40 8Nwĸi%*1y+-gGGA0؆}=m+ c{llWFiCqeqdl_nz`wm7c{ۨ}5vخ^gFg5=c5(h/N2Q! 1z"BzBhlWhX`"!^YC uȕP׾bqGT T;P&B泚ZWY!{JI?V2]S}.p|TfnU%Io#A5R{əG Jؕ]˺t> %K "M<9e=Tbvdw޴Ur:+kص7Π;8scXIݖ-܍G3MW]k&bBkHLpA*LQ$Fs<'6~zRN]ݱc[84B#gVXvp#ķG)t&s9$Q 0%Nh/Mq@&DrbBo˝'^'RDnCmMpNJwycP7!Ukd/Tkw k|Bw{ff|i7_vsn>O k$>U$>S~<%_ϺQ0Sq.Jcl" OTЃt xڮ}fcu_ oaCS)Db/a4:LԨ@?{8CmU?ZڇrpS~,yCy.9j/ޓ{olÝOǮqSHmPڢ`WΎwy`׮[G[ Àuic;}|Zw`{[>1YuŒi\zMυ;a~yܠ/2zXK-kYhv7vj\Kߕ T7K\m JS$h `Dt{%.ҠTRCps\h],2aAb #j~!]U/,}/Cg?@~;yb)YCeړM:.+]Z2ͯ>v9h b V^xS6p{ņBHE+X̸.Am??ldz/ϼI8[;s't/Z(6 C'HR acI4u7K&tWCwV߳7:x{j4cDN+X-Zb Oյ'*j{N)-A5*OrC9؆ZUA4ۂm #AͨiFr;wTiĝOh} *Geo/vR6ZHrjV6131XRv@:2|*#C.V5 xdǂwN :ݥcKeNaD i^B=(0k%U E%'fDw Y Rڬ2!h@RXCh=FX_r@Xwml/U;$YV%wb-݃ӕkš#}yt`K=L MhL'pOCw tW(=Xy:))A'Y^3a,R`d)I?U;&Eeq7b|=:W !Y˖2)%*rfkWUI#ڦ+m|^"yU~KSo@4[B>P?R}{ovg5m•fsFw3!p'h]䞺R-߾qSY(I6}x$҅L6`S)_]m:P %~nc=>su[Ep-_UV/rv$/?#п lRAiʊsD)i{P/׽Vo*\"" >gt|*U_$J I~ѶK* P˵iRA3;%#m8w!EǪǪ m3i/y5`!m-7Uz ߆XN}N 7 wu Has߻UpEﵱ^Rf82 ;g~a~E6dpH O잝yǙܪ$34ȥT>hZ0+"$7PSZ=_y^ zfae-ooX.ÊȢ\_>شDG;oTQ.ydE̶G0~bbϒ]ވgщ(L58c؍ST)o[Nm^2F3F/ݖk= n;rNq=MwFQsRO>B|.3)x۰p0ǔ =|Yޞ :5kO h%n [WɊlfĒphU><6=,1IDh?x%TkN̡2 eG;Fx كӷoL_KA\7czo#1Mr;U[&+Xh+ߜ7q B,}RJ/O,oAeۺs_z"܈صekܖ|J; zZC[-xnm `NFXi# b{ڙo"eUo!~uKix֒M8;ss߬-*U((u֙mgW="SR酨ީaL,ϩcBJYcXꗡvNqhr Ku^(P62q[ݩgp<#w[.}6WwȪm`4"\*onΟwf#Fa Y`ɔ.#:b, yXq- =d}SӸm!Ѐ͐ B9[LB ̰0l}lx<{-K&oLӷCz0|T6TaװR&cOC(WAH?ojl?VgU@@ZQ) XtNIaǺUiӮ@W4_O%WrCଵjcA >[ku?V\)ݻۼ/}SURU,S8Ni:#cfyPs ]Jq`/>U([mdKaP0Fvzw\nPnԬ !S][)P2axJK5}WݺvaZkg#}_AoAzĞJtosrF.b1e2;<>H?^Ɗ7Lo:~ծ~8)/ d96Znvӯî&k£@VDppR|" "jp:[Y4]FWyerG9NʾW'%S;/J^>~^GE]q^Ћ蘺ymg7g1z_ 5&N!uxp7h{m5BSiuʽ w "ؠ{5]uF>=Nc+/N wqk_aA:=."˷/(Tt[T]iY)J2]Im Ѫ"EٍƎ]LQ%߰`!HD' T(l B'ap8?':] "& 6#5A\78HQH?D\v8'䬧h?IT($8YGK.£%A&x%8N(KtC<'n$L$ eKTēd/A@=-OH,wJ[TN}OltpLg\ԅk]p^d?3YaF2Tr,zJbb&b1 W % ck1DBH.z0k:eQz$K,1IaH9N4=I&AZIy4 A5ڛi$7pFRiA-8A?)A͑$J&"HY@T'OQoH8 ۛn~GDG"I4D*QI"=zG+ 5:fp8vG3PoxD#-'J@) 8l'6].0D3͂M0U&uxhE @e,A+*ExjٍL.!QLDGIJboJ8&lLz{Q8HH8a1sBhz*fE cTu@9Ĉ.ONGbe=~z{;҈;b@h8@u"0[ic+ Ro /Lhnꛌ*}iN̘K+#SV"r(HhxB[Lۣ "ƦL <ĈW pa VR&HTgNmĝ&85OAC„j?TK0;I̘cGJ M$o*KJ#*,x< !ats1G"1^$pdzd9 A䑨ÿGsH|sBńD bEJ#f7qh .8^4[y C"< ŽS )r2 &<;(!fDEBc& 4KZ~d&_&u*p b4a(3E:MFL1TKb G0dž"k'i(k2F!MJbiqV IJj=#F*i숇E$&V4&a咠OG(Ka$lcl/2{(vaK%\X$ɝ(dԞT*<$1IJ~I)2 F,`' 9s{_q(b'>g{zKHs 822ˉLKx`WuJYx Stҽo zӐZVjQ`ѧ`4 #nЛiƟN 2Oyn@}\(ܟb>c-$ |04!S|?cfY#.b!PtDMTXF#tJ 3J+׈S7x~A۳T[G6(:r4'euCgĐFz`FB}2P~4@lL$IYRGlbhB$IÐ xdAZr0Eˮv}BTh?fE=7"6 H0MYk< +R+$)!]]MHwP##zlt+1[I`11hLIe2QBfɔ&q-TVQXTTHszB?2ļӿdYq1Wsa!|2w8'[~$b1v?Rg؟=:ڲ1jF,a!%S;aOq1d&O*u#C#4:m nX%=IyZk̋,^9 Ja K,l@(ӻCB@"+MT&zMKw0N] O$A ^]L~1KIq,=yM+yK]@d#fT+fX)Tw2Ye3&aV#}U|I Ӭ$(&dH(9'==}G,89ҸS~/&l:TTO_brh)l3_T/D("t$~CB0';G E*w}kfV_}_%Q:YelP2] ֌s%S(eɌ d_vŤh9aue#.".Wz3q 1%$1bb˸[F]Nia{pj#3F&pejgIH/ #%NQMFچ_N hC$.#WyȰ667 k~h|=ڷu7+I}c 1ebjFPtH?Mu,sʩicHX0(&WF4n kLr<8;{NxSQF}b븅1+@Jx8|Cw 2TpG=Fb@R-J3z2j p;&2;bZHsKwDCu> ˽[{>+ 稈M?E( 8~ui 5AMPX<'-R;D)E@)FY!g:JI pfU?Ә$ #0= +ͣh/<Ȭp08 "!iܟ1HC<#f71n 2^[= )h hH~8'%b9"n~gf!d6f]p/lH]\<Ъ-B g0uT%&uԅ®a݄BN OH-Ǥk "'б;볉Q3I_&0_Ԫ8J:t U5CXRXWFT%yn`rk9(  \uh@ydP{ӹ̼ۥ L+9*QHVl!QA(&~n"Y [J> J 10{CYe"<` VZ~B24hj/ 00UX]&lAK<37QKZsuf1WUb8~ZN =&ܷ֘2d{-z2XJ?40Eu!s]ɜUdP238[Ot+5p,&F܈t@ƛBnKb LwLxk09)!G@-r1#LAI7ʅwRŚx5IroTZcEDQ:X`m)Hh<unMP#Q*dh!،ݿjc-4ETKbM/mߨhBɸTdah[kpUVK 9֘`+kLvDغ8=f$׃8+uh!oTL*JR$/v-R׹ 1alJw g채 4(/ÃLwLsѦY_YBRMX4GJԆf̧:N4n9DHFn2Ê3^tjld9=du܂%2ZBQ7bZ1HEbAGq<*)by7ȫx:Y󈣠_tIج`Q+Z0ES1;Cl ZWuVgtL:Be,E\dvqQ#3;{ Kplcd*zrjIp05`Ɔ,C-&ĊTp0^ 0~ϯ8-(E*>Lz^2z'm(McE,3Jn;k%eJqRZ)Ei(E #d{<7JRB]V1kvP .l\GfyuG6}YC?vyt='XGbK^2I #etvxF?o]ƊM @x`S;aUr5V7l{5ؒ,u/.u'_OWŵj9OƧ >+wޢBx[m {XNV42&tV{QMx[kqmU5͗G=c4TޘGE.eln0!"R 꽇X8N36r<- ^p~ }8ZLwiЬ[&$!]+Y'LPJ-q<(k32_/2g*yؒDTP}0d>rt9ĺ=9l`[\k @F_x']^w0@* ƫv2%4N0K7c[79fa k4 {#O1'@l㮈wdz{a4]9s s] e^ y;ou=H@>R2HT5*Qז+/cz.z?_lG!6ūXD UMje=,~?:Cp x]ž&{dqoU,N5H `ik`QsM*g$SV  <$ϨOkzY9f 'm Xp 2KT5'lIք 3[UNjY! 71*́z`:h9r.[Ȓa%R\0Veg wV.ea" יbJ{k.7) ܜC>`I9\A$=^ȫoTU՚LCdR,{ #76?f/3 iq': Za/NQ,\(8i(4L7-5p&޺aSٮ_mFӬn{՝vX6ѨoURmuzznWmz??` eXſO9$rݨVu'ǂ H;XOiFy6 1{n^NQ<{󶶑_t~ Xlފeڝډ'#T_}&E*RnZ츦6:Foݷ%WhnkaM݌/A.o}~_Nẳ}*>+627|(ޗ|{e5u*qWb%a/媄8}MsgW}'zu!ڻLe :xX LtlpU4żn_eUu:fQ4\^[rn}dc4qyk%Jv)+kچx[ѴMӖtn+ĸ5GJU?ZQ]|!WcwP72w? #\ ']bݳ6c"Wzk Qa=L -!/?<Imzr'v}\{v˩7L #UF#$K?wr Oᐆa(XnYf냉Qf+z3 }4MRH/h^ .akh3Ԩ} [m2sk䌊l9TڱD QDou15yQ Re>q1~"޲/k:9 ,WQ:oPrU.ZKuGvo}J'/ GkU/fTnVpqrcUnD<a'vmMi:g,L[>*7hY}Nh;$I 1$<dMrFB@n}K,ZC؉@9Yס[Qϗ*$fSx4Q"tGC*WpAלt~؅WO3L+|Ĵq^[,} H c`{P,z1g_E`_Z"81nLU.k,]@ t:x8"$vVSh~k^f.Eό1 dq|bQx_ i'ޗ%\ 5WeIYJ@̈ Z%q zq"MlveѠ(ݺ/gfM_]_gO.!fCkw /&F{޾cIr >.YPMqIмXi sr<b]m!.<ΉHj}pQ6JWh 3w*ũtrg$Pp $ s&3Wmkڣ ;V3H*.#B[fe}cKirS\mVfvL⮯sΦ)j'O`nF)x̞;ȶ-h'qs@n-ì8f#8]4Kȗ%}l:\# muz,vc(P6~`F١iNlۭ)}iRېczwWk=׏{(Uӏ]\FTl4ӪJv\njG:HcÖˡ[U~gJ+h `¾5iJNO~9aV*KcB%=NNekQO.߈{L'%4M\$819AgOu՘bl 6^w滓|ǓvZĊd5HifHXlڇ' 3⃔|&|TIc; _$ՃO$ $& ז~“eS֜9 ]'YdtmȖ%瘏MqMn\ {\70봳4mJRC ~lO$"G͑_f6"3En4aPM5 ,`M26Ń;<6ꗦ 8H#۩nomNaKo ԩO>F';P\pþ["EF/ZU/r5EIVy .ah}2ZNDKr)onVF$9ZQf?jL[8Dn=1*wT*Dmx~DU0;F~x>,pf=:+/Z*VpHaG8ph\4ƣŎAIL9JCRwl8PH=B%'M:Ru XB&&t6S IM# 09rT߀H7hGw2XZ$QꀫB6;WNt6l=EFbz ZO֦qpW{ڶ/ݢmnMҺNZWvz8bqpW&;:YܨE_ް] B.W\՚'B`մk6f=y5Gc4I hߙ(ikB̬s8K[gJ?J24VLψ}zLb9g!h a} F@Z^bK~~Y~J9 ]I{Tl~uY([;Mҵ4澻`-`ny~j $P/ j ]\ wX^N}۬TZܦo }[о)K^+gS~xn )t79GC /ƼK=g3u7{:uDFb,sRӾāFĈԘ?s] B]TNU'Rx U0\+o"!&.4zyǤĬ-]z}"nLTMmEw[$p$|h<ȇm:+xÖ$WvöX*/ lzRe|p <2i9ßTe$ JԒGQr\%;/O.V,YfY,|_:'~s*| $bi299I4TLcCo+BW3RPپkn*G :VŸJZ7B`L&(c_'~7Ai *KOSןQ+R{6?by@ 5Gځ ttk+RڿWntݼAЀQ@wb.(|5"oS/IlcZc7uYX(1/ryayGxY*o:k} Ƈwʾ0z2_3n=̤%KVަainv]谊ff}noa?Ұ:̟0A,`FoW(W3&\j+kq׼Ȟc \<}ϰ㭣݃% m+{v{]"ixd45p cM Pƶf!R:$ %YpkEiQ :r*sw[@^Zj*e'$g,әS~w@ѳ6+¡w PT^[꛽[@Oe~iQQgLa7eʾ\Y_ h mBQ S9 yP9KJ#hwjZ:.<:/҆UQAնg #ތP*fuq|sG'>#e&w6DR2isWQféX8}\uNEY416?Τ3 &ˉ85k@"zzkLrӉl!51FłR#cAfPJԵ1g1ݱӀH"0SHу%nOIPEp;u5CYQrJI.2Lk<-^Ėo(f`3٬=sC6*_Y̱1zd5ұ.Xp+r`B4 E a 8H TC~BtsmG.~) a^UGfwx,}Ҩl ìLL)*/YH6)I Yx˴L{ uz'e9 b R;qENXmD$0n}wEbk895l|Ep#@9Ra?{ocf 6]~Dj,&D.^tg[n9OH>Rsfait~똵Բsjڌ.,Vbّ>MfX? v_' ˉ-Iq|j+v "%{j I OHó b@:=N3ADi%vd_ɵQ$ɶM5D@y?HĐ)lUx;TֆSl'$:1!>V;G6vB`̓_X>mYC*'I>`m;)V|jgj6ܽm8Щl%bJTy% Nx&ωk s d3uOmPR07 {<^'GW^gX$dMu$X-`繈쪍%10<~ժ)$PBry.t4ЩwN\c;w?۝Jی~8+ML2"aaViyz l{K9ij7ͽJӾӜT.M12d K,)rYqznQ =a5hrٜs96g?7Fa"~jԶ5'Fب6[ьfߨ5hFiTόfh֍fըGʨ>3gTj&0';R ۨ~7y=Iڑt4r4,_qtOB~> Cce+>Ց+HWϰ͸+ {p.%Ԛ|V3Rk8㊝<15 R-*5! H]7J[}: K;Hӆx[r#+57ZX [:Jj-nWmF]K.gu݊zvmʞ^QCv jįfdyѾӮZg(ئr!\Jcy3[ˬULG؎[!}fe<2Оy0ʴ ievZԾ7>/Ϳݾ3E<>}P.01_. 'nVy]6؝>!?Q#i$}ٵ;@B75#\?]h\A5ȳRCi!iGV | gO%=[I*'s ]T3,P7F4٪mݨ~puܵg`#&RB)g $y_%t9,T |?7O)ۼ7{ْȕM$b# mq}Slc'JC!v_QǛ#GPT6IH{>:7X\oXDz[X/U@fp T/ kr ` d 9l|U 45KH~l.7:)c-I86vxSi;! i|Qw [OL9V? wX;"McdGsA$;<[]up;S)$Q 0%Nh/Mqz| }+p+{{T>BjJ7m1M >.o 6JJo!u/q…n81/ il9gN㩹SS0SR,; AC9u T# y;!hIpp;Zw>:(nѡ\<`g>460WLwo&o?{C4~M))U>kiPG?N+TO=xo81=L9@L-GTS+?89B SnʏL FSa}7Ӛ{Q/ic3(]k=+KgyI]_^/Ko2Ey0gWv^>/kb+mX:LUn.$1a]9 B%OY1(dK9C;&2m}|T%I1 蟮v:f`&N0"sPʈ3A'F9f#%;%+ *9^kv0wRna"'v<z=fR}ݥerL*2b _f)qņBHD+X̸.A7=UШ' ~[˝4ñpc=wBĭF  >xc4J)ưX)kc,Yy, C0ɤxr3' j{Fx}1O2 Ƨ:h0T<xcX8'8>pO~) MkPEV6!VUPͶakFX5joCKgJc>-=fE1@rv6eLΉϕ-UpS*Q 13 2dzj:=u$譡T_#C890-ܻ T<T6NHJ-1Pp q`ơePIiR2Ĵr@aqvG4( xP9NF1 d- z 퐩>.W;Kl$x v`te0Zqd1!!5#E1^~D,D Ϡf]n{G/Y^!+k`逧TdI^jKQc?T ccf-/׉̴@jm,]> 1Ǿ|zcc[!Ʒ1o׹Dd@ 1iJA5VÆkHV6?Xh1UfU-ɃOl@ nxKnnk%mf}}J0"p=`w> @]„o!vy2|KDP|&چ`4_t Eo cw=ۜC>sz[Ep-_iV*9o]Z{uu t$ryoxBK'I{Osɣ曡̶G^lnmO(|׻;I=}cH7Xn{Y:99_:&4SP1&rR{zzG::Ponڐh럋*FoCP ,}5SS;̵?ቖsyHcu] *8m"I/)e JQ ~JpZރ rUuPi-zb^։5,;q+uޮ_S~aGsx/U9z|.0ϐw(XR8^$~v;$r<ܪ$r}7jЭTLfO,DHo$Z3Qx^ MZw2`?O7'vcz/2hG֙(vX: lK8eDlAz kgxa)\>,9^8|H/j;V8ECeYUVeڏ~G'7ɅP7J-ym~Q1NVQ#w(̂}*,\gbMe8ct,oSyxvlW~7ĭ+v_dK~ 6n3`9ƪM!wa䘤{@,U|(5&2#MVjb~el#bQltfvU QZ0 9 њ>[xʟ6~`fb~NխvJkVOn{ FGs_{(HA#RNQ:o(BZ+'@!;7hJpx>{Rj]tgm}GN!8&1$)}ReP"B瞢Z5vqq>Udǖ%i߶zL`fj-&BpfhB:>s<[E5-TQ!BC9>*#6TװP&cOC(WH756@7 0U@@Q1[&;"goR!߬uO*u:_2?"ƋPTƹPpjMՍ1 `VѶj/{ &~^YON*U*U,ׅ)U' g c,xCE9]8GPJ27|AV7|bQ: IYAP FxwU=k 5*}7Dx6QYl -()G(V_*b z^ZviڍiTۭc;_Rr*л 0@|<*LtHU|SNq`S^rm08p9>1P6q^ ʭPVDppRЩ B B#Ee>pB7Xb,ʩT=kl'_؁K^;-f59pk_}@oP~; peoj5(9CDWjmT}嘾]{W7X)[ ޼CPoo2 א:_ |Z;p颎qm`߁T|"t2jpgU#bUتik7&.CǬ `n"9le{p}"KYaǬefN6)΂Db@7V;!^;F{nHeB|x%#ˡ YBoA:4}FM@7>'b5 aA6Ӱ9ªSX>!LHLOJK p/h'{0aAjyG' @k/ GzSP=,2Dw`w8ݖoa[o!m}nrSzo%xEmx:83־6_WⳎ/#S:=>"˷ ա씵[TYi^7!ա ®suho";z)$gIvK*%U0NK,I2 =,dr9'lLQ&<ѤxHf8$\gyy$h,s[<_g68*Cldp(fS_&q"%Dju,a_԰rj`zq0GBIA:)ur1'y<\$Q̃j!4 .n"981{-Nd(g[+ᘆсa4/c ""фfiL4N&wII=ݠr1z>4}QF:Kdl.'3`_ A&4 [¹ꛌ<~c6S)?9Q9|c4 O16i6)SҞ"D%]039!oegB\,Qn~lD'F,o@X9"HfQ]j f' ;ǘ&DFDNSbJI`@ܛF(s8#% @{~\^D2Mj& ՗{(aB"QX%'?aW1/<0{X%'q n6aL{XQ8 $iqɚ!@&u*p b4Q$ E:M[%hi/tPPk;d/?H @Z^9@,dz"u?,41:|jlTħp|q&ܨAo}ZH$W'X,!q`ςK L%ыΏgS&]^Rq'LhpE 3v"j! 呖H}:%ƊkۙsD~Q j =Ku$./,#3VG?xLt\Ì2H/OK%p1$CgIVB@L%~Mƾp8aARr8㧗}PV<&\A6³A6@EK\ yna?Xz +ɛ d 5zЏ9S*VV6 1fN*2ˠ$LqXEi$/XрxFB72gSRM Eȷ&(unJV ֱ}1"' uV;YctQ}ڶ.Cfj(&Mg\~( d4 e+b;dÈ2K•{EzJHbvĸ E@>ϱ|9!˔.#/j0M-xo $ҜdMcA0PUJbX]&dAs<+7QsqV!WTv!(-xNŽ0f#"<^ dXL%:2}6Ef]h`PC߃=z.dΪzVr;y2M 1JV3Gaj0j!(uڢHWL ֆE Wv/vbp=*(Z6VHxiI%X.OURY)G{3W|c<ԛ{ZjDy:/ad9 8DH! f 4kS2z"c@4 AL.Ɨ3LkHP^_$qvUuritY.es*fF &g JA<߀ i/˒6]*k@ $L؈,WgtXRF1=vY6=%&& Hi+.5e#'%m𗽓o*#%>3> g0an_no e ^i`FǴc‹^+讇HM9DhO/sX>?#\]z'EaٌW$LjE " koۊ GέJuMrU_䐄`-6WjclmXgS1[S;06dx\[20TƂ-|%x*a+%}BQX8bCl}z0SSP , JlyVj۫P96لFe0\\\.K9PXZDF|r.' )sP{mXS^s0u*s^3fǀhpC~1ҨF ,P ./e>NzO@ 3zB yΕ477f"}&@MnSP)bZXyn YsbBb `a^Uq-91xDwԡY_]YBbMX4{JԊV=T}uӝhoDH Fn2Ê3^djld>=bq%0RBA6bZ0HbA8seQI˻A^ͧ82 @M $r-0 w =bzgH#B6!Θ^$]'P{&l V8>/SXY^C<7B/U͢$j)Lb٭qyq΢!KˮO "n#Qp2pʹ5j$JM*ⴲd(Vlx[jܒA;iFAo+8^PōvޣL!^"PjS- $`clXgVaQI_J:P=Aa@ &l\j~W+>Yш"?zϧѿfr66xܿ-وp0 Y@.B^Wp+MPu#P4~o.6ձqM`\$crC1 FY@8*UgB{P \bQn#_N_녕 *iD%qHIՈz~ozPbq-z̔a׻V54YnjE##`rAiIՄVVUtyXze+^~fiƤY|"yRͦR&/*"]{i7kɈ!\I"#Idz?R<^lOwHi~hVKWk& !Y+Yl'H3SJ) $65q?(i3ࡰ@e/2ey ЖP}K0dGoKq9~z9º=>l`['+@F1./ܜw0G* H $PKh8`&oƶn3º27uG kN/ 1㮀oBCA0}=TZӦ̜:?k]e^ Gv'uOva{jj87v(-kʺj%"*/xOz4++=ޯ'^BXvjsU8 k]Gg"2CpyuHY* %ɱ00~Z~x%r-P|z'YɄ|BAFj9 J_P׌rjMadJ̯O⓲ VJm*VղL6%naXU2~`:ŽI!3]81 ݏHa9# 7lveceG n㒷gl7r|Faw܇Nz yK a͢U< o!سFCج. Ʒɐs">],s7sXv]\HEe:Q;\5bFu-1ztu;w$4󚈄!B`6[eP"MJCxv1a{* S hpeSNW#8Íbٌ_j&Ux -k&NRqB1Hh\%h>.S'se(|)w{6jFL;`r,%LwmN'J5SZ?)BJHSIR/Ǯ^m1bsJ3q>f|9]{*UOszv_3(Q˵};z1GBIξ?nP uUmHO^(%}fZXͬyL(.~츌-eV'} *ybrfVlz-r'B6R ;Vg楈=)cȓT6)6"h`iJ2L%G1 " me~ut )Yy4TϢ?waD3 @"#޹2,#R dѝƔ#ƣ RUY'B_y:^M.bgQČ'r19'g * >*c5sOVtufNmqM^3n |V W&Vȩk0 b#QcQFݯ6|ӵ O}YKS%sq*ƒs<#oh'#Q+-(f{\4f+>_X-0'N um`ss/o:^A CXuQ e V$$HC/NJ틅y"\QI2I ܴYBQfGr|$4#8ݕTކnRJ+IÔD5L\cmTp6֬XDyYϪv'`;0p#~n@C4Y?jTq$SNJߤp*$|HcK]Wؙ(CA5wW Fex- +#;JBE+1 jKw΂^ڼX7ϠGˣN:G\egxNV2ՏB"mDȯ9XZu5]Uxؒ Rwr_K]T< wQ|bGWШp_^6#b ^Xm^q}/HoG *초A ]j- ܩ.-#[߯Ͼcy%'"AmQ[j/km4_/_/  C ?/Ko}^q sdrwubn9|^v-jqݭT;kia1P~[.tk;`nK#j[4%1U*_$Wލ |EqjЛ_5NTb%nLWRT|wR?`G{$o8yy$J,.r倫RZ>N&Io$4gkQ g7>2yV|rd/{p-\kOJMo{X|nms.qW2n}jN-xeڝډ'#T_""k)h3;. ,;umbg/Gv9(#kj1E|_*'t!SPO> im LSo'[n@]TK^R94~ӣ/aJ?yᄎ}5^gGeT\].v6=h7ւ>8#\ 9aَx/],Wkk!}dc457N̔G`-(;sJ7M[<) zթWi9z5DOٹY?ZQ?ucb>ߊ^۫c={< C6Gnp7tuڔN>Q N"pzܵq8j;<3%;\ݵEaaIQ9,Z_/&r.K*|lӗ]X'a#!\иݲ+z3 Ubm4_xGR}wh 5jM9V'-94gw,*#ߛ?f@ 6@+bD;![L/mv@֭=#/#嚐$\H,G8.AQEWCl]/jV3[Je%݇ SYŵʍ}WALݺؕ5aboq)X|Tn@> $I.$<d9rh! %irM`B`ё bGB,Ρ΄.NU?HgSx4Q"t$1g=(SY&8O Ep6cD,>% F.kt]@ t:̈́c"$;NGyʩ-4Əm/BgF]Lsfg䢯|-dZ",Bg]Oo%ů:b6D'*Eb&䕈uQ(uQ-X]_gO!fC =o߱$#BEŲNB1jAn!<b]0|8诉Im|dzY!;5L&$;&;![yLFDXP9%#"bg=: tV;VE##[FX;}7%!Drjm uf[~sh|5Mϫ1{n#Ag_o|dj:2;`J,ydC( CMkmCuNNx o A!;4)1 o|3/-"P*}wL?ؑ^'{_{q ~C~^SͲK-UKv\njG:HcÖˡ[U~J+h `¾5iJNO~9aV*U vOeB4ަSSvԓ7 NJi^9Ipcs$81 !'l`{ NRO1X&k+8#q-ݵOf+))p:vHIc3 &IH& nnM|2zb5g(bN$xWIf]qx E9ch\Sׇ'D'&:,M۸xr[< ϒpduz@+Z %cWOҤVZдsmQJJ\-Cgjl߯N5| ߻z{ކc7Qy*0FŸJ6k9nq.eH[Yc | - ; !)"JoǽS>( )&^Y6ͻyYd E.E>*CF>5 'G`P3\~jFd7pN& T`$cZ<ؽ7;k~i04F0`!1[о`Jkj|b` !&g͹â["EF/ZU/r5EIVyn\obaaa>f'fY mVH7+#CrR{+'u`tjK2Krs0}vlٰ!EzSkD_1>+/Z*VIaGȩi\4ƣŎAIL9JCRwl8aUH=B%'M:RuXB&&"Ӓ5Jz!x{&5G.JgjA0r=Z+Xւ`>Jp_U2rfʉ6ՆqУh\(AWAOWA jO[mҺIZISoG=#HJ,8grVVsByv/x͢~\sQVk4j VӶ٘b T  ?MRwJښ3k2GfiO넒 M3g-SؼΙbHm5BXu-&PWXccc4_h~kWl$f]i~uY([;Mҵ4澻`-6Ͱ[fa}*/ Ha>g |G4߷:AEmwzR}?_r6'L@/ꪛor/l6E^9PӡО 4hͯWC]9XvgX#VOPcfu0 eo0wURMcU`*44^0np:Ƃf 0uy,yʛ@ ^j,5k }ZƄi/>bōʷm<=.zY\^Gr_wŷ;/|ز1nKC4 YO̔Og.>WƳ 3woDAZbr^:J՟kq%)S F2=Ae"NUC|,A7"Q"D,Mf#='i)bl 0m%Y`A J6wͭ|^娴ASJ^ WFh"Ci;]#eơ3( Dris0jweZ^ybGl;S|HW;p<ђ#nm%]JWʍΜ^[z=y1N,%rJEoHK6ۘ jFIx [iހfa4*"˜|sHZ2,[V{aiEv* iin:H~0y)f]\=Hͤ~s)^ "_4: E|ya7[G-VKMWfl]Hk%֌GVo; M> ]ƚAQ١m;B٥tHDAJ}`8֊2*v3tT3aŻ{D0!ʺ\);!=Tey䰞<YڽpjBVt}}*{$K:` ̰)$$WhZ@gh{e?XtbZ@iPu΃)^.imXMb{:.<:/҆UQAնg ؝(Q3JS%,X40?7:oq4'>#e&w6DR2isWQféX8}\uNEY416?Τ3 &ˉ85k@"zzkLrӉl!51FłR#cAfPJԵ1g1ݱӀȭΩτc,`Y\+s`vZaq ΃sPg9@xD)N'Mdˠ3)Zz[Q~ f ٨|cg1ئꑒHǺ`!0}{|9EEtE"Š rLa荐 Q,ڎ ]HS(ü$6*YZ_)Q@ʇY4"<<3ST^5m$S iӝn ,Nr@}'lIVi # bb]tZ"Cr;?%uND9#y~;P"{"/@X>gcf 6]~Dj,&D.^tg[n9OH>Rsfait~똵Բsjڌ.,Vbd/MTn[S![ym{$,lXfujE#"Wg:`|$yclREE]< oשA'2ǫZ9qֆ-c,{pb ڲ;}k !buA@d}m@vx֤>zCW rbKRa:J34Hޮھcflbxtxb~a&NL=qZIc/cGN{6&`P.Dp('6H"C#,WNXP_4P[Nɓ { DŽ[= }ŏ7N~Yboe $h;[A}r}Ce+R}ƶS|ٸ'[?D5Ȇ9^ʺ'A( {B>l~QUg$>5 # ESľqI>VKm,y.=jcmb ~m|, _Atz}0 P\r{ޣK ]+M6t9*v$6_J"HXmU|~ީpBzf[zR,z.zN*u6MsҴ4ճK:nCSx #t +f}wʾrq[2{|Cmwi;Z\o6'FhύQmȨm92v<6 ֨Fe4c7jM>4l30uY52όxk4}`4LmΨT6jhćz 7 '+.klm|Lodn^!v^y* Xi+MHnswlsy/wYm*vHSɫ2'hl ;7 ͥA$g~e6>8N4kN֩~q;,Nl9Sƞo"6uNӌJ, `y-ɇ]FWm>W{]j9?$^igm`PFz iD+B2.9j*rVo}e#H rO"ֱ=KХY"C' c X3Hpd3YB. e!D4pM $%ҬK(Nz yK:NMg9+=T}6ACkk֫SNՏl|읫>(m 7|Ic8KSHa$)J,'6ܯD,^$:- rBdum} R%}RuvH]kKܻpaNKiLTAˎBGj0PpNǹ8Ft"tR>C`):&J[rt(_4?8_M U#6Fc*m`mOƐ=G?ho8m|S oJOt7nSJ?S}O"Nq oFǮqS;oˑ@Ǡ4ՅTNoi¹T#vTppn3w Er|Z aZO~ usx5wwnWKқLoztwńix/*Sekm=s.I%}AWNP oV. EP LG4UtyIo}w(>e#+㢉;' px},2bab$;r?~{NxJCDl~! ?̝C\"XqhTERb`Z̬21$tXd1 TzY9gKxޥB;dඅ˥ծR")'6 ނ]̼VY 0b@HHHѭwWmi!G2Qz" <4B3gk[w tWu"3-E0jOFDZ/Vm [unPC v'mRPMU䰡ڮ*$FƅMZELzUzK&So4[">P?RoaI`Y_߅8DݠO.nl3чGy#T0 !؆*͡W.6f?9k6琏l p˗uJ9F[@|d}]h*\iq}>:1Iyz\hf(*9ۆ[S, .NR}O,R- ֪q*$DNIp'kb܅^^^Q/μm>䛯65bJKvs-ϥexe\3ҨXw N2vKJ{YpkTܮV`\6@$|Z:>v9Xubg K/NJkW{_h w\8ދ`|N4: 3?]`3 5I`{Aܳ1x!*$Ʌ\` +2t+8S )I6L3^WwC%~Ӫ XM1X087ދ -u&GA& >+GrY#2~^X W"=Ky#E'R uZw?a7NPY|U# >%fr!/ԍR`vK^۳ETLDT9ʦ;1ccu=ਹo! ~zXr3/۔C"a>]fo*7_mč`>q4YA Xbuj|SǝGئ%89&KJz5r< HӦ_Az$[w+[ W}K@920\B^ac[b뫔vz9/by 07=~/ǧFľ_NkKD/Tj-mى\xK wo=|vUWJ+-^y]Wn- mWu׀™hKQ> JeۚYn}GaX6]C aHzSppuƚՓ[A_on#Л#\[Z$JNluD1յG;l_9|l^U VVö7ۛ/!j5N*:&V=Gc\eqw )*>(bՈXl9aڍ Ơ1k*Fw7[e_/GlYymo6Ҡ<4rV1p~~`F iatQ6Rhk&1>@Hrh`[uM/|D7eω;>/~BzX4,|*2V6OSb99R:H8 Ab"L;=zXlP=ڪfzpQ B->,Pڋ‘^D=,To ~'y@, سj@-6p(<{}[knV2m7BSnuʽZҝBm:#W'㱕'UwF]C|qE}dJ<~?3U$yxvA:6TVu *+͋|{_>:TAVؕ~|P V_}^o9D+0Bq\dO' $ey!|}&,)E|L.dS:J}hR<_$3Mb.<%S5b 'rc.~\hM4MʓQGq2J߼&x:L!5+Yē(o6,QOI2PO i%p/q-rF sjU.h6_< 8B8-n "a+)p 6(f4=w9EQ@2[*=ٌMv& '#QLQn-5{>lDcj6e'R!KD/.ٳX`D Tp$亞ij-@-@YDM<@?g4xT锪 )b@}N xz#]N7ɋ R'ywzEmv 'F,@X9"HfQ]j f' ;ǘ&DFDNSbJI`@ܛF q.CL%/ %hBD&d"M"#4/vQU'r(7 |vi,syާr1"D/c_,xp !7bb%8ft<GL @W!@&u*p b4Q$ E:M[%hiAk*fgDI6³A6@EKtc8$U` ޔ[ʴcS1H>zAA#{ D!]dA6վ D @$]px15 FPCB 8;3dr/久YJRb1H<߯$o7\[*DA?LXEXZ|.xǘM;,0Vđb]i;`Ebd+ 𒃓dD(&L4=8xG$8Ѹϧ`_LXcqXѩ`&D`-SqYWgbwFzr1IP89L>(RL@ D4 _x**"JKSĒz d: *V!);x7Z#q*)GF_ĸ3 s%1 0p:Kj/Ȅ㘿e12},S=#LB()~Ydu858b[Bڪ>XY1Z_g4[[x>o h7f1$FLr}[Lh i.yN)Um\ (&4wxB *֘L'yqgޫ,фGL:m6ƐzFx7]ò;0\U:(*kK#jz1h-ڭ3e%17vh4xEi KT0D'hl|Y|Ef4CAp(L Iπ;'kXs]$JP 1$F'!Eh(A(rƣP V3!tdV!#?pzO3p$x89 ~ 1i\ښsyݔAc&cjEQw9N摋v m\s_9vZ?*rkPOA|2aġİ`yMȂxVn5KBCPZJ9 )X!{sa̐GDy`J"ud(l̺<ճ#z U~ieA*P~v%wҶ4Vp8f1+GB4DԦZ dA**HpٰϬ(ti+{+Q@Lـ偹 IvvW6X}EOll+[q`,,+]^;1޽|g}WešFzh\lc:4H.N?c &qTXĢG:O +T҈J>|g[)w@ki<ԊFF七wy ok-vH3:VLӨ9),eIEѥlMQM^$dUD2 ioCq~ )}y؞D*lck ЬvvM@"CVOfV7wSHlk>*~PpfCa^eʔ8e-9)?} %arߏޖrru{|j#< OVlݍbx']^9c`,U+:AHp8=Mߌm3,;gu[3CenL<"8A _b:]`zb4M9uZ(ʼ}x;O랢 ո?poP[uyaMJEU6^ph8 U@nKc%`0atk5KU[(&O Xr>$>>-ZeԚ5940$Q_4'e[7lvU>e$1lJ°e5u{BfT˭pb$5=;` ӧ0_ UڵJVlcEmL#l gల f/Ȑ|e%{2]X+!.Rv/K쀗.g;)^CzQ4Hm>z !nQfE` 3]M,׫8J~w$QN%ٮf5\OϤ'랭 #/霖-j[U@dH~~F(TEm8 l!WӟգD'+$P'BR9qD`ȓ%+-J}ɁSJjihK \3֑u!XǙPnSa.eՍ68(@r4Hؘ{'+rQNj*C ,j|5 d~ʗ&{!չW|$P6r }Ax1>ycxsE.c\s15!$&;ǹ@i;@KD#fl\+,[,9䋰ME}6Rt|ǸlfCe{%멊>164TCZBՄvaKŦua|kǀ9SCVxbFT]3,phA+듈&ɩ7W[|aImٺOsP׍7w KLf:vk\ddC~agE s}2T5Ǽ)?ncwH߂[~j%;92*=cbbYzpOgf)vIW!U(tV81{- o{{:̆^.']ӻ"Q%GAZ]VT:z2S!\F5$Nţd;W~]vqދRh:1e(aڹ'{#~flfy}^bSl6<ĘK}mWO-3=9,]g*]V+X cwKlRx[;U klGnvrLㄩ/*1Gd' SdWoV6͋@-o}5M-HLF{fʸIz&2v&PՃ|bpv84K9Q 1Nd>Ps\:m)takb:}NQU)M'eݬfRKy??p>^|qlrCE 2<W3T| ϧvM CO+#;st4ϊJOafWzrQ¶4C>(GYQ|R \S4[qa"T%4sdsS)/ AJ)_FsmW}`&Y^{$֍l)Bck^ՙAfũUc>=箾ylKi˗c,Co_帗+Nܜ7Og^ 󙉨Ϋĸcȭjb7PUQ$P|4cbg ܬJ(Dz2f`J`WԶ[ꓒ)'_gPy3 ȍiX4%'l,pK$V[,_rt334gJ%K'4<~n0upsR)Yv QBAqW]rBd7U;Z9y~ _%{sZ?OB=qJ0rYY?x(+"ЌUr%+ؿ,\K`t2RlHZeO5CQ^1'y鄡43 ]脿᯴:TWQw<Ԙo ]~sPjn|ge6c+ ./LǸ_ˑeM)w!zyNi^V[p0/FWB:hU7׊JWQ#Y_.T׫ܨzOبy^R^ Q%a_C"N22WJ+'MTc`\'J&6>W]aϦ5\jz2!w>:6,b|% bЊ',#撽âγ7fa#/DRkpzPڨU۾&qQ0pA:K[N*6jf|>.V9sbgfA&wBm[(cK{6De ?CI֕eJ3}U3ZkBZvr}ĬAdz3vmYKhy5]GegxNV2ՏB"mDL/Qa_\pbi=r;~2$?f|`}!KJx=>zd=pnؽoO|BNݧtn[KeVi;H;vuco/</35X_& Ã[:N>ę9\/uT]/Rp|*ڮ*Q;Z.8^mγogkk mV+p_-1NiNm5拶Q^:N墭\<2r*GK߁l}C6 ߸t1 /wz t޵QENMemvb_.\iN_/P<1Fg:h4\_S^?IL50QpD.RT->#6OMoQ;;#Iѐcѳ_? zNo!SM?4QL:xՇ֖I=^Sۄ+˒Na֞S>8dGpw`(nekAzԱF-NgVq2E엓FQR+TiIS/ v!z77Ԡ7j={=X 'h4?<(\Zly-gy~н: MQU=E@9lvBBEzWݩ0y2Q@42&E*hC|dҀ hTߩ#8v|\7~0Ʀbt0p_Zw):`}~%DV!SPqHm[ `ijNn+}҆1g};-V2px4{(y4Wm ] s7ڍD'WECN>`_XCt$dRT9,t:L!qkk!}dc4+ b)]f#Z%l&S zթWi9^[͒ nc;G311oEםcTL=xgW͑.\ ']bݳ6'ߩW Q^ZBn7yS|x8+;Ei<kB'#Kz:^Mj<;[0*d2'*U   ÀǍe>%:/4\)ԛIm-iZ%JFzA#*gxt(՗Sh3Ԩ}7,U$sp߿{^Y_:J^ 8TN$Ҧ"\̹$MDlJ3S'ͨ'NEPQFIiLۤ4i^ޟSy+k^`5CVD3ϙ3'xZ"xLoV.N*͂Io4!!=zNR)E| (WF_]fIxP(1e0EfKØ3,z_E`_Z#B#fP_W'p4t#D?X,$4o3I$v|(o}t ͯ6cmˬХA&4;7hvF.:@>X(}=o߱$#BEŲM]ՁjAn!)Ƴg4Lbv1dӅ9q_^gC>d&$;&;!JH W4"‚:XP8(4q3>ёkt*92*ŀ$xdPߩ| *mvc:cbA/LkR TnϬŦc̣6<"V[:a6$6eZ,.\c.L~%,RZ, $Jfoo GM v%VSh 3w*ũtZhxPB`4tΙO"~^U1!HD=skUTZR\-ma[л8`['h`evMlKEj뜳iɭmtFlg:\<,?aّ _'aNI il&Ӥ2IIxW6<.=M Ẻa*8B kC<=/H<|okjwh㲿Ą9\iWORnc+~'YNHzR͝ǘ'4KS[Է)@D c~E bw6nY'i#]|T |b{k$6n g$%m5||jFd`a 9M$TS>': 2vF gj "z ڨ_6?l"l Xv9uA/!0XR2>%CfIYs.yW"EF/ZU/r5EIVyn\obaaa>f'fY mVH7+#CrR{+\b+%ܖm'Fe}? TcGNǷ#UPuiAۙ1<~:$Ci٧gd ?L1t!U+!+_rsLu˒/UYxMĬ:On>> ekgI<w sæ(^l1]l༗/@%z)LWy݇ H2Y1M^NϷ4}S竗ڽVΦ$ݒ H%P]uŘ-ƼK=g3u7{:uDFb,sR_ =lZ,wn`Q۝aj KX=A3ׅ.UI5 UU'Rx U0\C% Y 7X1tzDwS5v= ާeLSOs ̦''%gnK]QWToAt&ȇm:=5-kIcZ1mD1T^~MsِLt]`+YS{k%jIѣy(9TMؒ6gDN}7+aGY|V4$_8Wi~ vܣ|D_4?}i{H|_As-2ibKa8/I$PeQ:HK=p̴EkA(U*!;$kH36r@mk)[c9(Cln3[ Ktz-0M>F#G 8YƩSLH s4;UfQ^;ISbW=q<ђoG!RL/k59oznkxhD6cle]X ,JRՂGl֟4mlC49Qf*Ųle4 UͫC;W7$ ?soY}\@pG:zgiY$K@A3xg:j;Yڏ,N'9XRoz$wM' _lZ5Ԁd|K؜X.*L Gڬ&!vOk׍i6 kj5ZyL^NK& k*Mo*ذ"41)2QIYQ.e\99w<oYJRVwXK"g3y,-g%ǧKa荈 Rarhsm(? $˰.5Ko~6/cLy.̈́3%gMٮ[vYӓa6Lc(cdCP fH+_@Oqkdf<󸚒A`MmE>~#@]+~n: Q8[c!17&+Gxej.?Bek _xخz& +RHS~M?Hh\-= M_!c Dž}΋Z6!F޹vm[kdOY49=-k6'!XLw*񠗔R(mxbw=#2}FN)@hy6'SgkN7WL|B>^exc%ndJF!}Îx {RG/zބoq±ؒgOS[Y!+W+L('ODy<[CRa LD_F<:-~` Ȁ)@*C MEy0ywA"Gc^] 6Wx;p<9p'nD8&8jym̆'6?)+7 \nsWfIؽQ _`5/!~^e퐷lzI 9s+qbz]uq&dI B4D[k_r[i`ÈsW-9X~VNrz}2 QXK ۄMs*ædʵn$rE 61=•`ϰԆ3;p[TJgʐfjn{}ۦ1v}7=h 2 8Nϸa&.S斳 !< =ܰo7ǍzssdĺcҨԍjר]f76/sojdTlM36#csӨ|56fج1*ύZ826}c3h4LBܨ1 6E'uJGѓ;~oswb~\y3!cYW#W ޻nƕ6Qې3lSN\Cujv՞6θ_г Fv˼0J:dtq^kO|:o'FxZ0A,YY:Z=>ẅT*"MyoCza'0fcgWL?ԑI|AF7;N(uŀ+\-lU/<9jl&!#g_g+ 5iJ8$y^īeֶ~_ad屝%!A'vLW8 FURz ح||w‹zϾQsTmfLMugyt>0?9F}U֏x|Pp(z 5NY;٬:S9f٬9Cgs˩Nsة;f b9%jޝ|슩GKBmG+1lul"esa; *ꊈLKr7P1Ka@%ju9O|>+Ό4"CQNIZ:osU4`sX4RṼmZtV3pEqg_6AnKW)Ѐ$M"ڹ$N;Gdt]{>J3&ߙ`։aho¹3SN>= ɂ&_ѷqݼp!Zx!1h_#en}biWX:Í^v~BGxi?p7My'Rx4^{x#DnCYlcC+fǠvtThTQń.LĬvꯜKԟ;_%+{<_אmG|5a$<\|ߙ1@Nύ\Ёv^m磣]9:ToD_2 #b'0P v&m4{,8'IcéhkZi-25 ٕwˇqDpem`e^qULNJɭ45 24dž \'WDz:{P}f vY%cS{0`pgKT66-C~ ֱ,e3oL M6;nuztND1Ѓq2Z(|KH74֛+C-|\, vUHzOmdx wwh#GJv$ ujk1z!>[ ]= ot7(=uhíwۂ$P$[;RPK2(pCgJےsكaȧ4Z|7Vۑǖ6?\]$տ q8~&hm"rG]‚o)y2|OHIy#R0 aFssJ=̆G]lnuO)|׻d>YVH<o{Y%\:&SHcM.(@@z/ԁ]Շ"0Wߔ?uA Ammo95dP / ŝE@zc|TiWyh' /pgxPFeؽf3X^Y"ᧅ[sMz['uVօԠ{wFݿs  ^s*`M0(,M8^~?~푸KϢ=7|~ oФ,y[WuOvSg۔ 7Ʌ?P7Bs{kx.Unn@.53BGt'p+8jAGHb ~&yXr3/ۑC"Qq}%7"h;!"}֌ͮqןHm<[|ӭmlݭW\;opyY}]AlG/fZnzq:V?'Hѓ Yx-kr ~]pF6*_CvaNȴ&nXSppeƪ/C77@h D\<]]>[ۚlOC.qWjM 7Ni3cDV7:BrH~v-Ym3YY pʩu aτ*` h΢-5lScGm][T!bz@>#ak(T#*igfu $#EN< (']54鞐8;̌ݪl@,T4^gτeЋ)A&gTU$:[}wW|Kx\{/} \J\V4V\WzPH\, ZC٤jGvrIi[Ʀ/pȧ*fkV3ZWx-cϠVmUFϰpHgU·@$eg PJXC8շۭAc7S4Ιҷ51SHDp,7bT"U^S+bSE6YMԯc\:7"ĉycP4 |+Qxx ^{O1T6q^s oE#8GN -) fr١Vb)˩T=@cp'zvl.&鵶"8їC>A_/%H|]cfmmVw$&RhQr'9fSw?Rە%M"{ɂ8&\A #ĵV-N's\eq8ߎ>9Q 3BCŪNߜ[(zmB1:E'[G(;nxǎM1i䬰c>23'{wa)ΌLrH7P!x{F6l4Qwpldw94@0J I }o,m)ZWɈ;-mUNKE/VaVFZjuN i AR-B{R#_H~u`3>!ha,\igXOK,W&M@o?-U宾:-Mt RTn7de1,P[u|' C^vP1!ۨ˅vC{oJNK yC{|v<^+#'Uw۸UzxZߔyvv@g")3yvNnmUWVubJh[RkDž6Bm46xSZm)ҿ:XH*!=4Ȏ)夈," b)bɥv*퍧("ZPl)K9x0)Nf)b2MUO'MFiLlz'|:H*E4Fn:LqIG1 q/DV|=GTPTEP1hԧD.$E>#9Ff"2e*5t)#>$&u5 |1F xrrDQGbQ!5g)l7L1D5PC M 'r`䔃b&TC?=''|Ph(b#aiB/Q~?%g' 4SRGEx@]!י' )Г_oNӵc(}#A $yD2MygQ>jHU14=h,I .8N<1q}baPB9S$#w{Fhoү s siЧNĆ̣B5΢>Fv!TQ*M ʑd2oQE8b( y$$\@ڀE@$$Vl: D2l>@s `!x)bA6Iz'&ьӍtu41{iƳ^Rˠ SCS=eiO+ E8ͨ ~ߧ1 AT`)H ep2A{tII !)A+S;:>!x,#ng0O0Ė BU&Բ(N@P̍01 `'&R-1J1 IxBJǘwEۤi*cdJF.#ǽtHҙk1Т£'cĊ( AF".J1yPW +̃ iĢ!VȮ'^4!E0G*+zg1?`>}(gFs\LHd8V'T4qp̆fP1i.:%ƿixDEj t Ɔ$h$pE,hḅPׄjzg =Ϭud.l#S2V?yLrqFXjmGJϕeH&=Kg%Jp*k0E Q':onǤOኪwX4'[t6&Gi2ߴu΂ ZaG@"X.LT<4.`AFNQH/x$2D1@pw܎#2Īc/ V`ET/=ȜA G3(^&@ F%ِfebwd}04ܝsk}Ā*KP7t,.D~w1JɊI,M =#zȏ%`RUYX1gNSdAyV‘*H$}up UHPꆗ\>tӊ OtOP4a%bv}Z&CĚd\z1YH49],D]]/h$I3o4p',9O(/LQ sԦP& MyUJK;'즎,u@ZZ/D\?GSc ~HS(%ʮ+5ۅcqx%hr`b~3MDrȄL1%$0I΂&#kH.A ]BW܇&jai/_tgl۲y A1a1̤з؈ PP|He!2XeS#biDUŊLjG4 Ph-m|K;폘b بO&F=㫤_0*@ʞw 6nUp]W=F@R-Jsz˨%X drdhڑ:f4yKyKT1L'hyf7ԏɡ85$c<4ǚR SGKf<#-R;HݢQ Edl3eX @<' C:LBFpkY9%)u>&C O'f?'8kXc3AUljiǽrܼh dD⦌qaML H àOVTqJF|@PCi+ 뙰ywF] p/H]\<ݢб(<4\]?`M)j:^\t,.qbź;]|.YP/Xj܀&ƣnB@q?3jeKⵏw(VU{"h=eGRvJp,.3|<ק:grN/,< e=AGT9y|MBgE™L]U%]g++uk2oBf'h`>p{d0񠆚N8}dï2SCR.&G$Mf5#~1: pθzȬJ!APR: !%ue- Uҗ]bUUn @`DY)JkxdP;㩘,yKi_D UHD!ZqD! ℬWQwfV㚱M!S/d2r琺dT<BLȇU ((CxvTeMVglX* ԲV\U*TU_BOF%!03 ~$^ƳT2Z͘YWZbԺz.|2fU;9 'X־V!Rjx @&z+#8A}teڰ"A]gKcK\uj:˻ʾ*,6,5/4/!Qd;\EYӇ1d55,ZL$ä@a6X]3ɒ) a2̧YNufiv"=xeɉ6ȦɤhFZ> j?uHR.,+9EYE^^`m0\*k`dLxY.谥B]cX{mz:'& 'VZdwXG_OL`o#5>3V99VU#@kagj-f<',ˎoz-& OLj+$nd{"IK0|īARPxb56(IKg<{|nL,4Dn>f(]C2Bl_}aYw Ϧj6GjM/ЖqnɱX3-=VVK"RY\:c.C>=3sӄW.fBJ'EUWrZ"/'Qx>gpN2S:66I_G Wƃ}ݔ^ I6źԮnNU`RP4zf@90ܩz&d'(Qeڏ Y2r1& UgAXwl @kP8>Ax3Y+jlAse':"mnXmU" |+b-+4xp+00*n 4{h!:kRӼlQjl GF+ƑHAE MA~SM.;Z( w>cwbMgA}67l-qx$3FJ(/nd#U D* .6a$jE%C,{IƣqA0m [DJhnK6gh; BU!Μ^('P;MpJV4>/%YZFiÀg!4N P|l6%P.f>X7|rM![0ˮ."<)8Ob&˪Q1-vߋ;NKJVO=n'I,!m%4Vrl<čɵzR(SJ ,IC%I.٠Uw/k:@ˊOD ğe4=Nj_wZwʼnZnpȎF nw8940Ӂ r{[i>`,3d3ۨżvd{^KcPs}=g%٣\8:f6qJDu!qTXoaZ\wԸ_+-5dS4O/ WjVVE2⵷e+MhL;t7}DPح+G=_YοWY5F? Vc">nw.nY&jT#HA<|}qu?+~82|8)tIeM"!@RRCI<{1CN5F!"U4+p _CQ5Bb2rHTݍ5*q׺}@Uzm>T#(sULDPvֻ%_1}N!.$4]%UA%X &X޾f\j d6vR^-&\V^ עVV#^m 0"QXܟ4'& nX.rzXSȪH3+=JZa߅3tY#W?T_Z|l[_r^"`E. ŌwܤpS6 4!'yxaRQUUc4Y{lFa8e~/xCGh܉/fk-n_VQ2 482HiLSkSyޖ jM-E,El[ۋACҡkD=[p]cGK^!ʶAQD3b 37LCg4_ &"1ZѠ%jgK'2-'TpWڣE,rzXN_4"=y1osB3px"†whI$Z(ԲT4~g g.TY@\HNI%=V.ȧ,4uiS M˵:` $rZv$`{NI\& 4 cV/ ,U,9\Uw^ϣ\ptEucAbz3AŽ:\̕QbZu(iS",O3)eLȞyfc3DPV4a.+.tH( UUWaպ ;v.UaP0dņW& -uH^ c@ֻ"nk9K JP1+202tKS բKq'ۮaچUV %myRۤ5G<&Ҏt<R.}:(7cmXqdb2do4\#pFIœiǶ LA[_MN{A$wMGC磳h0|飾w/c- >IRmg{Fcڀ0C֨YG40 Kk,>dٸh<䷁@2F(_r $0/թ/l=PwxIŽ>=9#Mk/uoc}F?rr:{VXͿ_h$0,r;Ad e%sY )#tF5F"|XLQWB]&>}cs15$U nɠ.!WFUָYSkzW,LS5U3=,RoD?K uJDžW* 3x-ß,^ *$HuT6=]篘 IIw-ظ sᝣ2)b\,F^/[pn8lS8gK LFp sЬ 2AJZ L8X_ɩl>-_CTԵ42 }a͞_š6Q=,N'IW/g7 ۜ~l 7pxŶfd.Mj9RbfᝂEƚQ \MWrriFaT,$ p@^1cQrt36KyϻmU-PynMeG%GCl8y]^9JYYpUta4еax|;߫c372WGih4i ;Sg(?;3/GljuMٚG2%5УB0pE;u}ϱ ^c@pVENzYR񇚇F;jvo~q<j" ^X*6K:ɹJfc"Sv$&ഴ Б%~R^Yg ~W୮YFZu%+Sp-$sBwL["vZũQt_wO\3>}혔㧲qD?"ϱv?#h]7IuOAS?Q f-СT kN@ͧSj@ឿ24W>κ{dWބ#J> 78>Gఞ n_w-s *bRTEiT*u:Hn|D8d|vo$V-Lb~%#3 Hhpg jPCDGƽݎkvbeBW`_[/Bxw_U,Ų@; !$rنl`[,;@OU$sɞ9w=J2*E:s">08/"&S=#tFS"x*F$I#Om(0ڪ _g칍>I YV$+? 6 W w$$ v?[xu2om>(v%V k3@kߩIMH4<) @H0;C盬O"~Q -XcB#ޭVh5ĜwesYă&%ezIl mVhHr}K6LQ#>Qj?򊘷C#e20 Q[cYa0dj8X~hy!(P2>ʦ5J߶:Ѡb'<?0胄BhSfbA{ܭ)ir!!7g nek; ڨUmn[65$Ҧ8$I9㙡ܰV0 V''%m@f>M I6<9-F6=n vZU鱥~N{a>IIH3j  Cuib5&!>M^w|NjvZɞ8{J\57sXf c1n{>9x0#%O+|TIyL*GI}' &qL&V۝&'789}sz#8B k}<=P$ڞc>w%~yRxB4Ą9\睥y?$~yJÏ{I8JoI87t~H7W#KǾYi#FEYQMjP$_Lj@=5bOFtLz'*  )bOaKUJ%NG9qm*|?nyD̈́A {R` ˾uvtav־ HᅊbO(T .YF[9Dڞ.}|֞8`8$Ph?qm#R$³8Z`U} 2̓]Yw\Fаagiv*A`kSDlA*u!QTz'Fc֜x" >HE*(4 O5Jy`e#N sc등ulzԍ9K!s#ɭʈP=*ZJ{.1ӭ#ӑ(ӭĨLĒn\bg$To7 0&Gl突=0~<f<,pɉ>}trboW0hA =$dwPՉtlCU 3ضERR4@sP*t'vnvXI^È$ l HMiV^Ķd7eWt^>؄}@cGo6lHt[D̼[6Œ ='d۩!th$;G '% ( vfHݡ:6R?hIs鲏T L @@&."Ӓ5Bz! [],j\*ݯ˛w ԣ虴]Zo-X . HZ.7 X%{N\=۽ ea`y충-tʭ+'.[;"Mixuul]&[?61qjo=-8:Fʔ/K^_9}͍dmIjn5m6,3TƘ\S`obaм1?R֭11o你'Nǟ :,}يG2HB79S )[fV9d b hCJ'V//!4]h|ҫ?$H tG>dllk#h0CPm0[:la;6ξ*/Ha>g |Oi|h5>wJ~Ў)wKVΦ|"ݕH -PtŘmVO`3u/{:m|"E笾zشX";6.{33gn #]({jꘫlSOX!q1,5`Wh[Xm cbMBNo NCx1}N%ƒ-O,˞/ܔE}r 3 A^lܷtlK{ՎvKCW5 YO*N!['>ѵMQ19=WڏMD-I1zTNϵ [o''%N&O GY|V4$_87Gjgxeh=VGEDkXvOS16t@mk%[ i6oLǣvIKJ^WFj &}ʓf#G 8YƩ3( Dwsysڳ4wyR,Ps9h˷/+|J-g/-ԸFh9Vݹ@Z.T-<`Tô ҪgFIx+ /le4 TeG^yWa̍ue9u=̤'KzӪi^MӴN+k6h?&iNN3MXq)fQ9JvXB)^K &o s]\Fþg8pf5{D-_5Z''mx6Ik#?' M^N &5ԛ 6i>6C&??-xkEi3tx]xsȰpZ_ׂLH0DVHzs}6*OaL,ؘT=RXS⧮#PS2`BwhH+ӓ` SF#o#U6DgY>wƟ&iebD-u^"_YR>ؤ9Li#CuD4H^[Mf/-<oF:/Yj%XmD$0:nw#:+{mX#"j4F|"mp'@<ra7;$Vf3ƌl`Dl& +Z=ovڕ3JM<ԘY`Z+qz6U&i+Sμf$!X3ۭ|ϼ*ۦ7Nm,DfmKQr~a-L?l.SVt0(a_DX?Ė$s@/?vSIX㔵\ʟBe~XE}iID.nQ`J `*ޟleD(rε``*ڀ)뫱mFI4=ǯMjsN[Eaj@*D\?9 גϹۘZϰ5!9+Zjw{`!]cwA@|m3ߠ+v2Þl[ 7@0[|j+-"%{{}$̦'DDnmj-H1 '2J{-R8eڙ\OARuH9lTS WqD"FNa+5;h &[Nɓ OHtcB|ËqOmxBcݽ_X;>Z}'ImseOJq>u3N.7s;e-Dިʳ&N/LٸsڈHY?N[6}$ !uBȇ͕O8o|uqzOIcY0 BDׁ`Ć"ڳko[30=ԫ:)w|`(dlGA NTF-kCz*7zdYn`z VkaZ'gvkgʈUr^ath]jgtM11duK,}c,0=d(n7Idlh 7ÏƅQ՞Q0ύ3F̨5k-QmFè|3uQ337FQF7Q F ޜl J%QC![}'@{nc}~osB~\yOFB"?^Sg]\/xI7WxDeoΰMMs !2=mq't*+5!tI]'[:o7MxZr'U:EbozX [{5UmF[!˻f+/E{/"w|Ǭ|1i:>f=+$t I)_r$.vkYn2rijd ѸJg5쇪ḆӁTξZ}UNB2bgGTo gXn i0iè|tuܵG`/41LmRIsտKrR(%?}7ry:2/u" mqCSip+_Qɑvhj;$$=,EA"N@+CćxۯC?NV]kqPP2yy&jc4R6! ϩ.^بsٗ;lvG']g~ޘKOcw~xJۖtǍ‡T+LoY=GFթ\;/NSE~9-ZvSwW'N}ϩu=yw^g[6+T&(|WWbuXh4== i "T4!DnC*rrFY)83Eȵu&޺gz4@3HzTx&oDݠ k l7&Ke,Z=ZJ$I4kgJz;sHZ <5{F}>fzM3l¹SNc6a>Flmx'b 16ۭsT")Bc[m15zWZ: _ќ/6$olNl'ݯD6YLtR !b:qS+fǠNjC)WH_(WК.LĬr/ ԟ9 OOOH~(t# zP}G@0CУI'Es3tc<ێscuWMO}CSi'#Na4:jLTlk0;j$QsөlWTT /M~Eyk;կT>Ûяc8r$1(Oen:e#śq06e|Ȅ'l4eXNk;#'ӺgGl[o8]-= IƗVnwMғ,oz|ٕˇqpem<K'mPn6mn}H姿Y5{do+r O@e?zK|3G)1>^Qy8f`ϊ&N0*#uQʈ-3ŠoF~9fU{_Akv0wVE`O(KOcNm_[Ѳ\~pL b _f)s =~MQP()(c&63 '|`[QAVxw噷;iCv+tُ=wBܭF}N|(i0$S*aR׆ؠ% (}d aI g~A=l:n=2 ī^կv׭}t=xb6pNpVRRSUylKabUgUj ֌j7BRJ}&n}FngE1@њHr+vg#&H#" uJ%j>dfAaULOMMNVJ]<3c?X\'WYm"P߱Yv]~auGFR"hI{`pgKT66-־HNL֡,e3oL, -n5&\h=G%_r@Xoʸmx/UyM+KɦM`wFW3,1 dWoiǐ#S;= ^c쳵ǽ={7ycu-"J,KSmWa˾u0ǒ;3+Df@&aOzDZp+ֻimW )Z;R)%*rbmWUI#ŠS־:ޔH|jVKwz͒6%տq8~`MrW]„o)x2{|GXy#T0 !؆*P MY 2vKڳ9c0.R\Tn.T-w||_t1Y_WǀoBG)J"WZN'NyFi!\~k]l5f(*9ۺ[S, .'|lkU5m8KKU$kaWӤzvJʵ?P1|~B(((iZg~1仯;>#[mjc~Fjsj#~3]2ҬY6GM6y{edŽ Qvd^mb &Vm:K(t|x~s.I/{ꖼC_nx_o~:`|N[)7֞:K $zD٘|ʭ I2>Pc]nge0Sb#BR؁TiWzw|ɨ߰*5Gmn{xrqmGm F2Cfxj}# rnKͮb G,N^Z {V>KtϢ݇R_pt﷨.K6T#efr!4R`ng?Q5V䖩ӣ!FPZfQk\O>Bt.~zXr3/ۑC"a>]f(7mč`^q4Y6CoZ=q'$#b"@IT> 4XwD coytGGlj'Pw LWwo*z^۟N{ Sr#b͎S!*mix.3nm/`nBi# b;ژk!mUoġna:cm)GSޯ$[Vrf~]tF6*7bU.!BSf&40\!\m_m˽vpOhq ku*P6Rq[ܩgP<#w[}ְ=PwȪ-xr 0 #\,Ϟ۳#FaKe1$)RyP"}E0jxqCzF?q\,ΐs9 \p fh@:>k<M5^-TR!BC=>0e5,+0ʩFU;렏H[vn*l"[#w[6MZXj`࢈1an*<=RUm? VlV]e/rrx5x_1Tnثr*aY^X]r]~.rt2ʦ7u4hPJ;6|AVw|b^Z 񆂠@pkK{U<~۬B6M;l:(ZPR%<ۏP[Uml [֠Yzu3}_CAnIĮJsrC.b1̟e/2!<>I_ʊ?Ho:q)8 o g9o% /KxTuh5pV(;"8c)~蔻!Eytk١,iT5Il~]˗$qSg5rV83b^Tw@ލ?V#%綺}D11Ե?CyН2W-?4"xmuWخM/n݋Vq5r'[UoaƟ[;rpmc=z\ɨ==#T* 3}^` eM}fV-߃:6^ՐF ;f.3sc f:sIP&n}O6sn#ͦf2 s^.V =Ut'Mt}=\6;SRWI;)lUN /VaTFZj:m'K4 =/e_n!.´S'&,W-@o?)T宾<)Mt,ݓBP촲2} {_ %:N!un`;l{mBSiuʽRk O MyC}Ux:WN qk_Z)vTDRJ_v*[T]iY)JmI dU ";ږSH"L%oOtDBz$?Y"IX6AJ!Y% HUϯ߅]y0_J¥ L8dItfg\},fD\%זdqmKA8q# &B9A$G^"l(["$:t4Ƅ({ϭ$,ALEL=:f>haw0!\&epVd( eHd<(wѩti \QS3|68;x&`1r8o4xT/bpfJS,I$x2g&9[ z4l8CUb<$y LIqg6ybSRU4(i 3yـQ'ggm8J&BOYHXYxuԱbow2&  .T'HQ~ɣFt9Lra=9iO%G(~|~Da5(w2\b%'pZE<2 q7J ]C1FF}`U0xFE||,dLplX90F/l6,sd̈́,PXJHc %XPp$!fQLDGIJboJlL{3p5x1Sdor"fEĘI/9&8dHC!]]R]^) '4JEx&8D9:&1BZF! 14 J$ m–0Vc~iMLJ Z#awp&4==Oۡ "¦LI^!\eI WKКij.!.clgvGԉqBnM8m;UP+(oeZId51 *QQ4D"񦒿8 4BBޠ? ΣpFQ\B^2qGd1є2M WG{5Ѕ"~KcŨCJ`4&D˨8D@% B8:/Roϣl:yGB 6q ɚ|!BOX<4S(),Bho5c s-Ib[fvykЄd fp@P2^tFTa ͙N(+rI؉'CiDwH %,,`ؓޝφ3 \ϙdr1! $bA ', &I|+bgS-Rx/L-YEx&hgLup]&|N9$aqdd cY$@YX?Ի݃g4  .O !*1W@#nYO's`)5"ZEDWfDpjǗ, (侘'Ap LrDB,H"^$S9^MzmpHU@!Έz7Y@2tJ,Or M836Zcufg}tPH#j x7I)$y D^wX; D`˨"h/%{E,^ެ`Oɳ$Y+!p`*x ~MĀ\9 q7>]tCDjѐ  )ai/Z:澲2/BáBC]MP@#C-zdtx(1kI,@1ru'!4ٙ'cE0@PK$4M->["1.|KQFexb CՉ$NXBRIcu o NⰊĀj,zK՟AE5Bͨ5bh@H!)DTcahUT W '!U4 eB7{NX%V} IY #b|RaZDeͩQ V=Ҡ`"@F޿&Ċ. j߄e"w$14ܝ3kb@ %hy=:W&|<]RGKA~9yJH+SY%U8c&ѝTdAYIT+H_]e?bEbdm77%Gp@(yOiZ{tHp?y'`_YcqXѩ]`&DdqYWgbwNzQ`Èa(\ I̜dL>(R˄L@ D4~Gde0:cdB)N Q*$ĝvKG\:rŻ'fY".?™@?$qcb%1 0p2Kj7ȄÈ80!ǟG3"pnAƠQLFچO%D=\Y[kc}=˰b7W>2|[x1 h7f$FLrcOl ti.yN)UmE,aU+#B5CJ5Ɠq) m[::)XF}alN.&RǪ]*} m5,۸Qe u]LJ9T*c=z Z `~L;&R;bZHkKwDCt< ΫW;ov( fU?0 qvi 5Au;.xO/Zv±R V>@x*ZA1ͪ8qBGaza2WJG8^x‘YP.0HӍH6NGQq"AI!7Qx \ܔ1vCڄt0O}H*j5Խ~gj.h2=jw}PQ8#M;tq@v46gKR]Eo -&PX6ݔAU%NԊ 7YG.vX۫:2Ӿ8qɿs框2`DxՍ1`%@FCm\(}G<(]G*zDXHnWa=M[e&}_L__iwB}H0Uw]Qki`&⒎:U5 ћP,Ї,S dRiZF ޲GA IdMcN_o=7WUq$w)P1ՠ"bCHc]*v*BAؠ(+FU d&"6ov)n4@1QŎDKGIHT Iz6,f?5-J5 Q0{CUe"8` 4![>vZTEF7hj7: 0PUJbX&dAs<+PsuV!WU~.(-x)X![ka̐GDE4dJ"uPuy)BW ~h^~'kV olQJ͵Z#.ʨ4h>bb6j`i:(^h)WݦNei_ ?m{OF VV[y$hwjQߌsFsSK ;&/h!C I0,LHdhELi2LBxS5XN@[/8}*:B94sQ:HZ ,d5c3C ?a(eR(!7 `Bˊ P, !6" 4Q(k +r\MOI#4dڊN(Ii3'0|pt*H5W'l:fUc=7c98/n2WɂH84:cZAw=MFjJ!@ ?#\'#X]ߋwċ..*V0lūEcxb " 2)ko ׈FBX&skRA\U|-rHF꛱jgliXgS5#v`ljB񸔷dah.|5xհՕ?vQ,2\W!X\t){׹IaA\RV;98 Cm{*وk\F|ќ3t,Trm|s# G9 WƋ}ݔ^ivy s|]~jKU7S*v0C} = P toU"zqbrקÈ(l 23r!" UWXlpt8-=8("5+5hzDGS5QJRĴr_$_ Y즦sbBb `a^Uq+9Y1\E)FŢ[.4GJԊZVm4} OuSO4n ka!#cGƌCe25OX9acI*P_F,&\l(E%,{'8 @MM$r-0 711Mr\!Θ^x@-{\FU FnVAbE?<0k/iAj0ZT͢$,pvm, <)>t!`ʹ7w7B1Rao#㴲d~vql$hOڦQNc'SJܜ^wk$2xi@IȂ4T䂍a=YAJRqWX@Lل偹 {IV8X}Mz͢b64xYEl@8,kWu(+571}_=ͅSס*;68ؐ $u=(.u7'_O_kr OƧK?+wvc [V[lyq1VvU|yXz3:vL59,eITDʺͦQ&o+"]=q:ɥluG` 6WI+ 9sZ. HvHN[ ̔b7| tKv'R5@mMvǃkQ0WH}+ܤ9WĹDhd7Ǔ*s)V rrCJKKYim,7 }P]V_"^8)0ܜ49\ySwajK{ESRs-YcCbaL1O8Aެ!'<@\ᰐrrCE_rH])p 9u_ݽ/|wpƛ{R~ʣ`NpXHVp[ԻtUzReѥŀglh0y d]%jf> !_3VBx ɼ?3WlW-VHpQ:~|6) 4sʩ`vrJ~)L.'e? 0S2O|3VSք~W7@qۯk"5%ӌo{籨-_6}1Y(&;uϕON b RpCS}AnZoYPc O&1f_b&萂[x{Z9r*K]*4UM$̻r vmu'3K-p+ xV"Hސb E~tH|>IMD V\"/.3|px -['ynl(hx4 O5fջw]vй+NGHA~\ń~p=,6ji)d/ӧftQi oMj~w4Y:?>\Y|IثPo:Q{0.1ES7+Z'{)cT[#nzCU'P5Xr bx -ƞ`q[_PS*4S0Hqe;96^,s ,qU&4'q2`t CMgRSt@y(V-Z#-_zo|l}r u7 (m]fV}x퉣$@G4DxѶGFt+Dp[ƹ˩nͣ eZc-$ܩ!{_Ow^;M!nK̎ͦY)RIݭ}8TϫM׭4^.M|K{ďTq~xt|K踂Ǫ:_:5lSM%_{b|QY%@ݳ8VUrx9ov:9T ^:GTd}}ALַ[ e'tL‚uC'cSN˛^Op:bs@LG{i!I??)p4ΓI^_/#M;Dg4Pܛɠ:±e҈Tsƒ˒p'aV_ y 2MTpǒN>X_ۇ5ͬvñe?u8.Ͱ6ѝ-"U !zW⊫դ7jYtkO`8ALU˟-ow>8i,ppuDUwߓ}s|MP9ܿ_Iχi8Ivw]ZuG+8_[?ON ;6gu]~RhVfDx]t"[sVd_bpT&ЭM}۩Qvz."F W0a3)Ri>Lea!8zQQܔ-W/Cbv9S*I`Ȕ_'ޞ Lhwn@p,9Ćוϰ @8+_d$|iRgZN㾥5 hy&DՄ@<Umus7D'7ESN>bO-ۑ(D'm,_kk!}dsԏ5#Z!3ez+X JE6ӖH]VqjebZ^?/l))"8VO1OEUnL=DcF {n8w+7o5)f~:ZYBG"N ܵ]!p=9!P/ʞ)z'Cwz@N}ɋQ&ΠPӈhxk!j귺u iR➖-@YG;"޺g֟h!7 T5 ,WQ_O(EWC Zܮj^3[axnòLϜs/V甯Vpq|eߔD<a+wkBѪK;2my)_fbZ_YG1O@-4B(_9Z[_KVW&Pv1y!0 XS&G!߄w@#^@ncQ;+_/DS97G g%^;+fg̾W*޼.xD@Ky[ 3<-S jJJ8[D󖫂z~eTP [`"z\ZDX i4W*с_dvOvm߮KnrK<('A+DZĢvyJ!W%s._@){NEͩTg42Yڮvc'Ӥ1p!wgHB$%℻y^ WWƹ*T) tg\d$}KHu#~tLRs[ވ6*s\˦doNzvY;ӁT:!@\u ̲K@JĔT m:kvYƜXOe* vdUZwJhmg )Τuhn?6tSZD?Z,,tc3I$Q-OnU6XeVR̞zܤs1.JvъDI2d-qϩ Dժ˾[P]ߤJ@D'o"-+=ԁLzۢnqaVtzVP{q"mB|^4oX!ȢGBqqhwi |bۦa!<-ΙΚHj{uآm([, Rlu=aA{Pq1>%?ӥUQB=[FHT_%@ItgMĠwFS"8*F$AOm(0ڪ _g칍>I YV$+? 6 W 7$ v?[xu2om>(v%V j3@kߩIMH4<) @H0;C盬O"~Q -XcB#ޭVh^ݪosA)D)YBۢ;mzf3Z*R\_ SԈOnu?iԿϦ"Hٮ 6= Av9 X-aVfk Y;?Nm('6 g$%m"1ZFhOܬzcۈi$7zz`U}EojG*uWQ?4lDڳfe![о`J]lT?$^lXğ5"nO,2}|ѫ&ā)J: SRv}jadlu#.eRlHyr2"3T>(77oD ߤn} Fe"p Z;#yBt|)O1?btdOF$ a`k@:oJl4"%)-Vx&L?o5?aC"ħրJ%bW޲T,T9!N5wCsg |Oi|h5>wJ~Ў)wKVΦ|"ݕH -PtŘmVO`3u/{:m|"E笾zشX";6.{33gn #]({jꘫlSOX!q1,5`Wh[Xm cbMBNo NCx1}N%ƒ-O,˞/ܔE}r 3 A^lܷtlK{ՎvKCW5 YO*N!['>ѵMQ19=WڏMD-I1zTNϵ [o''%N&O GY|V4$_87Gjgxeh=VGEDkXvOS16t@mk%[ i6oLǣvIKJ^WFj &}ʓf#G 8YƩ3( Dwsysڳ4wyR,Ps9h˷/+|J-g/-ԸFh9Vݹ@Z.T-<`Tô ҪgFIx+ /le4 TeG^yWa̍ue9u=̤'KzӪi^MӴN+k6h?&iNN3MXq)fQ9JvXB)^K &o s]\Fþg8pf5{D-_5Z''mx6Ik#?' M^N &5ԛ 6i>6C&??-xkEi3tx]xsȰpZ_ׂLH0DVHzs}6*OaL,ؘT=RXS⧮#PS2`BwhH+ӓ3` SF#o#U6DgY>wƟ&iebD-u^"_YR>ؤ9Li#CuD4H^[Mf/-<oF:/Yj%XmD$0:nw#:+{mX#"j4F|"mp'@<ra7;$Vf3ƌl`Dl& +Z=ovڕ3JM<ԘY`Z+qz6U&i+Sμf$!X3ۭ|ϼ*ۦ7Nm,DfmKQr~a-L?l.SVt0(a_DX?Ė$s@/?vSIX㔵\ʟBe~XE}iID.nQ`J `*ޟleD(rε``*Ӌ:2Aӻ 7vx{Bsޖ[sTvټ3XgjN `֠*O\xF^[.G2kJ_6 Ԗ?YE|"lS"Wc:<_iz_"Dx蹋ҹÒ"9U.;s%s16m,akCUsVYsCh:郀*t;PfAW<d=)K}n`,'$)VZDJ IMOHӳZ b@:=Oec< [/p˴3r ꐄsD%ٶ(}A(ΉDiVj;gA}wL@96'!< DŽYm>ǻ{lv7| ۵TO`˞}g\nw>[fؽQgM_q5/~ ~dlI C;'s+q:?=<Ÿ>Dz&a$vi74ni #>EgO9Pgp caY{W7uS4:$PFr>.t/7jߩZʓ9*׆p:gUn:"@$ >R*#NHnS){+#Yi4jj7rþК{-?ccX2X)rYaznQL=ܰoforo R7=vaTg4΍ƙQykF54[F#20j}Qf4Ff4*Fgo35o4<\5-9JpC Oѣݓ&'8,˟4E(κUR_ޓop3awx Bd>S{8 O.~1U RWdkC(PMkOG.tn N$Q/*u7Ëb\ j(CnqxDߞhxuokʓ]VOmij︼w;]B-fdy޼͛ʔiWJ(؎r!\Mf;3eV˦#l-F7:)T,_-| B8wV^AoeMw_ݽEdYbnt|RznW%In#A5RL#I%\ײ e`{qH#jNUcY=ם}l%e>v폲3ΰ@<&daҰŃQdk_,hbHH)(ɏP=)~yG/= KH7oud_E@.ӄ'J]!Wv *Ǔ#GВՔwHH{>:Yt+S]Qz/wZ*OS1oח'ǨAǷ- sVə߀iz`NS);Sv*_'1fs[NT=;HNS4zN90Q϶mFWM>Q=/ 2ֵi*;{z;AiDh(B2.9@IӇLU9>R=qf$uE9'kuLuVtigIL6~-A (nM&HYzp- xIi%z+=wx[k^|*>|gY'!5ޅs돍'@u+l|UFOĠb l;[.\/DS! SHubj:;:t>( 7Ju9_ m%N?Ia$HN_mx{?\!- rBdu>V͎AԆtSP8U35 %]ZÉY_:N}ש?sO()=/4`Q(_ F Ω8`1MGNf,;xQD}7/NF>ԉhHu6ٙVp` wHS4+7JU^N=7g<Fda>|iC YU|/d얜gs`.Nqu \:u]dZ~8xAc>߄R4D8>Oh iBzjPf#TN/su˧rY>]ROR}O,R+ ֪jq$DI.®Ip+kb ܅QQQ(\(OGQUcw_w }H۫7DGbJKv s=G3geYo:GmL*8m2()Ë{3gߗ{2prMuPi\`^։-y>w5m= \ߺEG3u*J->ZSo(=3 uIa{Iܳ18[d*!7} r9Δ`xF5eTӮ~ҫ!QaUjG.ێ,me&GA& >.N]{rY# " r3A.|?#E'R Rs?Ճ7NoQ]m:G~3ʄBRh.yM~ԣj:-S;GC8݉1յ̂}*,\312̱f:uC_#D¼}ts]] \Āc?^fe>Cy|*LtH:U|SNqdr7J9^>Яө&kn£PvDppR)wB B&#E>rײC'X")ʩTk}=\_6/Izjȭ~qg>6έFKmu}/bbkc;e[~h6 wET] ^"(һ/#j5N:&?jw>U",/ƈ{v$GQ{8J{FhXU)V7gV%L^2t˚í[7a;tlr2!Ov'\f椯|H5t$¿:M" \.~mFʛMd3(]-ztթO4&zlv8s˧wRت$m'M _ ¨ՎuN i @R-B{R#_ qu@3>!hB,\iXO MjW[maY[*>>~RF']}yR8՛X'Iie7e!,PKu|' C^vT;%۬˅vS{lH_٫3xu2Wl־&_W㵆/jS;="˷u/TtkSUҲ˷S*}dےZ;̵jɪE Nwj-?Jߠ`!H~D' kX 'eC'DK:A,$_)& : G1`eK7p88 θ8Y̆JU-&a oҍ㄃<M$qoIt5ƽDPDEf07 lpvL$RHNhF;=Lp(stMDcԅАյ1B#b Si@5|IHűZۄ-a&C1"Ӛ2F" F0Lhzz70C DM&BԹ 9`"H5g]B\ 5!.ㄊp ڨw$诃VQ-xɔ)kc,BU\iP1DM%)pXiF0'A@G81Dd⎦bF)d""X4Pk EF#fwQhL wQqJ/T̓K-⍧ ^qt^ҥ ߄/G٘u<6H#A4Fm@5BxhP=9LP!FSY<|]'ߢk*!#D0̂5V15! U@,Mʁd茨&a39iQP9&&V8&a咰O(P_{AKYP}{Y'; !g3<n)bB6I|'ł2jO67YLW,')#Z<_8(Zy1sd9Oky"P`-4ƹ! SJJ!&MOvሀfD6հ9QQݧH1~DUjX&}Skj4xzAglehF4̈ QLl1$}gIVBT$ LsD=!%n4|o'pI w!Aq`#<dS$h}U_t}ee_j\3C%>uSk 1!TY< !F ZNQ(XPb֒X3b8N(BCh3O6NJ`(Hf"Z},YEcV%\]"1i2~AŦ7\ūIJѓ2n &($ J>=7Г<@G`´S1H1zAA#{ D2M]dA6վ D@I\bi;g81 ŀ*Jz uL.x*X$k(r.|ǕlW.X3Kba5_kaq,xǘM;,0Vđ*H$yĊ۪o&o(uKQ0ӌ =d@OI ⰢSe>M~dɡɚ'$ⲮzIP89=|DQr @0@ifA;**"`t %%)JbX=RR2LIAfEeUH ;'얎,uwOZZE\?~ ~H(J+4c:9ad&Ho !^?7q`Cy? jgEH/ %%QLFچO%D=\Y[kc}=˰b7W>2|[x1 h7f$FLrcOl ti.yN)UmE,aU+#B5CJ5Ɠq) m[::)XF}alN.&RǪ]*} m5,۸Qe u]LJ9T*c=z Z `~L;&R;bZHkKwDCt< ΫW;ov( fU?0 qvi 5Au;.xO/Zv±R V>@x*ZA1ͪ8qBGaza2WJG8^x‘YP.0HӍH6NGQq"AI!7Qx \ܔ1vCڄt0O}H*j5Խ~gj.h2=jw}PQ8#M;tq@v46gKR]Eo -&PX6ݔAU%NԊ 7YG.vX۫:2Ӿ8qɿs框2`DxՍ1`%@FCm\(}G<(]G*zDXHnWa=M[e&}_L__iwB}H0Uw]Qki`&⒎:U5 ћP,Ї,S dRiZF ޲GA IdMcN_o=7WUq$w)P1ՠ"bCHc]*v*BAؠ(+FU d&"6ov)n4@1QŎDKGIHT Iz6,f?5-J5 Q0{CUe"8` 4![>vZTEF7hj7: 0PUJbX&dAs<+PsuV!WU~.(-x)X![ka̐GDE4dJ"uPuy)BW ~h^~'kV olQJ͵Z#.ʨ4h>bb6j`i:(^h)WݦNei_ ?m{OF VV[y$hwjQߌsFsSK ;&/h!C I0,LHdhELi2LBxS5XN@[/8}*:B94sQ:HZ ,d5c3C ?a(eR(!7 `Bˊ P, !6" 4Q(k +r\MOI#4dڊN(Ii3'0|pt*H5W'l:fUc=7c98/n2WɂH84:cZAw=MFjJ!@ ?#\'#X]ߋwċ..*V0lūEcxb " 2)ko ׈FBX&skRA\U|-rHF꛱jgliXgS5#v`ljB񸔷dah.|5xհՕ?vQ,2\W!X\t){׹IaA\RV;98 Cm{*وk\F|ќ3t,Trm|s# G9 WƋ}ݔ^ivy s|]~jKU7S*v0C} = P toU"zqbrקÈ(l 23r!" UWXlpt8-=8("5+5hzDGS5QJRĴr_$_ Y즦sbBb `a^Uq+9Y1\E)FŢ[.4GJԊZVm4} OuSO4n ka!#cGƌCe25OX9acI*P_F,&\l(E%,{'8 @MM$r-0 711Mr\!Θ^x@-{\FU FnVAbE?<0k/iAj0ZT͢$,pvm, <)>t!`ʹ7w7B1Rao#㴲d~vql$hOڦQNc'SJܜ^wk$2xi@IȂ4T䂍a=YAJRqWX@Lل偹 {IV8X}Mz͢b64xYEl@8,kWu(+571}_=ͅSס*;68ؐ $u=(.u7'_O_kr OƧK?+wvc [V[lyq1VvU|yXz3:vL59,eITDʺͦQ&o+"]=q:ɥluG` 6WI+ 9sZ. HvHN[ ̔b7| tKBL(D"0&]M,+8 $$Q%ٮf5ɗlWn}{Vf5N5!<`SV}+G t4yh6%[ܓ}.OU,jl2f;x /RօqN LZKcXŊ )>gYт>O"_\|aI}ٽMs׍7w KLf=m; i8(8ٔOLJaO];M#Y.Nya 쎧kuH?2wt6?Dxg$JvsìN.TWKbwEoLqSr/g@ꦝ lU0srq؄OhRиmtVvn=Ld۩G׺|:/nъrA6cyU,WW7i"|IF(u+L 8\@J'ur*-0NV4UV4[9X5)=vғ<&O 0NV>KjsgWVQ?mZkw'jtdn/[bh|Rn4q q ziTw7;,8 6 Ӓ(E*E)8hǡb [ vˎ9oehZbE$E 5fV?)u \,<4sz*I2V>6)EN'ˊɸĕBc^ MRFT L=aე Iyy=VW;wc=Ar֊lejBYn$+,SG{H:0|0DDR/U ;ptHh+yKMbPUIxYSx?܊2G9XKtg1=0ʛi_h eW rкpPeeau9Iyev3|>lb^5 :]飚*3D Yyj._<ip+EU8nWdNbnh֗-K[i*ϏRB'GFo.N[F4L*h:*i16&0X(;xWL@_Bd\CLSe. )О]ZH/,@*g-C{& ry ٠D~Ͱ?#)sg8c| (Uq‰FUL_en+5p[9R+ ,,0yρtw&FlCgvڌ4Dy'GVegLlW =ڛ_y[:3C/t[,_-HVZNp|^= oYm4}|riOHYx#뛊xe4:WyKEUZ߲4w@qu{"䞂 z !p_?0I.KynY6 E]9Fk`D9kL=-ye#[͹*@ȇbJK/nU%&^O|1#mZizGzt^Jez=OpmlsWdnA@ R*0Gp?.S6 k7ǵB@"><z5Sܒd;=gdQ 0u|{ :gcwKQs7pq庾,c꺊+o*t[[v~VzwVH]Q%g>,E̪Eb_5zOHE5vFmMihUz *qo)YF=<tn f(kGuu3>tw!4[ FCDZ,=(zŸ)VQ@VW=!t}$jC`#+ :%\ .KSrG}XEd(pV"&2ƞQ)4NuMmkB TCс/6 뻳j c|2K$zA^р$ĕNAJ&W"Rf*X$-zW,1SWXxmk|ezV`ʎ\w=9Y*k3ɚ~ʽd$nE9V/Άc-|Q_(!QUC9il3o'c\Kk%cWb<vVz Kz٭6N3R LrpKhspn+\O>cZȳ=Θ ;ΰ0G a>=I\S+G7o*6cx3Im7}uW 9˪,hZ+AA>Wp2;?x|Os۷sa`q.D7WiV mB!rjjpL5 Ӝ9hf2Ð3q{TH.f1N NѸ՟ao^j&.- N/+H5:;?r\1ōRCmQ&eU-vJ1p)lŢ;&1=_X㿪a6.{LYjRuy79Ec:SFS:wd[;W[J\{C%ne}$Ɗ|)e˙ %^п\LDr^]ļW"Ul8*mЪܜJWr9WypV&`j 댞Liβ>^+XT,9A9=KU( ˯ QBAqX9yY!rڠ掺|CN^&W~zpG-_ļw@g"r5u ΌMC f[VfW-[^{r-1f,SƆ;6}Ύ vN 6K' ݻa Y9`^eY!-N* [ d㛑sc755'A#F\Iϔ{FkPvkjeVPe{46 IB{=5/3(aok皮 ²=e{U2ՏB2kDt/Qa_\pri,A}z}r+cG.py왻z!KJxu==[nz|zTE >tk%z~#r|jۮ*q-o |U}]_~lI!uc]:t/ZGznm9eKBxluՎzKߣؾ8G[ڑ[u^:j^׻tkD܎󮕍*kSԩVmvonu[}N הp(q{՚Mp h~z;~kF X܅8>>XÛMP|m;uSNӛ>GNdpȁCz4-vx><Dž4Wソ[vDma@vӏL1N3^7B4Gt?,K[}N6e %C ^Fذb(u^kխ[FS;ZASb\leم݈W.[^WqJwߡ8J4L?2T >[^9p;ʍ}}~%D%V)駻/`|+24qg}gX9/ yiYGErj+o~ᆭ9ޥG#lh8]KP%0{ժo}&:jpU4fe;DG"&Eqb x\؈''[K~*:xNhe#L$2SVׂm`WȦm2>%UZ@LU=߂unO&Gu: 1oEUcƥL* 0sg}jlh7n4략( %?Q v- .D}X]K ?ojg(/W~y\hȒ&N_nno>κ& -FFI†"a}(q#c&FI*> f`DۈhhVR^Je#1jTӛ{›-2sk䌊l;Q^MDMS@PAV MsQ RE(KIZ!~:2n} o3O/Ӎ, BrE 9:E!Q\EwTR~OP3B}83E8C*yEMz9.n7]p# Otn攆(%S;2mn@ЎRK}4 <d/:G IG-HkuEH.(t ;y0`JHNXv;0$<PE#dVZv_#k(-jyQ _N;J_b+T>Kȭo%S67*#fuK +x ǡMP&nB!Ev*7!U[͈CcϾ~{M4o.& %xa]]*,IlH,zU>U$sS_۵zln\Puoy}S*'JS_S nuH!W5s.I_C)ۥ"Hp3S{:QxZ4S6=M?J Ax`W25X $$ !'2 ˨+(A~C7R^%*T& tg3dr7 Ym]9sH9ޅ1>ӛ+ `6{0"pMCĨ}HޅӇT:};_ WןYvHR74JLLeh~8;Ykǀ0 b5x*˄W)8ؗVaݸGhgĝU@+Rlj+k?~ t:LI]>"[z BmXrUR N[4;c} kaD>$C&i&]WnOo-:61D'*nD#Po#X}y%Aɸ*:'PZ? [7Az-ZfC>;=oݱ$#BEŲM]ՁjAnpgY c&1Z|³8oIm|dzY![[TME&;!JH W4"‚XH8(4q0>іk[ X|ZJilLSxi_»`eVoLzB,x+Ptjb:fGfX[o넱dېiEpKp0iA Y*պYd$M8JT 7KtGM v%VZ3h 3w*"T@:-H4<( @D0:c\f'R?Q]LsswnB݈@e&M5! KB|rVfVT9V?eԿϦ|ƨHٮ#A盛_wܳZc=Yq8fnP,Jz4[KD(}eAehPc@wC0B6MsJL,glnLK߅`ߝ#5ګא1 67O(Uӏn>,T*BSFOP>\ʏ uƆ-Cr8U:>.h `e¾5iJNOS~9eVv|-<Kl{ -*8ZʨU鵭\&&'4nttRBϲĵqB)9E46T$g\)ul;w=P{~ `sg } R{F!2e!ŘDQ+ݼ,)DXF"!#[CII[` _O\bۈ,̐ 8g Ӕj`U![i\-ĤRdqXKÆGpMT 760L؂;ULƧ\}Vc 1?kE=& I9g!Yoe aC Ƙ@*<(Xc_c*c4_h|^WItQ~ GޯBh64C\niJWl.6pK["eC Ha:-Fc4K^NϷ4}S竗ڽfΦ$ݑ H%P]uŘZbyq{f|~.tUhW[E4KI}5iEEmwe,1b5.wuJc*Le%p5=9U|斔}]~%A +Dgr Q.|vK#Y3۲1NKCW5XO fb(^¬ϜޫOǍ((QKR^z\%;>n ƚAMPƖe!P:$ %tYpkE+*rW4'x Ȱؚm1LH0DvDz"SY93΀g-VCAbv/-ڽ7{j3ܜ Ҥ^{Na;ev.n 4ᢽcb8|gam߹3\s%XMb{:.<m:/҆UQAնg 8Q3Joh[XW7w8lf"HvyDUy"F_K/+(#>\Sm_Ne+Ry¶S|xކ[?jD5Ȇ9C^ʺ'N( {">l|.i/ L˚lڡ)bѸ$u<ў}6Z<@>ZuW':E> Y([.9=%B-BAéE[dʵ1z}Dޮ3h`Q ÍgXj'dgvR.{+2o+RC4}ק9}cgX4XS䖳 !< zlþkL[z11Fcl4 n4ΌJݨF̨o4NƉQymF52MQmшFèѨQ Qyf[ &0'ۣR ۨ~67=CI1ڱt4v+D߄]y'#! YW|#W*߱$cq嵍W:?hK6fִq;xb<@j\dkC(P3-ko6Dt* Kmɍ $Q+*u^7ca7:IתMnWmF]K.gu@U:~k 7) zKP#~0#̋֝_oU.vU48{D Q`']c$Zf50a;n@8x S%m&V/=`azcZԾ7}ު7=ߴ̓E]X63Eȵu"uORti'ID~- (nLWMcJvIiN%z;;w%i'צߊ|*>|gY'!5`sSNl|]>(m 7zIcW'q8=>þiZHYN_╽YH:- rBdu-} .TQZ.\ZنҩpϝSԟk(>U(>3z<%_iȲQ0Sq.BG>x3BϭX0wvx}磣R OoFF1QD[}cȁzϖYP)E7S)6 xc4J)ưD)c,Yy, C0dxr3 j{Fx}>O2č§կ}:h0PU~ TjE&"uLTP*?(G-;FT3U3"Rj]j~%ReϨ{ ,Gao{kt PʵԲ &HDʖ*){U+dzj:]Փ[iP*/!u ub}u]*eGX*qgkd$%Tј)Z(wM}DEhc2h틤4ug)Y}ebگr@fqVG'4(xP9NF1 e- zW )m K]=^ )'[6 ނ]̼V[ 0b@HHHwWmY!G2Qzb <4B3gk[w tWC'feFda>|mCXUC6f?m9k6爏pu HqqR:[J5F[@|d}Sh*\iq}>: MSyz\hf(*g9ۺ[S, .NR}O,R- ֪jq*$BNƪI)v$@m )8P8P8^hyW(zQQ}7_7 m7D/n]#ȷ!ͩZ_HD蹼gQ:G{E6yI/)>[vX$[9";[ }{5ގԨnC@t:J-?X޽fj,)/TzgcRnUHK5eA 0S|b!BBx5%Tz|ΫndoXEI8ƶ. f{BVԳ>ȴDGR/ykp\,(>,퇉-ᅥr[}Yrq,:(e_ GwqAʲ䫬ʴN)n4 I-~nBSZ(b S#w$յG=p=}UXp0ǒ =|Y޶uj}\ՖK}A%/4ظ̀%P7E|ymzXcT~I$WX#c`4m[fw@ }LE .;1{=ѷo՝C*m&+Ph+wq @l}R.O,oVߺ^܈wVߩx-J-۱_!RWt oo^|eummޱܶzqYwuX?/ѣKޭ[Vo͙uvٷ[n~XzEJbU!AC9u WWvZiW[=.-̞ %#Ghץȝzţ2ru8B/QWN&ߡCVEoєz`lssԼZB.q&3$)}R0f(Ɋy@Q-8*ohc4o[e=!0`3@NÂ~!P34 z!E[9Z׈E -TV!BC9>*#6kX(!V`S렏[U@b6/LwGDdB]l"T4_O/.CsCकcA mݕ*_M. / 2?Fv*U&u,ׅ)U; g c,xCe9]:=(}Jq`+>U(ZmΤ (#\R;*[}1iϖ9*E Jʸw`X8w׭u#yDI?Xfʊ?n:~ @єao% o㽧ө~ աMl£r3\DZ?rAHhAHl1R [jvQNzYM`;Pz@l_Zo6Q̑[g>mzs3+{V驭nx(&Qhϐt'wͦ ^o`N%jy6A8&@2#l*kR}:E ^ǀ~ "|"t2jpgU%b~sbUAc6T0o `oGl|ÖwxXziPS#9+옵O>?f0#%YHZ 4q0 }x綍[-d3<(]Mz O4hZl9qۧЯw\hV EVaTFXju*X΄DNg~f|BкXN-W[5,W?>~\&J]}q\EtLBPW7e!{_ %:N!u6}kcԉZj[uns[rԺtТ+jdgxl͉}՝Q5\QA>LI$E^o E`-Uޗ Uu$vTGkU%)85ߩ՚N!?8Jhjg!HDT|-p_ )X#t8S d)+ei:ɛ"A"!Bi6'tzU 'i<_q섋Wqz=ӕ[4b>J|R0WAnt"jVrH&q*l(YJ/d{6q}"IɈzƔ[@KM<ϣa4Q:j5{LSL$B?곈N\aRW_"(w0Mǧ#!$h$w$bOn18J27Q H=9cH&  EBF4}gLF7 r\a)/3`"ń.d:,Sy$8es7{ F| ٘35!MjL}9FrqzB9eq+DWMRM?=RHOiD)Dp(.* "ʑ$ &7R80WO#BKׄT&lo2>pyG}q@s⒩şVz~Fw`ZI)1 *QQ̩D*)pXiD0h8".h(CB%/ %hB܋2&DlR|n`NШ<:G {@"rlg7Wpc4&DvTAr%Gx9@=OK\iMxX'wHa34Dc&qI DaPb9 P!FC%L!"39aQPg3W8&nҨ?Q8ݦƉARq,}!gwdr1! $T;MFV:4_%40{Pˤ !SSm7 S{jPD*ł([#3&#Spv5Id͹1Z|4I8bgQbϡ黷G2џN k^[zWs̥rpDWFDpjϯY@P W {E'&R-^C1!xBLZ5ۤ*膽rFf.׽ Pbz&~/EB M936̓Tr4Bj-wz"IaQ?>K<\O6N4A0FRW="QV L}ƗQ̍fyZJke47>.Y0d)4zqJlJ܄K*NtM^paCdVc8|;3O᝙Ɗk۹sD~Q j =ˤu$./,#'3VG?xLtBÌ2H/ۈONR GN2$yD1k%9 $ T$a SD=!%GA2~zOᚊ$cH4Zx6&Ni-pg}KkFhnjP!&$*YN!FZNQH/XsW%pghm׍Pd{nMAQ*Jă`5$e*B*iNOH `sK<2*^H%,/U4]$V=!D*H Ϣ(L g ]J^ϱ|<`ZͳP^tD#$7T[Υ}=)(IUXev\=] yX͢eFt5tIlQxb|BniN Dh`2Q:VlY/xEٻh~cn7WUlJ*DSbA EĂǺ0j-MU1v* &BAؠ(+FEGn`:7[7{ͅ@1QŊDKGIH ~?H%cCR-CT/$3RJ!eɬ2xV02>vV?)rkPOAr:aġİ`yMȂxVn5KBKPZJ9"d+X#{sa̐GDy`J"ul(l̺<ճ#.dΪzVve-5bRcgH|aBPE-{-_uN41 ZYEwުkᴲXEK|eH(vurz~ͺyW?s(?szsQK _h5O%,=~NRHj4䚋ɘ")` &㫙BxS5X΀@[/8}.:B9Q4 Q:HZ,dVs*fF52%MҊXV r%=LH{,9iۥ0jƻq[IXKȔ5'.ܦ ^42mEv udK+'0m$7Ñk|\kVjW0_anno e ^i`FǴc‹^k讇HM9DhOLj X>ANXW3^M#weLk60(H =ޮD_#v ]&B7y[#%! ZmFZ덱aYMlQGlM/ҒqoYA5l[/] kP]/- BDz+ػNM Cڿ/\-d(e#YamB4wf"Qjqy0.@cjk[M<X03AV].OaZ/KOz.ԩ]L<G1C=$F{E߄q|Fe6J~y!" Ihኃbマz_se8Mt4+~%@MnKP)bZXyn YdsbBb `aJZr/2M3cD񞣉\צǫFŢ.62ZY2#5|+uhoDFn2Ê3^[Ԙ,|z&ͩ<K2a-mĴ*` lłqʢwO'kqd0- [[HFh 6T'{ΐF J|-UCt1{lL:B.ٛe*XhvagI3;{ K 5PA6,-(j2I`fu4,C,>M&DDJ:+zW|:+3ƣ%@bӢW I2 zX.o7ke Rj) #dz< JR :  G1,u0=N{Z 8bk\H0fc_1+^޲ѐe_B-ډ;{ ~t(+U7> EhbSסلƯEr1v&7?/`e4r]uF*t%?ԸUVX "|1'U#Bϊŝ1S-^V[ִdy: =HB&r#_YֱgfFHa)kLŧ,׍dl*n !"齇X8κe36m$&J0W*'I).jizm$b;$`%ifJYus7Ć&% \ x(,TًMq,$!"/r?6wj?a]6ڈN5`F1./\w0G*  $PKh8`%oƶn3º+CenL<b8E _b:]`zb4M9uR vQyG4p].lRK F>%Z./Z  nʮ@IE!,Of; o9*ͅPVQB+ChyIY* ec%`0Q|k5KU[(&OV 3|. *IrqI}b[^3ʙ5k4aksd@aI*1>imMܰVԶiUmIæ- J^\Z[gWx\<)juNLB2dlGz[Ws}דmaVy^V jk4Q;Q[xd^kfaA+Õ[9C/[r$},56`rKXHXK {F5މK3)@EQg;!dպ")c ̔\j$qp& -9e 6OQKkNHTk48-ondZmI|Рvn3E N, LH#tJm%9B+V&.w!^͟hwuv*<PT']i啮5O**u {Ks|k.2`SB 9.b+m\E:j e~5\3v)x>_K+]L:nס^r,^l+8٣ v=7ۡ:N.>zWo5촎\FAS]Ƕ_;Ͼc%3"_Ay {RHݭ}L t/2ǯ֖N~5)ǸV;-O;_>T'aw/EmK]+H9a~wNwU%@gn[}W~t`سjۀiE\܇npκvT .G'5ofu=tǖ-{iU- ;hJvw#pQWSy?kn=X 'h4><(~d|sw2?8^ *kI~@&acm0{ժo}&:jpU4fe;eHQp\ug14?xrolDgS?Z }YdIef>z&@zœQfnQ3rG8*b@0Nsv 9ޅ1>ӛ+ \˦`oFNzv _u¯9w"̲C$|Qb8`*WpԩZ>$1g=,kSY&8O p6gĝU| 3)Xh=D?X,$4^$H l)c?֮\=3^O[4;c} kaD>$C& quxk)~Wֹ8x!:Qup-'m4 4#9nqfVߴ&{r0j/O!-y%I1r, }(4/mv V 'EV^ԾJhw myƽΧUȡtƖQ)N? "xNԎhנ\.<9ku١܈UN!I V$+? W TU8_X݅o 2aW/p h1@kߩSg$Hp " s.3WЂ &r#;ZHetV} v [1 KSDs+czf+Y*R SԈOnu2_gS>p̞+ȶ-h'$ă9 XaVFf{Yۥ{6ҟM/8cJ:D_tAP6T'X'P]F$MӜ [;7S*"w!!w'Wk5 67O(Uӏ8\m}Y6U$vON%G;W.c#CaЭ(c`2a_ؚ 4%'Ч)2+[ubwв|3po۩ըZ^eayBFL'%,K\'$8^S}Nc> OjLB)}"^w|ǓvF̊d5Xifv z,muwrMJr>N+ôF/QZNi0I)Ro_x>l :ޏteqtE9cۨ_SDž'D'&:,K[!)!?E.ޑ3W"JKǾdIiE*+&^T߽_Wʓ&~cOؓw{?H!B c\L~ԄPT ]= LtDSPou(G=?`BɄA R `Ӿ}vtfv=#Hჲbo(T n^YFM"m,KʐOlo!ጤ-"EpMU_l">}9KTS>':2vN j &z ڨ_6?l"mXv9a/!0XRg2>CfIYs.y*b#VUoMQҩj[# ;.,'lԌp1  aeDbC5Qjpob1D7LGoQKQ%VNHP;n`Lď؆NUCc駡~7f,pfeVvĐig݆ԞGu"]6Pg10u,z$pB,X+#)QQ'E6_>$ l H'8W$ߋX,슎=Q>s0}vl۰!EzSkD_1>+/Z*K*m;Gaا1H.vzNr9!gQՐc ZF-=Y,\֑*3~B7o76lS IM#~Gtmq q%rKM3P~f $H/ Ej ;R,N}ˬMz7W- hߔv)?`|XߕW+">g[%3iҩaշY~]fa:,l gYmv3 +H1 Qoԟ(rZ5o b5As\טvsub5{D-ߴoq6Ձv]"ixdu7C41 lelY|C"*RNDZV2-*~EzNpq YFÄ)sIlG,2z:p (zbE[8$j™k Y} 6ͩ/M굧0aS HHhF@gh{p. 1F- FNp0887W^4-!ˣ!>;mƪ-mX_%dkj_m{ ݎ5)fuqx|sG>)wf&dGZ5l︋!jI42[N0bq0j1 /Mųhvbc-~R?XI{N%qjքElnwOj4HCjc[ԋGh!B)iP7, wNC^c2>"0SHу%n90;-߰Ÿv .sPg9@أx%]2eP͙-yZݘ-(PffY{lT1ٳccl3Hjc]?B0}{|9eCtEDž"Šw\p|Ȉ^䞁*\Z]tDgY>vd৅@@X%ydPqw&O,R>`b҈Nd#kHP$Y+dmMg/,oE8/Yj%XmD$0n}wbk;3)q "j..^z(rOH-^hX=~61fl`GbBhBExnˬ) 5fJYK-Yi+S52uƺ1d678 9΄. ? 9>dH]=qxP *K;}V^q?ط^Aa%jix>.bb9ͶR'> `0m(<|1;KEӇ:2Aû7v<^:Ž!wFYzwj?۝a֠=<,gūw!fAhzu+ؚ2D ്^:ڒpcYDa[\{D.ƶu0H|9~ ؤ8$`w$;Ly@-;޾SOeeWõs6.-['XGŜSVew־Bh:郀:tPّ>MfX?6v_' ˉ-It>e{Y}}$Z ODóIIڥ b@:=N3ADi-ÏmR8e֘\KA\uH9lTC} WND iVj^;eA}W; @0' < elr{:uWl%bJT)v6,)~ adlI C:Džs+xu˽<Ÿ>Dz&a$vhw4#Ij項#>Eg_m },77O-VՉNQ/w&BʖKx{tdpCrV;Crm^lQ+E dA/Dªp#hEr ٙl鵻Kʅij7ͽai·gtFV2%}e,8;d(۰VdlLƅsß3R7Q;3FcdSqbT^Qjh$FoTFmh4"0*Fn4jFbbeThF3`4LmT6jh@PxRLv,'wx ѯ87!?EWHHC俲C5uȕw,+f\ym9AZ Bd>5mqN*J$ u. Ѿ3vJ;Fx[r#*IxԊJEbwF-FX FҵjkmmgQ oz%o~'Poڲmv>dHF*aF֙;޺\2Vipm)NZ>I>c῵j`:v܀(-q@JL^B{(A;¦`l%}ocUUo{8ӿiߛۧ`:/xCIUA^$4vOqOrH%gI"(b_v-MM;/:x,!WPI4򬧔PuZ9v֑qx9PI֒I\Bc DlMlnT>YY:Z;  )u3E\/7S;xi6>tvd7r CDLb䢍0owvnpawemE^x9r5\LMB21R/ 4 !;$\v̳9Yk:Ŀd塝ѥv큍OmbS?-  \lDmB=>t+`އk{zSCҘj ^uwyt>0?9F}[{$9wY4otSo#:uNӈ 0Q^_-GFW->W{]j9?"^igm`Pz iD+B2.9j*rV}e%lf$uE9'kuDv ,N ՓS|mZ$8Qߙ,!. e!n4pM $%ҬK(vv yK:NMg;+=T}6NCkk'@u+لػPW}`y]<&_1q>uA xh_#6hou\ul|P(n|iNh/Mqz| }Ӵ+Xw+{{u[\!5e@6&[Xq73]P!}!8M+%] 'fSԟ;̩?5P|P| fxJ ӐeG|5a$8\|#~g:1 :)z[`PkGG9:/߶Me ]#Fc*śj[-`moƐG?jm9-|SnJRtm:JxTO~*I{7a5b*x-90@J~rrx3NMr2&a}7ӚQ/i;,k3++gyIܜ^/Ko2Ey0gWv^>/a+mZ:LU펹iʻ45Wa9 B%ObQ~/r2M@e>:K|G)?]_Qv,MA=Q(E<fck?glN&ߖ3sFKvKVT}%r` אa9d²E`O7"Ėz3fQ}ݥevL*2b _xS6`35YG} " Vαq]la1 *[o{`QonN@Vxw䝗;iGcvkty{[\G}A|(: i0&S(aщR7X%?X ({dߍa g~OA=ln}eF LO=U_[`u` <xcX8'8>p+O~)LTEf5 VU~vQDf[TwfDU#7-gD!J\>QX\y:עh-k{5eL.ϕ-UpS*Q 13 Vt'譡T_#C8#0-ܻ U<ˎTϬHJ-1SPp q`ơePIiR2Ĵ_ "QNhP5rcʉ <[%H!S>.W*{K@RGOl02y8`Ā󑢛T۲z?BdNvyigP>=ӣ^,5x0l3S*OVI^j KQ?R ޚ`cf-674A-,]= Ǿz|cc[!Ʒ>o׹Dd@ 1کJA5VÆ6kH6?Xh1UfU-ɃOl@ nxK~Dns%mfssJ2"p=`> @]„o!y2|ODH|&چ`4\m:"7r֞m9l p˗u*Hk~-=:O :JT7|<%t =njPf#T.suçrY>]>YZU=,mUHUSH1&rR{qzqzq@::PonڐWo_*FoCP ,}52S+ʵ̿ቖsyHeu *8mbﵱ^R&8X%}(F%%z&UnI"csEzY['vVr ju?Q~aGsx/u9ZXo.1ϑ{(XR8^~v;$r<ܪr}7j 8`BjJRfM=W|ɨ߰*5G-{zrqm'] .̎bgˑ}i`w^hXQ.}dY&b[X|? K سa7Yt"PʾP+5X1v6eWYi?mS"h&ZB(fF iڶ2+c;d NqybA\wbczo#6]r;"U L(WWX*z]˟XNuj )WSZ z[c׿^ B.nu=`nBXi# bژc!mW7P~^:m)G$[ t3oܪ(*F7VWB#郰 3s2Xuzr#\0?ZE=JF:K;Genp^)k믜LCunc')->Iyٙ1:\/M.fI S `P"B灢Z5vqq!Udǎ%i߶zB`fj-&BpfhB:>s|e<{m[&oDӷCr0|TGl װP&cOC(WH75A 㫀ViGl^XtɄaVEi.^1^\<"CI+UU7`$<[E +U% {ULT%, X =ȅSv@X|Sst4h/{PJ27|AV7|bQ IYAP FuxwUDQLtu!?N+'M=h߼ZJm"}{%5q\MԩZe[GU֬ޑHu@0l=#t$EDdG)pMJ< ĪDY۫7&.Cl `nߎ2y-ҠF<4rV1kp}~`FJ i6at|m Z(gy:P2 %VC h4-|ksO)K_%!Ь$l; #¨>>U')I I~)$upb=.[TjbY}PM Ӌ蘺Džqon,Ct,PKu|' C^mƨjyB}r):^uGqEW6^uOώǫJ;nku}!>k62|*H<|; QNY[*@}/R +Hh0W>J/RpjS5B*p}0BRqf3pmU oޢitjw"H4/nǔBSx{]$hR2_$\gy2K)|IPpƣNyz5p.Gt-8t00M/a irH&q*l(YHpcZF'' DJ@-znwKGQz{?҈R;"щQ]4U$ ͭ1N"`&b Cw /NinDs7 L(Ȕ1O^h+/& لu2]pʼnvJ ?̂hāf IOL$0ySS(!cy$_-mz2P/0LJHKr;͜g o_:4*+3KSVPR|eRqs&G<,b` ĭ\ӱ" 82H`cT^j0e1@ #8 .B.&d3!wx.(ӟޠtk+EhίyF̒ahbeR 󅃂) =_Q(".E9&$a]MdYfs._=x&n_%@M7βBTDb?raL~`qXqjZNʨN5  Ϧ$XeN_$`)jODH !sJmҾpHU@h!Muo'd$< x:?ڟ. :j>|(tPPkԛ$%ꊈ$y D8/C .NYh ct/%{%S,lUԧh|yܨAo+D66|S嚖H}:%ƊkxFVs/7xzA gdedF_hA&ԣy1ɉ"WJ"cI$ϒ(?f5S!J$ NSD=!%GA2~zOᚊ$c 3Fx6&5:HU=h逇( f4J|{G!&$*) ʳ hhU􂵜>weZ wxy&PvEhMD L<V)M![&"1.|kp Fyx?j)CՉ$NY‚RA˜yڣr ~'PqXEb 5~EY}eh8xg^zAA#{ D#]dA6Ӿ D @$10kb@ %xM%f)IZ# |?HDo$DA?LXEXZ|.xǘM;,2Vđb]i88bEbd5[+ ңӫpD(L4=:zG$8=Ѹϧ`_LXcqXѩ`&D`-SqYWgbwFzr IH891L>(RL@ D4<Gdetq %%)JbXG}R22^Flg wNmqYnQ,~,[zԤ=TBRf;9yF*TNsm6!}qLW h T:ֹ6QD >j^i%DԪ*Lܥ?#q3L@@b4WMM'GiTf ,P .|L8(6>Ix 19Wf܎cDGCWT鶿"Z 殐Af:W!&Mq>-0歴*%G"4=F9u{m y+KhZ,ڹ b#%cOZ1Rsʷ{P7ۉ&˛a-L`&;Q:cuĻ͂G,n؜Z㰱$FXJ(^FL\,|,*`y7ȫtfGF#ْDnF`C}nGL idˠT_5:DZ+W8ɤ#T [FU FnA|$?<0א P d(rrb&hvk\]^Gxn0IJdB;oH.Drn5ɧ2a<_qZ[P +6?-z|n 蝴-7YL/)pF}cZQ/ (Y \1B630/%@Zʞ nڰ}T j\jW+>Yшc?z/fj66xܿ-وp0 Y@-B^pkMPu#P4~o.6ձqMhZ$cgrC FY@8*UgB{X \bQn#_N_k ,"GR5ߛ/Xy3en5aM;KZX09ݬpރ$jZ kY],G=e+^~ffidY|"yJfR&/*"]{Y7kɈ!\]\Ib#I>q~ )}y؝*bk ЬvvM@"CVOfU7w3Hlk>*~PpB۔)pBKr"R~B)J.h}?zWˡӫea۪4:]Vn;r{sdB]aaLB ֟Yflf9, ۺ2TZtS!v_ E!cMС_h(*VKsڔS/@kEWD#g7uO7 ոpoP۪%*šЋl=x {d_z_oV\X Mje=.|?=%[8@X8:^iX.r;XV;9Zŷ//_D.P2[oRD* +!kP8Y½$'E5cYF6G4p$jI kJm*VV$1lJ°e5u{œʨ[w$t?.#Ifzve \ }+{ukUe.HMlZLVw}&֥sւ_c:yRn ,3ž0rGs+I{I7Ӌ()&$_~%uDB@fMՂ Bc6 (4Q,鎚O'A?WvZ6zy'u;1|nbv3Nl[ W9Ы\@أKW9J&tW Ai&m&)GSRT{2 7kVY=S:R%DSfIXmSguc[_V6nQepjGECxz#b]v%0XOL}TUs;+-34n%4>~`//Y(Ѽx{y [C߯5Jү[VZ&j6U:Vs?Bz?<ϫ_ jlal? t0q}}]UwSK!$F> ^`}\#ҤW[v&vāͥqҿ %yqm@b=;2,)=}s.H:Ah/ERm8@%bxH*mU 0"ݕH"E2.CeTk*˼lMġK\;3͕e N"N[rDNѲ$vd :m ~n \}| jlF2}+;ow|5(Yv/Y<v: UT? /ˬ͒_(":2prin׸r+cG3̮x9q'3UXzoOzLEϲS){|Rt|jzU»xM-v}/^Vb…іgo7HuTǩGO.h5|i5߃op:҄R:pb\1Wq7AdAġݕ;8(j?NO 1Ie5Ƌ尓v\ іvV'a]]tVۿ$ޥ\)',hUnc^>{q\;uܗzu-.|dfg2W>dMkVc.Hߩ9µ!qS s];"N /,x9.\x  } ULQHx`G3HnZǒ}MOPۭ>giEEHnp6{{q?rMnRfVCwlnD#j[4)1U_$WHލ uEqj_5NYvkOKܬ5><(~d|sw{0%r^$y/HބK9^WAIbX\}%/)/[>W'ihi*}_^c'?uG3qYⓞ|q߆kA\D̈hnK̭Q|pLp [ZSͼb0vTɓD*/{PĘ\K0DqUu`un7%W&!na œ/Q2}~_^,qbp'Coq[v]_KkL̩|;+V2xw%k>WՄ@hRQ)`V}#3IVs!t0/-ۑ[ XoF*FĽ>a|v_?Z:UѸ5)ɺ̔G`m]-YրhڶiMVqj;1k?RO1!g|T׷: 1oEUٝ?̝ͣϸbzͱ-\ƍ&bݳٟk-q^-ZBn4_|<gbKvȢo% Յ՛۴_|%b2'_ $@gwu%+aAʩ8(4q0>іk[ X|ZJga/Oԍ N%-Ҹ۩-TCM|0t c1$ɶ!ӊdgp*saX.f]p_p V 7KtG.ʇ]IjpaZNE_jH'wvoAAX";2?z -ōPj`\ܪo.9cK%)AGs+czf+Yf779g5[j'4WٔOy>f=Qyd4|s{v[~0+#=ℳ]f BhCMmCuNNxoA!4)1 ﺵS|3/-"P*}wB?ؑB{*PAhs"X5؁Åڧe8ɪ:a\ʏ uƆ-CNV }ak6Ӕ@r< n,Q'6=KR{ZʨU鵭\&&'4nttRBϲĵqBS}tXI9%?`Y(ul;w( )&^YͻyYd E7S.E>*CF>5 'Gp Ps,Y/L}9KTS>':2vN j fn<8a#8&v*[စ`jS{&lA*u&SO+q1l9ሇc#*e\I.ЌՍJ?)F$ a`k@:$xI^IJdOeWtxnMySY/ب=~?aC"ħֈJ%b}W^TaÎ2[|aѸiG=^Gșrj;B5p£!GKnO" uL{3MMdEzi#%BRLj\.7;2r=Zk؈7`>Jp_U2vfʉ6ՆqХh\(CWBO?S- @Un괚i&m^vzH8,q*׫e8*/7lUTo+~.jzV!j1SL3ֆG9!fք9֭S3)%N3gL>=vrW3Ő۬![cLfub%xTƈi},r?\ ` 1+t?#Wll|4IvG`Za.d1Rnp6pK["ewߑbt[f hs)oi@TW/{͜MI#&U7[h;Z"B K\׫Ю 4h`A!,I7rΰL%Fƕ3ׅ.UI5p)Td9x": 뜎`Y5`]K&4b2B#~:>x_շAHOO(KҞD}_IPEGʇ% Q.|vK#;ۏ>lYK{Պi'j%F'3ӋKO/,̩/;&QP= .JX6aKw|x^_9}oO6dgEHS{EP;`oGi.Dlw\$P=2EF% \A]c'hJ[4u?A^7BplEDqbΠ4ɥϩk¨T2ĞvC$$B͑zxB%]g.Z59|7ns=zP;4nD>%НZ $J*Lȷ41mUX(1/ryaܞGyG0 Uj]yE1{3l둴d&-Y:շ:6 ,R ,4K7amV~fa?u?a|)f]\9Jvs^ "{&h.tSr=nZ&"w(V:>ƣu5xIӠB75ԛ^١-;B١tHDAJ~`8֊VTAůhP N3aŻ_0!EaJَHYd*#tᔿPŊp7H,3UfuPmS#_kO) `0v:_Íp0\bZ@,;apF+FjZ:.<m:/҆UQAնg #挚P Δ|`Eº8\esIڷyNO*K+T_"xU oJ AcIq mv'yd8~A,}WdzQG&hx`y7^丯МT(};iؽ3gݩdwj z{TyX.WoE6^qނzt3lM~"l孅?p4 %daYN H"j ΀sB"Wg:>%}tmcR "ݢ.LSX öoנj|,["XckC.U0ljЁsd]cuA`wnG;to+;҇ kRީz+^p c9%9α?Om%@~R@dw_m_1ixv1|rsޛfϮx%fF=T59Hk8jx&5zuڼdӓuOBmHZwZ07xG]^'X$dMu(;X-6`繈쫍mwK>0=M=2 PZq{ޣK Y+[vgW- R8VeQʿ-L2Jba^{gXVu əlΨgAձ꘬Ur^e˺Мow}[.~40 8Nϸi!*1y-gGɹC` fk V&[ /2.jèF¨=7ƾ50F]5ߨm[Fgl֖Qfl5U51όz82I>cZ`q*ĹAR=ჅkσQM땽B+E-|n?B"Xw|P\ء6UA^$,vCqrH%gI*(bGv-M sK|TKx9e=Tbt$W;ްTڳ\V9 Ɉ|gPma1|LRݤnF|W]MńuHLp}T2AE\|-V vx-ޛ}ln.ud) J54ޮ6p+_QǓ-GВTvII{>:5X>tE+RcRe<j۠o/c8Yyl%gt NYpw ,+$+O` JTk,Wv! ;O]Sgx/ʃʽTUg~vnח=kQ{^]j9'YiakPv`z ;.16QQWe_vM3WTސj-R/D7̾ ҬӃ􂫰/oe_n o)/x !MJvIY +Vr oxۂ l|*~*  Y|o6|RN]ݱc84B#Vk˝X#ķ)pMF#SF.pW4++=#48Ǜ)ˉ qMw/{GHK !d6iS+AćtSPUӧ5:+ܻ03 '+n{vxZȑT$KL!m М:@sqPOF`4d}j.<{ا:&J{rt(o4?9_ "b7a4:eLT/7Aߝ{8ClY?ͫU|,qEy*.oڵ/TߓwٽCpSkTpM9h(;<~T£G GSaȺ?ׯ;z NWK{>jGdrK3r#q+VrI7=H>;ˇqR|le~, J!4zڲrbV|2cݥ";අJjW5R"9'eo.f^+LF d$pN)yӕ-iz$3uj'01> {G/Eޠ{oɀ' TY^Ԃ{ K?|S bcfJG}ub3`GY(j'FDZ-l+6imf|"6dm7MeRPKbUd֮4Fܦ{+mSun)>!fKjp!/mpw_Mazm&dNvTaF˷o/y,lI>6}x$҅LgS)_Ym:P ]AnY{9| W`W'["U9o;kuu W e4reŹ|<%l>}=(uwţ䛡 MH96eU׻$;I=}Dž j$VUh%R(ZXoRA#=%#m s!E m3?Wʓ^TkC^cKl_o,~A A-t,o~2o,F1>|`XTiisc' /pfx~_Feػ:2M}:/=f_`^I5Ly|6p*uޝ;~Nݿur v_賫va~Cog3lF!`xKG잝yǙܪ34rs*6hR0+" o,Z71*/jVˀ8vc@.qa}E?7ɑ}i`mo 6J8e:_6vڡ.ᥩr%0;سd{oj`}WG1ZT)oNi^2F3F])MzP5VTPP#/ԃ}*<\gR``)7өz]9$guj֮}^ђKX!4ظ͈%P|><6=,1IDh?x%TkL̡2 eGvm'w@ }LA\wcVo#1]rVwMWW;oXX*F\˟XNyivE)c{fǮMIJ zZ[-;xn- /ߝFG3u^וv3{Dv_ի߈C`4)lf}6͒l#~6=kԬGF YGXn5Omo+*8\%9!՚^Zx:+{̈́5~j&𾜡sF:nK; Gudn݅^k 믜MYN.LS >Ii{3o;btƮT>G7L0*#:BzH~LqLxCB6C.:UZ \JTUVT+=|\;#cfYP3sJq`/!Xcg0(#ZqϤzwU]nPnԨs)ۮZ0aJxJK5}S{ۼ5/aڍjs#}_}BoI Ǯ^g \Āc?a^e>A_7э?w󏅹RQwA]]Z3ߩ`J޲]-n7WNoQHn"@Մk{oUu]\lcy"L/B RG)pMF2Ŭ`M߬ 쵫C.po.GlU޾qbA<4rV.?23;y=F ͑ tM7R[D!x_ů̶J"|xe#˦ YBOn;?4FyypG9KmFJvkQ&VeRƷ}o鷏$r!'5rth8_ 2-EVrkRvp6_kr^~-EtLݯF< Ykj@6p8<g3׆;~Sko![M}nJSpPkI?o6^ WOǫJ}՝nku}^>,H<~'gH#ٗjOv*]VE-,n}/՞d;~i/F̦gv1a/Bb D,'^̗a1!s7:Tg1" ,8q<?R-bĀ '!)2 tQ9 MbJ0N•(NeȱD#;28q7b7ͧXƈWp͆*Iq v'%:;n%.D$~< `8 bp#t1V[#aB#7-3A8$cP.srq4f06|<1!m9Q5uUE.C}I: :^0^%y_q砙1Ǟ{W1bώcKPjt ws7T؟ Ga̱j ><{|HiO`S_&a,5ѪDubAXXsԘNgD#"X#$i"ص$J.FGN2\-D:g8 >`1x !> FZ 8lŋ8.q`fZgE4f4/8lLsŏTd|W{|@sb\/h $5E6ݍ?qSof( 0%&FԹ 9bM0X?Ucf]q'h.2:8U z~F7|OS-$Y2cӄH8א8ETLh"xS VQa!ăi0 П#%R @;Y 3b6 ]bC4NQk R٥?a?g g \d Dxْ@a:db#l:.' 6I9X#5I!F$LT ##1hȃ@~gbG mx bWĘc&ѐ:9f ic?A!z?l`J!cD҄(fhHH2ZvTa cD̎xXJ@r sr`=E\"IA$$ә^\~0%ψy؋G LJyHP025e!ꖉt_b CR;ZFKAteTyH8dAŔ48 y-CeRl|%b!$N i &$Z j6'J3cx HPbz 'D~DB L9 6̓X"2E!T W;˽c"B]0lu>Kx[!l|vDot{삞E94+*:@{f?gFNS)H5σ/zR(G@kyK5U X6EoJ*..' = I;"ɥߐ$IC]^,s5c̊C***pgefB gd S,E. #.Kmqq\onqAظ1MfeN8 Γ52tQoƪHP1!&! 02=,h26h0]`E"yOw?Y{ kc}=͐ܓU\-Y.-}eE o>4FL2}[L htI.eN)1m E$eUe+l#D7Cʄ5&I) m[eNxQ=b˨aV.

inq9epKƂ!yIo9<4tl.EN~6Ix qe%W DGC5UJR$r/r-v׹ 1alJwg채 4_e(/ ÃLRkk&wvޢ/Ӿ:F_76̄uLqDm9hf8CٯNM-7Ú LwX>cubN͂,nٜ[c$QFXK(^"FL"\,0G%A,yM'98 IͲ 67E Ί-fzcMr\!ΔL^tIG-EHoEỤ< Ya`g!} Pr>YJd1ϯҟ!KP.XH gKQ(ghdjrg&.`#[P2*>_Lzn2z'm(McES},Ϯ;k>e&_KMjD|.Z! YTҗ 겲' ۩B"3{IVl"0}Y?vyx9'XGb;K^2I Cedvxꝕ}sX(o6ձsM۸[%Wc%zދ> Ʀ fqTŝ"OZ\wԹ_W/$ lW4K>+wޢBx[-=i'ٲxS+)T{QMx[k12#IO=Xw33ɣ07&)Q)K6dF0LȪt@'z!'@XE"8P| $I9sl?4|k$;`%iJIs@Rۺ%%yo<3/Zeě57`1,Q՘]4'[7trT9m$3|J\İ7RubTˬpfRo"KzHvuI=gԞ0޶ᖛW sGl,yF4NIbȆU۹j^YKR}K/7gHhԚiU<9 "[=*QQ\#3诟(5>=XdPK?ن~4'Ӆ,saeٞe6֚L%ޫoɞN3a!، Ð3[U6' }7-ۯ')N vsű[)"pMfBUSI+ 2#>.T4Q935c_"WyEvKZq]S}pܳMGaV ][>ߗs/s&N}~psXL#'yҌUn'/u[ VrNCBu#Mg:Ko"SmZ\h@'{2I' ՃyLFζ%S|/\%a˩m ;;A5AH MYQgFa~;tS"摮H6ݙ;TݝH%EӐ]PB`'۩6L' {IHvM 倷0ndfb#wZ8G2ܗc6u*A*EB$=AnLNrF^ĿDz nԲA.,@}w9K ]RL`yN^n>yeap%C,!ʲQ\H'rcRJˉJkڌAVz@킮t)“Ǜ̧J11LSsgq]^7LKԵ$ǐpN(k%jզ5݆Q*We<ĜƁٸᏦQox݄wS&bi7;MNX7cXlfnemfշTj;rف険nxVJݲVa\+ӍclڒnY7|-Zo|1[g xϠVtTD mVZ3JY{9,M: 46L\̢ pp2O{_I'̓(?x||o˖Яh ##;N3]up vcKs`Ij9y םKp`թ,l-h]VHj.hF$eӳIye7NҪ)lpH02sVB q䂷[$^שWz8Z[1wKGu /6NU&NHӆtkqD'f."t>!\J́/';$MqyN^qQvMajW):~#P'u |~-%[iQ\p,꛶)HH>i9>G,a#!B[Ѹ|3  %0U僵J]4Hn#6/;)ajhtRҨ}' j7G(V_10Án_vz̏7+DjAIT,wQum&CaykWl-a[iq~z~UA*&C$Ju -h࡬5U,{߻u^Z_.9ʡ^.aIkc>vΛ7+v]%1TAahdص9Įlo,Ԇn^֏ҎЮ"A+*6@2LI½,[E}Wl2Wu\ޮNϸdOD.+p87K4gj 펵Er$ |PQ!,>hCGDGwlwAoP%n]?776i%|Qbe.PS7}tI c`s{c2uBS+4ؑVm;fVuXn;5tsĢ?,g(IEtSC}*9,J+]訧!2A_(V3%!D=&V!γ.MP]ߤOBMՉ*fBCh)Am!mc& uR uW-.曎[__H>fC%ljvyÚ$9GV>@66-v W p*'CםQ Akm_8gnwĥ@6lmԍEU}A.'""*OBIX7!ӳ~ZCV6JF e4LJ` vsQϮAm[MZU3D?yFD[c댁#vKe5dcg4 kp0dc Ge¯$Q0,@ߩgI̾24_h'MfG2?R*υ1#0~ΩUiN^g*9 ⁎czJT m pN-4Õbu.U3S+](/FSA^pđ͡eA?gs+l% C~KBkQa~?`X~qiz!Pʮ5Jϲ`:QG'ÀAhSfA<{xgNEoZE\shM~T[Z C3T6$n?i!aOL3 {.{NUŝ)-8;",);>a2lU7֎ ZZuS kTGF-uO׋$sv0 Ʀ5hkLJȀ>E_7` q1ev!h3Pga9ATŹ;'3⛌|W{qy쿌'qc7ƕI&–Z;&Ʌ'79{szB=HB`k=<=0$\5wk Ǜ:+z'.  Fx×Jᝒk'H8qy SY4Q3Q𞼔(bǴo]<]*]Xb.>DbRx"dJxü6EװP9JRMȠ'`( pH9#-Phi?r}#2S$§<'ZY 3&;ŃL;.o~زgi`rJϩkA|WBg>GFFaҒd":*Xe$jQ F:uCRVcV82_ c}ZhF1K08! 4eDf=n-QnHoDol\&Hߓ* h Z>R%8mjpL̏Ć51Ӷ y<5z_gX現~60+phoR5$mgmQX.;TytvLz+ }`qnK.{$FY1J~KlQ{+Os@u f9VK0,V6f`t0؝8%/Pi~^@@^!I)d> VfY;3S֫qPd=KWϱZ_^;~{jT?[,l<(l~a$+u?J|D_,ctwL> գDхP A24 ͛lYT,^mk=Tx%TdETqbh .ǹrʴT]̞w&NYY#_vGK|k| ɕ+59o]gzn+-lB70hT˅5bGYNm,,C(j}Fx) Ol˦Pn]W>`|H{c'-˿O#d.ڑV;J'oLh1XOM|Swo?o?7/߼( X]az{q:"Ky-5D,2%l.tEr[Qa7Ǒu*oyeNc(2M?T}ʃJ;U;lw[v;,%ݳHLG6,é|gK&¦g PT^[lf>=a[0aϦl\9{帽h-=j+=nW.rM#x{fV[Yu6^;ª,m_'&kj_,k sTP͂ MuQ`uxCȞ= H$ksHd^.‡Hf2e2dqeغcFa';{_5iMDb윀5aqzKO2љ,!31FłJ#cbbPNԵg1cm^QƼ)4yɃ%o,ӵvZǸ6w5%qYSvd]eTϙ -yZg{QfuWSygk&ƚ?sm0==\f#p%7mkph7a=+ 7㞁+ΛgBg]>; OidXbQ[ky|amT6QL 4"<$ `M?i p M+?VZxAQf *>\rm3\iH 51ɸu#.Y;aܣ ބ'|y&_6\F4 2%l}']B"m.'wײJe;h+  >7h_fnqñy?r%6{Z[hq!+u YӁ&TR`9ȧl1Pcp#QKY)Fk4wz,)l6 UbI .'L't3 hB*8Q\kO[ 28ULPPTpN$6%/!SKͫ~VxͺT"l\9p'8&8kb) S9l5W?Ɓ<Ћ|DDڣ)bи%w&<}c Ƕ@>i޹7@&BV+Nx{t$@eˮ긥v2Aժl:JIF @,kU K}ܪq8!9-R,:y[J]`vܫlY7.O6~ŏF W2:D%&}c,(9bhQ =alM[#cEօQm_7Vߨ5kmc+4:Fm˨-2ߌU7F=07FQGƖglFV`4ְT6jX@Ȥ@:8[eOpo8;?%W'! ^Wg]\ یo-5mqN.~15R-`oA)RDzU'[ &y}<J4z+Nҵ6#`{Jj.FR )ONE=Uwfٲq@wZ_:y57Z5q]9Bܹ+'g,#Pl;N858H긧P5|w^y0*tiWhe5QÿM:_/;>}P.Qz^ ;4f#*뒤nw Q#I%ZȮeR dNzijwqPO>B,*|Z{*'3]{ 3,075Ymݨ~4ki`IJ&|9_""CzaN" {=ٍ\مL"A)rF7ە&v4qe7 xZ.)8cGǼkQЇHҿ2 UzEWwL*gw ^B}5e'+.bi|4 nyyvLA1 sj.c)ٻv}x [yP=[:6gvǷ-ǍR3Lo<0 v][5zmW?Տv](4ơm*v͵ﵩطor.j^钼 ӣ:j B-$+1:u-l@a݅6F1*ꊰLKnc*BrrzY@63Eٗݺ}T[uzBZ=^pLkmw ő]5 2V@W).I2" ~a#JN#q{[sOO3 !9O rթ ;VW}`yl?&_1pj p3y_#eNuz\ud|P(|e'q8| }xW"e9|!ce\y?i)"!6&8mc;1n*U*UۻjԵfCg{ffxe7^ڍvcn<O 9*{ ?/ͺQSq.J)cl" OMTЅtgVD)uOe'?KTQC@&;FTG̛&S{gȁz4vgPyUJ%`7(Oť?STӟ{R9wh~ 8vs )GmT"W~rv|qǏ vYx?Oh*8 Y7<#[=P"'Ӻ}u\ wiG투:^n|iFz4xŊZ.=]}y0Rr+J]<2dc;Y]'׀[}ұXv=qavFFZ"xI4Ph u`¡epgɉ>֝lڛ}Z]zԧA)Ѓq2ZQ[VN,ي/9 @fTdBܶpZ^&YV$wl-݅ӕkŁ#)%ocR%BdN&vy4B's=z7ycv-"J4˂Zp0u)0ǔOw*$[Cl̬A PpNlf6k`Ym8mFW?mߕOƠL jI 5UUȴ|oϹ-b7#6?l @ n8{kִ W>T̈́I wՕ*h}>/}>C'fDyW0 l*E0+KM Aa_+-9k>g\l"puVj"qmM=#>alF8P'M٧o%q 5x|3T)'9ۆSS zd'A7تm$YZ%\ :M*h[\cķMd.@@zm@yҋڞC| `Hklo\#ȷ!S@22]rҨ9G߁ *8m2wn줗Ž Ш {w2\87CPgVݢ ,:)ﰗNٻSxGݯԩn=@nv}vn>3o(l(,/Xb}Iݳ38[x&!X7=F\zNT*xB| ԔZK@&7^WwC%ޖYjpa1b0./Sh796-7Q쳍yF GL&1N;t܅y34Cf{l8|Xj;f8A2mSi=:-P&h&@( ;%Z ӱjj~eӝ8㱱zp܃ӷ_ L 21f:uC_+D%7"vloU) Pi{AOVxKP`-ޞ7ӨRVpfrخvfnYzq3{=E ь4wϦY9{֦gm`!խmWD0'Z Q SXSppuƚ}/Cџޗ3y0C^m)vm b ua{[![7i DT<__?)mvM}G!ؕ'()FReX"BZ5vqq1=UiϞ)i߶zHhfZ~!P3, z E[9kɢ^˒[*ma v05+4ʩFUdc>< (H+;*q DVM 9E+ˋZx"xq(3 Z?$*R]e/ɥнŋB,ѧJ+^+QUʲju|g$c,xC, j3[vv#sS4E8SbL6cD+TΠjЍuCbF>'Wn0 X!J.ckS{;l_[kuEةM7jݍV㸚p c*kz8tU}\eYqoWwArT*|>6(bHlU?vU`u5E*7q=],r2TF ۅG\ff'|H9nF4q( }Q6R)7Qwtlw4A0Km燦>h2 /(qHZܮ~-ʷߪL֡oSm7Z.Fο '~Af|BмEXJN~-V\=T|}k~-R˯sXZқ3} _ 5N!ul{poym6BSiuʽZ?J_-6 ݫ3xu2[83}Mk_ԇ $uLi$<|;_7RNE˪@Օ}]/SLG֏3mhT~]z}.Q7:XH,!=<qļrbs%(/$D_腻9[>^*x̱j ><{|HjS_&a,5!DubAfawH9G= tvM;"<q]NNć'!hPzxvя7e9eA _(*PSǘP,!aDXGI#o9:&lD{3p6"&R E,FIV/DeRQ@;QWub:H "hJ#Jw(qMԅ8!յ1 }#f CqH5|IDŁcD-~&C1&М1 #n@wh4<-ilcSĈ: pa GURѼKۙF3R;1M`1 !T5ӈFiŘ$Kf_cGH Mo*SJ#*,x0 !ats\jf@;Y 3b6 ]bC4NQk "~KSrܡz%03b]eR y C"ls; O KABMxX'wDQ34Bc& $ׯ?2[bHN2rN<C<~&v߆)zE98fBǟcؐi[ƅ?l`# :4*)Y)Z91CX!Q9#8101 +h:6DB~` Ba`{E(]G@3 \Bϙ A[2zͅE HH3Bq`8K'4;%j"<`djy①3mUe8kCPDF)8_NdX'S!t{Gh䃌@'.B\D?b@f_wG'D2џN`RK =$ueLWFDh*ϊKVq_LIR8dzYvHDUu9b}>&$1/RCH(T@M I* lNf.{|N$*ɔ3`.Y1\R^ B8ϧ$MHߏw񈀋fT6jʌF,"gLP+V oԜ#A/@~{h%\XG礬*X2H,HO/f DH%U~V +Puik0$2,3{BK'ptOᒪ# sFd6ئI~?k퀇aec_j^3#>wSs 5!TY9MHw~ÑST= r:ܕ$VLv b4D&`d1L;O >Fc` >UET/=:}a K,l@(ӻׄHEVd뛨LԵ` 2wtMC2 u?'0g쒟OgYJZVc1]ϫć!WrD]@d#bT+f¢H)Tw2Ye3&fV#*H$~yp†n7'e|ҸGS~/&l:TT.O_brh)l3T/D($t$~CB0';G E*wy̵ HF3+o$P',q JKVSĚz;d %l:N-;',u{OrYkd".E~c(FJ+6m;(p:O>ȸo G!!7q`cly?$aCF]΂&#iÈ([4a!t #W˹ϰ6 9=)Z%r o]V e´惚ja[v(:1H?! i,sCCRgffeu?GhSԪ(J:t U5CXRXWFT%ynrm9(  \u`@ydPӹ̼ۥS?L+*QHVl!QA0"~l"Y J>5J5 10[CYe"<` %VRw* DFJI ׄ-hgf&\YTU߄BO2Xܚ6SL?br/EWTRCgb֕f(=ݛpFz}8YN %olSJc.E"PI̲& Wq|'YߊUQMi4Tv[ޑQmjl-ݢ)%[EiӇ{6-Vwx5O5MJ+$l0O#fѓ) Rb3sȨѵc Xp^_$u:94 1:H[Gd93-Cz?N0Rf\)(J{ ("mR(x e0NdA濺͚2 edaNa*| 0ME "{S: &}RjzcuNxO4VjYTMo!*_t7BKb LwLx+GzM!C@-r1#L^^r!|TØ&IFC:㍊\kl6(Jl-^#q ᛼έ Ju 9~IrHFT3e4 Ӽ `p-L.5FYkJ%%cE[,—}Vs%ҳFcto']&K, J(UnBd'ziLras0XKm| #AG WƓ}Ք^Y ~y i.!?ꥪCSTO!>g((&[UTo"L}6 YҨ̇A"38q#GPfxɎ[:gSTbマz_Vr% Mt4J*PSeީ.EB /A "Gb7q+Ʀ$xpK(@UR[p<<4Ml!frg-8#KhucLX$GJԆf34}$;$rx30d|!3Vg .Ԙ,rz&͹=6;Ke-oĴ*bł1HyTn7t󈣠_dI,`kmxS`>hb74(5LY͔O':t]7aˈY4$QZ<_gevݗ, 5/Ū$j)YN&!X.y rNqTp0ʋrFF/'|f2 ?+N%HŤGF)w64V9e1JcvQ/nEԤV@dQ*碅=aA%}) .+{ܰ*?,2hn&3ڧ9 nw< s|d+-ِh0 Y@LjaNwY97:J聒x`S;aٴUr5V7l`lhG:ꌀY..u7'[OիybJP&|IHzqkzTq-z,׻rÚv-7209;IIՄsI_,=Գ7;3CZηvIH"CVOf49 $[>*yPx|Lڪƃ9SŦ BZn L&-};T!!eoa۪ r @F).\G;a p +'v2%4N0Ig7c[78fa k4 #.O1N~#/*)M Ҝvef̩WXb(,[_A p8Bn/V6'x;]ycדdq'؎B[m3zP$~mRBBaqG^DmEb, 2;Xҝ9H `ڼ|5kUK(ѹ&OtI|@F9Dz$ 3^VNY {}8UI#]|R5!pLW-GӦZZH2çM ʾI3 5YNh8?)N̺ g&&$dGz[[w<ǕmanyLfܬs4Q;S[xl񎬿[h"Ŭvo=͌#ΖLZqn# &D=D$pwbfaNFf-Pqqu@N(,p ө$QhVt67u/m=='qhp80C>iHF ! 눓Alr`R[:IR5/'fx`grQd`9r.hȘ4jZ`7]T/aJNGF)J:7a$+yqRYj@LG+MbC&ݪ2>A5;w] J1iBmqþtyIR1Z~T̶m<2YIӌ8ɳQ$Jiu' 2 o jʜ%%El*N׬9ʽ,Ԥ9,5.gj7kYN2WA:51.y^eT (nܪ6[c2؄g`m4 |;t@Vڛ"yp'^j[mU?/]6C3p5jZg?( 5еbxgur;qң+w #QTIdq^ VÆ+b ,EdweHh!]BT$ H|H/0"t 39q ҢJwN )3fh6AF6ZYHۦOS$FʷM+Fƿ媛r]V/_ezle C.7[nۨƖW P p^}`^Uϫ (UdG>/eHzjZ9?c/[p~@ҷ$w տ(%8oR4-9sKNjqL*_vjᘾV札"p5||%&0wg +Q.&_ڮϞ\pS3U )X*'?nt1e \d,JV.F G?'&$=nٕN B9J#'Bu-/ܭ:MB(FV5o`WJpwƯ,D>RLBӝrN .Ѱ?Qu$D%{GA/ߪ_Ѫo"G_dSn㹬6GyN0؄HE\OQG3mVXvkXjf7*}\Υ+ǷN.oC>O{(thKG`d1 oJ0i]%ߔ*u5|%AB_L)r:BGܳO=2iۛg[J z:Mtܑ<|}Qs9Z1K\șMUѿU51>*uȒng}e8r\[o]6j1t5|L'Jȹѷ:P@s*I]h_;9j ޭN@+8n9cwHe|uk)%k+#$ 7jq #j8+V,IU#M@,WJ s-XX7̍:,8[\ʐ=yK◔Vx5$"+~>c|\ s>P'ξGڱ xrfD4>Bpna/2(;uUǐܯIvޛ5`R'CH}ݟM}YգVcn2W V|9ܒCT>`YEP  iP;jڕNE~RhH&-Q=FϠ_HUc`$p5|e%\5*47K<=\"ᵒjҖ|4  +95Dw`y*]]'mW|.;naAҸwDY$mjw,}?|[ m|!TխY G1<- ?UoěGA.r5N \fc4,Tjt7|Ÿ}zFCͽ^MrV̮?g [qziғk 7ڹ7oK‰p ҟ,25.gv^Ͽ2lފn|gXv;)a窗0YZs]b&Sq\w[f N5njFF|k#M,fFC"C_ut>`~A8I;8P}D縨e5wc*߱sr5$@WL<] b8z*fmܺX#L p:=߲p_< =<㼅-ӤiRߺnټˑTěԋiܩ?D.Y)4~w2/GlND4~:IivFx0)knvg!Uaw*q#-y9sd[ܵ.+!七&֐>/3YQoDa،jL z<U;smo̍\M~})I5mb{{[GGIoߝj%^e@,>2xYoՖZ*avP $n^1"kgTw.Gܞ\nxVO/̖r7dt¹`;Ԛn*[rhܯ j'1x[&j^ pƣe"(쇓඗榊"CKrNjbYܨZ#5̧ltQlt/ yX908-%7 PSPdSR~`|4 5?XxY6H >B!O8^< 40"2'2\n(q΋;[~[%%}0Ѕ5{ڋ]IޥZya< -V# 5fQʂ>gf8YdEk~TT5 B|zvը-dfҗ+%иE&~ٶKa0.0 OBe>9Եh7X_* ̱mDJדI2}|s}ߕ&Ea]e1r :rZ8d?9M7o|9Q΁Z9Nuk?i>X3eƤ [Vr}y}ӾW5{.~H6mc^ o<ڲZ}sl2w]KG[}f6fLH.P^ Kķ"05n[ܛU %mU{4>{]`vz,!=hew{8~|mL;:;Bp C(_&¹m]+ =T$Ԯ!ʱ˶*;#|] C}3F@?m' O]i?9'aS)Ϡ} -gk[@J.$k. ]nHAQ* sОvec0{+VLJzyc?b}|̸]xwkWYSdbbVzUU+j+ .PR%btFEA:zon'UxET_T0 EFiʺXJٚ`Ѹ˻Y}*oh]p]+u6To2xC$V'翺`1)29*5>2f[ԉbqpUP r'=łN˴Wv",GhsgZْI}ko EMMP,)hdRp(‰)p:Wu7#75^-̾lJUԏe=~db[ܛ_W%w(\>R(Ezm[vn(a?@; Ie`Km,px*ϟfmV_anyy Jw vY$6Nt:PgI%5ەT:,/AQC_n*,*eϮv?ۼu#7:YҰ34vݴ% /*bg@:߃  ]l$;g*Lﹿ6]pQ˓-W7G]X@*"}oSYկt;-Vz -̋(R4;NzPӧ&R﬋heб]c7B+m'+Tʗe?JԆRbڳS٬-baъa7W IrZku ū"xgzrr[o.N)|nw92[ lZbOVe'\o""ElCkt0*[R}'("n.Z.#—?.xx* 2:?^FUSbKzJ{*D5AM|i4OxE#0I_boR"\z1x|4 ʊ 4>F7]rf1dN7mlrD7{'zbǚlLy5'<تXjI3wLƚG1I^%PV3L`$@#= &\Q2VSÊXLGv6{Ts۞㞰'4 b1iU2P /-"/XKl($)(-P767 U %u{\Cc\vN g-h'z8m-E݆ށU*p(*x-jLØ~S\W`."J}|V[^y?.t!؎B8hD kd tdkêN5`𭻇+eɦ\kU]GE(h4JkWR)r!n>䂘(gO<1&}2qՇ^UȺ|u[ydoE[*4D,*D{H {K~xTNB޹B流 _wGe@PW^[}۩"֩hr7[,x'_(v%J ϭ7?9ģw?Iz SCu޻jo Bf9cϖjľX"IC݇CYs\t*,22$IFe  Z0uw1:S?u:OS?u:OS?u:vS?u:OS?u:OS?u:OS?u:OS?S?u:OS?u?u:OS?u:OS?u:OS?u:OM66*؊oxlnn݋{qo܁5{g:Cl.]HbԬ /ӂcH//1I3Iy,"U|^* p]wX3ځG:7%_ianN6=K(~2IV;_o𤲾R<~>7D&W_og_ߋW_WߘoݩS-V֩Sԩu:uj:NZNS֩Sԩu:uj:NZNS֩Sԩu:uj:NZNS֩Sԩu:uj:NZNS֩Sԩu:uj:NZNS֩Sԩu:uj:NZNS֩Sԩu:uj:NZNS֩Sԩu:uj:NZNS֩Sԩu:uj:NZNS֩Sԩu:uj:NZNS֩Sԩu:uj:NZNS֩Sԩu:r??t.|:~c7q67xsn9N?$zJ[{f`TOl=J @u^˙G/=q| ݃nz6ٙ{|?D;~< = wߺqރ[bkuzzߍʋ~mv:hscݝEvG[w܊D 瀿<@"q<%!`X"'InqJkt>/AGg*IF K;$3μ-6lo?|qB[hg8FGk><:x~{ptA7FE\5:]y4Ͳ(;D(FAOϥF[h+GS2IɈ/fseggD-?S`CR]iDӍQ8-xgX|vq_r] ntH( G~l4J!@N͓݁']ԙ+_pŸ`*n4^|6 :CELҫ^nmu0;Emtu/$wQ9lQ4e)ЧdD e#K`qqC>D,Д:I]fWX(ţ(ឤNo+ Nd#\$MG#t!PP*_}i_PsIfӍWu\L<6C$/Ŗnh\}ZӃk+&1б'2ݣ?* FP& pqӺCHh]m:,auX6{7i{6-G;ڢDtmU@ Of 1ZVF\ȦI @dMy}W8d.Y2$z8ZMϿZZmgؒ #[uoVh.`־7VY? M&s$ERJ.\5`M#JYSSڛdȬȜ!M\,s-: =%:"j u dd06;R-wpP#y@ᣱ 2$g}-I9[pwu`isAN'+^!IJ: :8[fr#CKwW pvG׽~OicGC3@N |ip e5~jaMGD44(\?jvG-*eO(H᨟S#K֑EmOTa>%_`r"9*qq6XgQs(aeܲÎ>>>82]p)zdNU_69zӞ\(|] ߨ[›ᄃ!-;ܐ0"T FZs9Ww(qRC7{ zͣ*X:/ۿ9<C[`dD)#hN,浞TRęN*h,9p`҅J؄m1Hݓ5Ԗ С%\8vG:fT'^Jؗ<;*2ϔIISM2.$y/L2EbAȾS:wyTP7R m`5nIN)munW)ܤH $pE !Vf<>|`FBYW<>K1}8RXvA^gQ,a"6`$I\h^s(z`W9@@k>^~>#wrI/hK E6+dr |.j/a!Άhbq㭢{QF!Vz܎eAuD@#:k9P`,KZslAq6nRi~I7\;~_S;@]r퐪D_獒c?C}~O.4d]Cq.*DPMǪ^_<P?=:/@0/hGf*=;|~xbNl-6 EV?>xvD>rbgǓ$axbk^nK_45WYYO"xrtr<hH4{?쿒_ϻk\K]{rh$)}MߒFQ x4ؼ]b*Oz=Iu,4VTfl. ƭ?>]~5KZ'CÌ|[hbAR:.,D887ˊU'~44\X 3p[?1]8އ_uD?4_Oh9|_TH؄sA2WU;ZY Z% Y"e. (?W/ּ4Ai4odǿb O^>{q VqҸX# e"8zzx|rIżw;,uMxdA&(P* 51qk׍sGbtQ/Zt=  LцMn<<ùS~rO|7P"M(I;\Z SŸ]z*P}Ywc Z~pi|6O2.<A[;K.S M`UH$WtʫD mf]Sj&i5.&oa7ŸaSsbk}uNٛ03_nYNğo~Gb«?7lνMY>`;zwݭ?qFB$x%е@ ް}v $tU(uQoItdenlv>FUt\g#n!u~{5e uDЋ]*|9watxLׇh?שe:?TKDDHdpD 8"Tz ]C.ptKW(EP6"S8 _|v c@~GW ,5bqJ|\l*!Ԅxv/FuM;Z> `;_'N[R̃gk ܈ HUkG]Ձu`>0V1quҏ HkC*YJ4̍p&AOdYg~xCPP)T6 W7gԱ؃t?0 o?nX8:BP1:B?iP#:B?J#g.OQBZom\LJ(|Th*c:FQc0F?3H?Y ݽ?l߁r(C {N}o+Gr) Eϱ +A$ـ8p]"Dee /9cʥ#1)"AY>.FWҞRę1-t>Zz@ӦI"@Al;yGlXXr_WOA|` %'vTO.sۦܖIQ>NzVZ_OhoŽ`n<^*&Rbň=P{&ˣn W%;۔5U*oLGnT:6y hfVN!@/՜Jٷ*%BQ`2Ւ8yh6KssC\]:HQrrLUprn62NY2q@ vb"iȾD am U,tQS"KbWRJ 1dG;vynK ށdEEj Qʹ]MK[52AR d6<Ȧg }?Lx-x.'D^̆ IK: Tݑ{!@5]inUR /*B;r4J|ԴW>3j9`ASŅ j$άeJX]!بz}`gSTi|'ϟ,Fb  2" ɭ)n_4_$v= (ʰt`9K } R#X8e{ďW<&M\{oV~X;'oV(Rc]f37}8uBz \̽D1s' 9 Rʈ1Do<^$]1UoQ$uqP''Pq2 WBGrYKGvb&!J5dnd$4|(28K^HyL: Q5b\cbǏuΧ > "2#! -O^:gB{p=^ 7ʇJ bhCpu09$R05F9\5&QԏӍE3HƝpƌOyEpG.~תv^W| ԡm*xPXGϖbf`;JaI*1~6%&PxLz +P"L,}}Eޖ}4EoR ^Ӯr~/y)?3MmH"ȑc}_~nDk _eqZsn4?U[Y7@h0)՘Gg%ӲTH"xTJ5e: [&T2aƚJw^Oy'Ǥ'jEfA 3$Pld%BQJn'IM>ХT46THeV(fVd,)]EIFۑKF&5:S}7hV1<7v;{LЙQM3,:g]i?EwJKYMg$0HDt+ζ?T)lX.^MP~eP/6QmH> y#8@0Ks i:"d rh1.mn1d^ HF+2t$H1 a E orVu;Tu]pCFI1FaT*ljicUP)~iFk9Z) 1I,bg4(MC8k~ɌIQN4r]>F?dW"Cn˝3ճ25Xs>XEpX.m5i'Xܕ/qHZA% PVîqt a 0 - 3=b+`\>oK)33 bq` QN`5֨z4fзl1Ac6 VXAPtX;#rdaiIa_ͣ8X:`Fî""gIud% =fy!M^5i"&` Mx'%0H.-l6(74IJ3p{p 4-(畠`P`OE@6uR_sqoe#hMn*y|_ `O^QY{.I+ f]ݻHnOe՛b ^h+8Iuopmrk֢yW Э05yYWjfSK|=[pf4nW?jrBIOֈrmlOXEu ]&هk W8QDMӇ-e4bB~KF"b%u yud}n) d5O@Ta P +W|9xe}Iy;XJ?I> ȴ鱔 ^܏CF9>~cs*)!AΠg#:TB0<̦ 'hx:Jp'`;U'rfLX׷f&ÊSbqqϙG[rjP!J*:[xGt.0؊e xj09'/_<9>XŸO[jĻ $AbGQDK~֥f nٴOG]T;ld*,\e?hTU,cBWWQB+}rWc-%':h"AAx\ME[fwy?X[ _ѵj܅oN0g(rAp,(G Fw*!E)33 EdhzBeOY_IZlFtȹ59m6mGE  biڨ ]= @v@ʬ~CSk*`1tAßO`+#Bԣˎ) , Iu1@w>j]ʻpksD# 9z(ӥI0si=:J6 g A#ʕE\;ut?Qplp2D ņcPşLH?QMjb<x`9!;:J1⤢}AyɃB2C|UX8*S>: .g Us}$i }!iC;xpX3Ȓ"T_!!uLB@3 _U**TVUG9+7n{'d9Q(4TvIx\]ں>ݪ2gokD|27Uh&{'\%8ɛ|"9s@= s_lG0sXT^YeM6GBJ <¨ϯ)YqʥnE \Ʈ NN3 ]EdeBy[ A1YGOxD9ρdih~EL.a;q5AnT1Dvl.ww;ۤM;Kίw(1:KLFխwi{#vthsvGg !;I0 _#Å).0Y+P¾>PPB  u2\I"xFˑw%z5Շ?0a $O#"Hs!^޳JМl]جnuԩ~7Zqx KJܗHZZvHaڿPݶ{WM9/% N6Xo"ϒ!>#:-ͭDW8͇O}" SsQ^d<wL+B+vd[pF/Gѷ:S1"l ++> =˅Ѫg} .YRGk /z*s YAجD"og\'T=hKR:$햡Sj٠}߄^v:fjeIvޤ l~&onl 81{KU;gm=V2 NNm?FF=~ھED_r9C7>~ppN^6P-r_;mlM.ngzt{;hXnk]6B^Ay68NkO]#_2+U@qG1*p U`h\/21EYN)%Ew¢[jT[pxmnl8ҍ~IIl\|Ok,ӓ-z>ڦG/r;eЧ١rwz==z ro2K{ 73'msFgK@w*eWzv=w}xET>+=|=(z%#&syj[6C:|?}W_AÊK8WTLO]^};|&{86'هMSj;vt X,c ,*rY{}tXx/K7rsX8+*pVՄLi`TjV9J+#+~8"Gq;@*ؑE8T tGpخkz;͖ȥ] &Y򶙼d&slXe :B{:+*Q&3,D49=ʤ{KsʶtB^^T;uJ[ZO l:RVt)OȤn&@v)O]5DCiTBBE=!ELE(L"q,TH;J@O(9i"-O4|hmMZzPl9?&umHaxyWFz,h VE! G~k97^adOidpXz4T99E@l%h1PP`snFʢ7IQ7\t:&- (ƣtlG+n׾mxɠ.G39&P{G!Fg',г_B~o#tԯ<苨y,[t'\0?Ld!LX ֑߬2|*|=+wW~*x?I;r C_8)m.b/Sx8I 즁4eTbGf6b %uXt|mVQ k m!?[l T+YU̡YʋrzLwBtESEoC($!amgwL7L=/^mBG~:*SVcH7J!UD8rJw=>8i᫓.fof+xJIO* }sοN_g a;~n(jPk'" >XȧD q;/^Ub1^wx_ hqP0":etћbS1E'W|ƪwKc y/;\(t%RL ܃VjabZ|>~)/z\.e1A] ' r tMffcL+[[_m=~qҭp``OHN*7tCni`Gj2qsX[ p`A(deJ>yO˕W aui~Fl'pG".#.M@.f(&L38kf2,`kt4wR5ÎX9y'˭ 7>W[6\nq).N 'gc/.|OdG4lrhUHrӍ<jwޤS h{ndԗA臃.:%u!%K֚8+)Ҧlt (*1c׸f ƈGJ$x,ȥ_AK{XK!y-֯L^, w$%hlHl໣% :W8l*Vk$F,V-[M; (wj}>SZlnhy<<`6i6ŨtM2C" XfX6WUQ0`BZhZ;N:jiwnj;Q\/P|-].NVr~Ja:Yqr0mrFI33n$&ÓCۋ9Vَ 1Ͱl28@\USj_"nQ*E|̕6a;C!<vaS۠,*Qu/.A֡zL#G ?Ck77{<}< 3E3əsݗ7U7.}O髄:Stf2S#/-'E;i85"}˻3\(ȗX=3%aQ9eT6hjtIh>`̏H.^y}/HsŘWeGOQg/Rg"mU6< ;|T=m]5nbI@qOaxc0~rzKI5$a0C1nc>]/ :;Vz1Vrd6" d@>ְ6Tϻ:}) y(СdwW.O#oG`uQst;h>=GյtsQgр eF"cxH,PI1nwFi6zOoEC/uU<+մRYӷ u)ZwUw (my5C2:RMo7ω 耙^Mg2&RĂqsU b (ގ,(aN\&o#_~O#:!tB6mꧭ@VΩWk~njnlnƒA&l<>OƭbDA %d}nyOD͌\rp-ҷБl–.}Hhva.Eɰ$MbH}T``LzTPDh |ShYky:2406 E Gpx'ڣyf [>y  eѽ+CWtp]>gNMLeyH-f'>xxv6@5QLlWC/7ͤI6N t(Nhc.˽x|o8~/qP7F[TwPT2P韷ܼ낚9 cEav t$u[P_˔v&Eb-TT\QB:&2^-eOۭYM#Qլ l1€ܪpQP]J&yS"LšrRR8@NWx߳A}%}q=d:*r(~ZE9 '}E(tb8 ㆉ4ZNs1k`KdIx & TR$QElHӵ v:F4$yMH}'gp˭.Bٮ|;Kӵ' ӮsIp/0h u x%zԉnMwtajVfX1]u> 1A1r22'j41@X7G>ć4Ϙ $dD3(smaS j7v;XsrMKmuL>v|\3p sIg$=yP,{Og$;X{&`NpuH3Vf Y6kkcB`z8N0N?\ z$!=xQ{)98ak+5a<륬ۏ52@V<oD#xZ$0kXnG>k`1+Q< ']mI_y=A7]by Bj9I-OY([bx$QHTeYSزˊӄaLx2YUԛy bӉS9MudWV)bZ ?ϑ|8aՉcV8Ͳ6Ӄ$B({ב4SAZcu_~{;BΣd:윞%M+S!kgJҟCw>_R0 Át$mH5n|=%uK} y߱~ -+i]hN<ɞYe..kbi`)ѭ.%7o`S=3:ķTA )`N&9_+504„lB>wg@*@jkhA?u. +%Dl>/\`]I08ħ?7ko:o|۟Y;_Y7|K]gdw}o6_߬c^tt._sKM/=-}%B?¾6C/+Ӕa|sڤnLm] R.Etn2!~;Vc&m! ag Ș#v0fD WzB>+^]n+pMw4 @kQ_nMoPE/Fb!;yiNt3Q՞EfVH*Ut<̑;tr,R*eC: d/m9jo2uUdDӄѡHb6h:w19gZG3۔e#̨ a76;>xv$zVnс uXێۘ#؅7!&@Ѳ͇v}3kb}\Вt!8AG/Ns) %@>`?b*"QܓbwMx4>] V!z2t >3I9OM-;dnkGɴM|ɯu 4;Ws 0.LzZoy -'#lwecI 9_h?/QIEi h%8fYy*qOi#^g>Vg#ZAL*.&cT ׅp 4By%L8H(")fT/9ԤH0 ~=Q$m8!)]tbh],A?tވBd-R˒e!67.SSԷE>'Wid~$v2jr L)Zg)U=\Ig΀R5iͥDSDVBŰ{+n=ܲ<0"$l.o'1~,O ~2I{I!COi){x$jkcYfUPQU~W{JKҾ-x,z1_T,[azّ8@Gt@ |h3kKkbxk=V*{UΑg`Cy.Ff]2:fBr%Ni+nل 0`dݬXܝR'.(XTH䉉Ǔ'yuLӊc[ҝX N*D5zS(?:jbui&T=g]|FO}dS^-@:@ lUILV Хq Zd<7,ץr0]MJn&9=ʶY^J Q1{#}\#`I{eG Nh;o8wл Achff"W*[RR-t4|7p /mm`5Bns]eruk4wT/`ڿѓ@z#|iG/v ѯ "P$NeYš%L<>YՋdC 5R q)8*=շqU< 1I:ʗpu΃-ylx?߬|a<3hߜ"cʳi-B_E{:g_Y'J}a'zR1d9M,ӛ ٢Clro?"z>(xN:/49rvAS)X`(~s^ݡȅ}We/첊3bc)7滟?_77qFFH==| -Qj" M&zz[t t^`)̶^eR._\qm,F;lMAD!c߇ W&pŒHDB[7Ono/PNx]v~v1,R 8*T W$DH72eZT!s8FGT!v ^+|[t0`lֵ^D8 4YY?= S"AԆbsNMҷM?``3mxft rwF A$yEw.rȾ~_ݻ+oњwiូ#} J_" e(oz?7gc+/ZS5(J)-uk[e킳}g;kkJ-ajZF]$:szZAB'ߏpӋ$QBUy[&7!Z5<`;^͍|_p:ƀpqx}/w6hsf0%(bJBm6s_{R UU/$=a yxWpWh+^n4\c8 iisy!5È JHa.TQ L3#TrFq {O J: 7AAj)c̼I6rdqy ^eh;F(}#2 g`!x`D=wn nNsDQO.8՞H֐3010$,%QMKEA ,a7aH.ҳtm#No=_ o)tL\PK;\5Z~M}RK #xHE?`T=G7;_1,_}OQiRxqF٥2B)7DCKd8]M] Տ؏굢4$H\ Tfn1hﲮW}RPʚ2TZ\hG~=G rM ux3Z Sk5D Vϡڷ/Hz6ӓW?K,cuפQQM5hI۝49Nye˪~䔏}?nOQA,_gyǟcx}ocaOUVp7YKwZWWpc۳ߝ*y4.E2uF)gFz *05'ӳA*>҈u1٬q{ݹ& cxtĤ(wF8>j;hԄТW^TS9Iv~B󟱥Kj;*LXfqvdטv+'P:I|6v;'9 Fa)\2!:t|=bVh| Gm*)S6_ d)lW>P m.9- +0ApFsˡ<}džcq>OY 8_fXNҥˆ:d5L1բ]5xY'ihf5!z+ ~-d4*Gc3]|kr]e,$>+ܗRia52MV1__Ղ U$S.YB=" * 5v`B]N qLI*j+EV`)W||ȹ,ߥ|1cJ霝A)ʘ>kT&YsjEAPfBeq.ZWTYX8͏(\1iPC" oJ?MQ>+jTr^w:f| 4Mk~q<9È=89n4,#SY#N(;\|4;+?Ru/]N%UyNZWJLN % {sI+*ǷE 9]T=2x*ixܦfhv'XOk88H}%HB0& S@e[{7k^PҦD)vP&vK9矫`kvkhH*=in+B7Ȯ{{U45'.q\JR8JL g*BADqQ[4t,ɧǚUG IX>ôg6jBy/078|(^LR9K>1;bj"q$#} >ұk7\ʼ*NQhKk:| #%!t{&Zv&^/wsP`ir V;x{Ui˦_VH7%l`%l%FIf#'>3CS"h"b~"9EtdzRh{CG1jU3a8PH>|*?%%as%ѡ2AfzN$V,@q4Z 0`t 1Wu0°bg/IX<WUNQ#Û*jqUಸ[=\|&%p>&I>y7fcR{(S~xSOX~.~sNܧeF { hAMTs@;TV85ܙ E؝ÛL>vN0fn)\tj]~>;=Z&ry1N;Tu!jhnc kkWv4}0I^G%I(C"q[Yd= ΡzLŐLjQh ƹCq^eH9AE4uV4f1X$ I9Ka<&]oZBFi6HΧQl\O⳴']W.a 9P{#IեS!E4\J%iFQcm2]d!5B@X/E+ܻ6 #!`WyNɵ(k\]VYʄ4>rU!pr5B]Irvç5h  \BFPln3ӎ$IF(4qs7k@ H`MPssXϓ$ ٘)IIz؏8u}FDlb*E_~)*|B/te#>iPߦ9gP+]fۡ0<ܨ$a8%hsr#Ʌ@$آ$GfB{?SJ0U&Q2]Ii(v˯<* 6]@.R#Kʧ3qy*!$S6.$tLBqQޜtVDHb6كYL0Dq%v'I6Swj a 5lpDA/he+fé]< 鲱+>:A\ a 0Ĕ E&h ~Ԏ=iEs%ƼX Z0$n'!>;>}g/w_~fy1#S⁹ QE} Zp8ǎu&^ziЕ]B, F?A`:P&ϙA^C y Ctx^-edt˧dks ow|kMcoN wrr)e3qr +H԰ʎI9"g4xGXUj0jfp; @kpS``U;X lƶOGfѰsk8/3_^p#+Jp nBZ`q IڔQᲳ#*wr!-zܙf;b@Y PE2>Gȉd#O!B {+Ish+,5NcY{WGxp Pu~09˦lW0_&իTb#o797Exr~p/;M|NNMs }DOQ"?[5_ 6'\c;_.v<%F5=߁Vm̏ 8Dp#C= :T%T0*&Xخ"fV[BC)KV_7dYy`J9Mubx=<5$4@QmM(@mPN(u~EnYdOO_ˣ b,xnjH"TFv!JSi)5C L6iے*c}|mu!XI6ׇ9U;Eh[}){W슀GNߋQF٦ȶAVi{ڔ@G.ڦIXMoo+o7>ݟ߬j ~o8FzlF+5W~)_e'N30fwM'G밝`Y tБxd֚ΰ 1PGQPUP(e;?/%f-0S./T =mxMC(! d,Q(#nx8ÊG?'7wsw^=rTgF^x~Hb7%T8||]}ߚ˜7*(O5 i3]d <)Ĝ͊ p(I w)0S:3-]*h evd$Q1|bh$= ]xQ(5~&Rm$Vq@ fR9I4%g0 9,yJwNȿ+Q}a'DB`\D1M,l _낢ځ؃ q4?Be($99́Z1zF/ xb+AUvSYÑ76y}3bd<0?8^\ 0F1ÎBAlCuPM!}lAaŮ#+>Ea=л+U 2j֎Mv. N&QރzÅp yNxξř'{ B|HO1Y {6 A[&S=$3?AhGi;(x&> <>qnmLuhni\; V"_K>4@BK=P,}憶S,ZEP^a3bS +O $=4( x8!@kir3Yة)2"Y$cg$eL@y _r 0s6B}s;H_ԟŞ$ u 3sbhcHU.Cf3-3#YU(@t<=_tʘOd rv`l*L1F;6\Hhy.>TϽ넍M/'I1FH99cyPZ[pl)шlD 9'(iCM39A,('vKSVNM[V>cAN"`u6 ?=0N ܯ1v0{n\@cMA+jjwIk7VNZHOu1aEr{"5/O̺۷sV]RڕS5+UjW h@ ۗ8e.|W]Ue:ͯ1ٴM*SRektKi+LZsZ-KUB3bh] }C__2r6o 5~,񬻁gt1n8k,<, )R ϧ\s7VV4J*y&XccW(H(V_MRxG _=joiv ѱ`m!c2c|b<1\qM}g9tabJΒkBY/ـ_c~ėcJC՗J,||Ul:@>$hb[!: Y ,`Bl{EJt N5*NV[ VfT]eT;cS UgHex7=hW$᠞~?-_Q}0 %Dm< <-ENaw6c0~$.(1"Ex479 :>./@;W\ZƤ{}X 1Eq+g~QX]T]ԨA1@p/9bkމVY)"%ʛkkkk>8'ğHoQ[֕ Pe˳ݖG>W16@WެOr:{`.?_>G f;Q)nʦdjቒ.;z3P1y]1^eL [ӚX^$X2q ߬<0aSEP0[L„؍G ) HK^V:0C~8mjh1󃭤pUOkuSO.XFbU`޼Q.}9ʯҩb1t꽳s?*'8g+Hy%0B%!\RZ㱳{Di $kB}^& cnq$',nńfd*lQzA8脄H"05.fKebCnTFNU\FDv6pehƣt<hѡRKMcl8^fٗfc'6XqX3qv1 ~_fm@mK!.i8 e:  K>!JVgs'Z}0aL}9݄H2A.zu=K7TnS Tgpx&hCfwJ ះi-5i{9N'Vf3v@;Z| 삂;G1R ]yȠ7砭Q ONi$ +;fo5LÂb>gH?]+U+r\,xfYPPy)0t_bɌO+qJ+ R}BCP+@ވU^2Fڇ/ z٪ŜE28zcU2N֑Ԣ4) 9=dٴ!oLB UlNKG{|KTCm(<􃘏[Y'D؀;]!x"lJLɈTN]IQ*k( h/U!vy6*0(T/1XI䊓0DMə)3:dw{@Di)OZchR|vF cuXs}L'Q DO2'#g}OuRzwmdgF,j9 Z eVGi`5ɨ{[{Wp>2ճk6j-m8n+{3}^iZύk Yet{s`M{#%$VKwn ̽OsԬ bAWIBu#@̞s)J[hep-̑]͔~BjWbO ~5c?}4qqmd%gWsG9+4"unFAJ Bǁ?paEp,r\u9s?^Y%5q?1rCZ*lO\HbwOT< gh &^2Ԝ40U2>涩^طkʽ<0nx>>Dfd.@߇G/VZ!lGR?l߄ݺ׸~?V2u1&5O/PG`㒏u:+JAf_ Rwl,o4 π.QrJʂl6CM1.CfMO詎Aٍ?Ysz]ѦUٹY6Ä.$8[v,%خt1"pL*;m\I/kO%];:)\gnqn폹c/a+fcF;ѕR@(rBn/e}bN2AtJp_a2iv|wx^3 є4>!aև۔SFt}&C늠@md.5s􎧍Tv62ua'%`%0)9(6UGubFB rbcfh^K&e㏄@f^['K= A msyS&4(:I$(P7'ܟǜ\±XX e~ַ|`p;)݂'Uq0U9+z}Ŗ( 0հmVMJ?{{ 0#5?xH !7uc@U70Ѝr6Ji>9MNڋ^J/bS% ^xfřub18z2B99qa«> ogZs[TuBf)?~v_t>r@ ԓDȵN9B_9R.wQ6ĺE6KC}Gc+UXOD ȼwTeEgWO ƛ%9,'j]wtv3-yOWb`{HDA+MeNHJa$/ϟzaIͼU܄@|Rtx ֐,eeg,vKa8{&v:R E_韫 0^f}Vڸ~vvr1KJM3Tʣ&$Z}& 2WOc#;"C&NQG8wX,.Ep͛KgFsq(V, UD#.&fL&LXd(t⪜$9F_|%,.8r=әo [ xTP:!g遈]jt5LYw=ᰰmZxCTz,|Y6*w1ƲYL3$9;d'MFGFE7grs14N\梤pX~ct7כ {Ri rBڸ"f4Fәd~4CwO zFhu?̴QE؛¼wl:jWG} BdнCJ]IE¬o%'b &9cs_,A[3{"@93"3GY aO x**hd k9D䀧(#ŔQ]#`.XQ!ٹAzF#)N(Az2Z$,+kעY܅'BLI*F{>&p ]%k}s*3~B WL@Gβt|q65tF73bpBU-8H؝ci[S3rp+^੶7J.{3anqt?ON~xa~pBi8 -v]x!)ǟ3R>0#uڋ,Z& æJ25G @SG]TnS[cV,藘(/~䇁qgt4H@ Mso4qm&WN}HUW*fMe{5Y@!.*V8HIArGRZV{; Ġ.!WxGFgAAO.lvG4n8dOp=9x_V9wfaѝy uD;qO_~=8:zy7$oG5Ai Y!^`Ca=vڟ3)($Y@}cv}^Hcqc/0N|z6Eg_wuWOciR07Lrg V1bs8/=xOY.IDzV<>f<ȦFk^-ێ.´vzEzAЍ (di<3?f,4#au!g sn4t$f #T5ITʶ`]G1r,{"=AQMG,[AlO"q 9K4m9Oێ\TVNTRjX\䈭K<5, bO[XieM_b]:׈5y}%& .k!@Dk:qE$t(@ "C䰙MD58:M16np $Pe$2Y0mX:)q[PAQ¡1j(k3?pIy~Wt\ laLn"y. : ;wxdda0w=Jz3eDı37xWc+'iҲڴ堄:Tu%s(8ADq(K!u3R$+BADf !7-K?Ƚk,mۙjI|d[F5PD/ +\rML7u;7$RɎ\y bBB"b(ۛuEC+OTl|`8G@I;yKoMTN7eWK. =zhcxbz5 u845H=UQ(l!gxDd|4Y34Z׺`ʼn]êC!* XIg (FvF7+#DkWk0/`&ʒ CjQd4 sH.U_Ew? La/+h.z<"igi`TwO_4k`T˪?n(A0g}K\q ܅S)9s`3dmu!F HM Bg`x$t6vIP{N o!H17KyI-=qbU FZ}r8 -ҿACi,-fթ|SpnBlh;ޡP. Kf Iv9o`/m8)i%r o5^\% )Ί"uN- -V0" ).AȺNmdD5bQӋXTQ$̲ CڛOيfxcNI"V2.Z} M*-axYLOob(?! 3~g `;3TY|v_x=s"}6MA=0p@PڃaXm=>*6YIR"״i*aG}OH?TRS%/i:"ΓDi1:DPR0=rcݦ)yqD\/9ቄ]Gl>V{enH+Kv+sԀRde>attQ*n?{LY)Yc'Pw,vݱ72oDO88nFAKn*SCUPPЌ&_ksy#>E+,yy\-ˋ;D 43Q:0C^~oQdUYrI ObAY-Pa@ ܢ'0 *k:5j:||Yk"jzh?zmBLz94ɴRӧ vƩNZQV@'zK%hFk?TG)¸ȳ_G34&{mZD*2D+0ޕJ1IenWT5C,$,SN#m_%5ͳJ5SJgZUboy4 H+ߞ6pOA*KI+en>L//.QQ 'geQ K> >"ERʜ؀nٺQOܓ]4Y=%mC[*q(mhg>֬`j:Y|qLj^qd6<[K/*ϗ̳3_uKRڒgYyCFnQDATэ.En/ ~.ܚ-M`:>,}5jd0xטGBO.]a3r#Tx;LnMџ/~,huxQiZ]`.!xx0 B̩pM{$JS5_;rAݽA0L(0>,Q9@Ek$`.Yͩ K`{3/wVL)[a].\Ez b)݁wŹ>t)WqA7oNe}!1͏HOPb?(x\+V՛N9xw ;G}O>ՄDU0_O~0NQհ hy֖l:]ƹSODWFIZ. ga#q,V%4K$" ˼l{~PjSU7i)ZBPw]e~'K+/۫SpCU˒-^BfOOgnZAt?i&so@>U]p3T$d OU'OKI>^^tm_Q%lPF&q0)='łLDHI& Ix,i {%nZ:XK->r8){8&G+;B;XE ѷ2WAij[`2ȝmG,PCRґ7N/ӎ@B՟P!M<딜P>t"T9Gf|=ۯ" 8TeiGEF H <'+ #B[P;`";k/{RaH^LevzQ˺ - qH8 "e$qZo7a.4B R'Rw1 ׊iF"Гa>:O 8 wu6#<ƴ?A*;{^osu:% Hڍqƒ"y(!9rPw}Kf5Wq]r3sܠ* :\w\؋FIdn>옿VaGпm; w j?*a(6`N!@4bb;ANܩ D0s2w%hmH0K)r{p2NT{l4-b:9KNj!f :]ȥ0c8B=z#H-#X1` z"A0c9`J␴RL0~taK̳eryW34Ëҧ8}t3t#i.<0qK[[Ԅ.) [_5 Ќpp,9ez!a\g ]g߆{n?MKW-aY}(KTT?WrIteϕj:+D͕Rok݊q,gNe5Whq89L|t)%;{ػ2.{+;-wKnM%0ňՑ*[0/LkF,lF,0[sцw~*.񇃣.v)Az GW$*{nUmz6ɭA(ș^ ),أ>ߐD1Qɐ C.8P;MMa")KY(y= }R) B&rSJ# j?Kt0"??J'y쐠H,wR+G K8:>|bR7΁bB*Ӊ)`ndLaԕhjh VC~[JaXp-h;RPD%}$(<$!€s"D>C (uqWl>o{ޜypBYFWYQ&F(E1`;%\yߜںmU[lx"״A,yyc[4c4">-:e˖ H:wW?5Wӿi~f38k6/˶ ](r }YN9:=L4`Mz0i~@x)hefVM*Tjogc*VL_BT}_T9ޣNJB^޵FzP3 OK>&z*B9JiZԜBN ]%J8 ⴗJe[e*yzk3l&͵,)H3İaH"g gcf`Qr;d"q{Dhl<.綂)"* z+ o,]]ϳ 8JpF>Ko?DwZL(,q7(WmW=C/!/r&V#0U$!II0;j\0a8 1iœ / UTa$ҠV²+rr>c)GqB$zY)JQWt;IHslpix͔^D#{K5Ue,;0¯9^^?>;0P PYKR":m ~z@!T]r[z=^)C Ǭ-݊.Mԁ'A(Ƶ`{anr0 $:&& ]̗''a9&lªO:<**!QwQSNXÅ*;SUDO ikP|G%]a)N(-n|)\3xyaM\tƗc&F:0\*cja |H$hI>XG(7(Ox'/42\|J|wqWQKMosKy&edW'\)O(c]sdGe6h dMFGQ/,nN0`5l^J l]nɘu+F|f@W{i]5 XoS;[yX{Z-G(РRLUi5g+%!M3jz>)w4\csH樫)l?)܊?)a]?b oy\\? G0_W퓧}_ݝ{INUv_Ws㾋iwXs{͖s/oBcHunC_G[Wu]B:?ޥ7nnlnonoݍy?Dfh犢?\]]2MK>Jp s wG{@6onomE;[[_ՋH-UO~Do}gs4v m>Cr>N3zړf/u:!vQ_"c|Fb:Uckc.4wcz&$ĉiz\M#iRUE/JA2Ua a촳qɈa56[%3N&Ah8 D6"cE1mԠS,p1{ A>5~dX/:a/Weꖏ} 3n,PU|8˅P(ŚB(wj[a~#QO к`S]e 2]u%eʔ =fRt9&ǜ&0c0L؍>y8FL@/ԁП4h \ғs-dNDAvF7F@:S?':\3&i|^i7@{?2 c< t@3ZO=l<Q~ƞsN;n,zQvq!}L&@+sh;N}hP0 ?/~(Xm^F`gp;jEܣ:{[߰<߿ȼpE~ɟ%E:?:^ɳLJ>Ho]xTyڥƕ8/zws$kRc՛=Gy,9FI/??<|כ+bymk."gwG/WjBQ2pri'6V|UXWx2_dw-7:~JBO|F= Qwa9jjYE4˿[]ʟQy?Gnܿmܿw66mܯ?OhqOvhj}cs}hsswƽ";b#?cMN))( B|{t3ESMZ(QHۻ/9K>3ܩx,v";$^=hkIG+LPS᐀G~HYUBpmU@kP.hc# \^"2*d((_<`gAym~_V֢mREýV8%iQ:&a*ܴ0d0gWd̺L׌әJ0}y٠oؘp9<0{}<J;z䇃/^%04 >`.y#W$qȼ-af獕6[mMQKsr6!~`@"'WB0z\{$WW q#Z4?>u;Y8z 0W]Qs{ĵ^}mVC$5Z|>׆1 wuQP 4[j!F\ñjS?OHmʰc?9kҧ针B*!ׄUw1oGޗi hᰞG#R2o[,ˈP쌱vr=AL;./uK727ݚ:(80oXcg厭<\`zNbX48GPAcNܒLt}Kn"qoQ'ionBLv.!X%Qplt=fRʮΡejaz|Sb*Pטc4>An0RKc @qu89A;v6R+,NSn sCM87 -~St>NԺIf== rKGLYȞ+@Z SbC1)x\M4{b۲ >Av0/1>2sBxM#JhGiKXkbx ď֕ Ow?Wɵ #ϯY#hzL7sK]P,mؗ4|J=)J6(DY#3UVde#’󎃾0 kJ-kAXqBp?yoHу+Hl<]!+h=Z\fa(L65&tgA'0+.gBg.|H0_Ɗ[57mx G׉#-Fw:Ep&bK0S^< UՏrb8&7T Pt20GrJ ]P!N/.U+ZƝazFJ5N(xi4||: QQRY4<Z )nh{񆠈$e;D?cFE^qdQ q\XNjՓ46z#40`NyNJW8E$Wevm1͏דC{\QY4״,b BqqΪáo/)x_G?Aht1a цwbuos{mܻ{;5=~B_ }F_٨9 bQ-_U9:^2QUC)&F"oKpES(X0)9a.Pdi+wŠt7*E?OGd0HI|S 9j@Ct1G8kFb&mVu10VƊ FBKfyuQ?Jm6C=k˿]ʜpKVk(wieHGlȽ+n-Qg1gx_JfB5pFB.njt<"|e`Q)o/R\ͥH \t~t{b`[ ͸sfJb%O$ ᡜ& sبpq hi/G ̂F^\iWNLjo1#\SKI{#i5TT$F{'du+1-ېyrty>1,fq!驪TUOb?2 ۣT\4{ة`xB+#n" 'J︚ qhߥ [5X+hx SI越禫#6(ߖ /}+nԽzƈ|UB-rzZ(~?A7XkATlpL;ESCl3tI~Ѩ;bp9ֺ4߯\ɇt8x@Ppo6=_ʓ3M87[lC~ڐǜ&\xpK ~$pĞcStlt-5/\_J9IT[gW`{xVvtsF#@9OߣŢ;>?4MҗKFM8( yR?gD!~FFKzZ]AvU)jn,Yk:ty*:݇jG,XY#en6^w"n$\pBdoaO|Kbl&)E {5 n[I!L,ύ#\BN #jXe =J\xNB&]豄|Tu}u4bc ]q< X\ƅCWl緰t.a9>K|eńjQZTr/״_@OW _μÔh$ykyz y) lĺ>8ML0m9 Sޓ.[> I@f[wR_r`b1Ћ&}D>2\ ]-N\ׯLG.1Hp.Nt)Ixͯ$ݑX"s Z]L-tlV*\mː/ӛmߏi=URݝ]\ kTGw`5Dwao\{zȚw+""ю+Z3t+caָG$GZ#ApoFzDݩ]; @bTRY/ީ6P~He~BzI4M'?k1G1Sm߶&G׉$x٭,X*@ ;{k6P{ޖM9P1̩-nY\keKN;O(_ڬmDcD^`]eX-B?9WŇ킹]G BUM-[UUE< " v|= {xϧ~\^Tg#_{,lxf[d-w S]~C $m\vOĻ/b ozn22ot1sr?{-tʾڣ~:Y%8a`:)w'ݮ`iԆY =}&hѳ.VMx事W&4>QFX|AxһeoKX]PY$K ṟLRvNT7b͸ )|^_fOʛ~R@˕PA 3QoYo^pIr  G¾8p6{Paњ#P^3Էq轺Z쟩  s/}7,䁏ZYPhN H]kt*g j_|55&XA35IIT(Bd`BB": OoS %[i#R?s?ϭ͝?lnnlnoo W;NYj=՟ 4k ĊM+p2dUcoZt]HN9VUtG V725o+7"J#w?)<HG9y' F4+B:+mOݣp^o(WQN-x%:Ja+a!:L=bKϴ"V`+W' n-l H6n>=zjYۗ/OO_ua^?>hy\O#›P2RM =NgS7+*Nܪa\]|*.=,בj̼y"8D(s QÑO:+9 ︲&BSl)7x۰nټvata,WIqx3 kl׭&F2匲#] G; Xwס]EH:XcNb?%B,ٍub+l!{O23uH z"8Ź8E|u6WXA266IxB׆+WQ( CTͽvuT_G|I6T-먭W[=q #)|KE0Vѓ$5ѻ6lZr@9K4Hy˶*\*XZJ۲=tF<D1\_a'qYkMӲ4Klm%\]O8_+Hs֠2DX!B}=_/Z'wq&/7pwâUl"`:5Ix[homU%biC'z,GkP!I.>1 ig5R.BAJvZ^)r"^V}cTp)6k&3&PZ5&Xemx(φMs` =2 2!) 9D2>0F)-= 1գc 50\$Uy,뜌(DsJkjKlrO(:2}Lԃ`ؗ)DYElVat~2u.`(ileUQ89U0edF9Ū`j潭E3d#J:V{5fU4!$AD` pFENV}ۃyhOy[ײltDž!+raRṼ0#bg#8IC@P' ^j/3}p|1r'2 =}r":kQv;#ԡ {7g\.װ$կޜE;&SO PX]V׋H"XeZ5`8*&¤McGz`m.6v;h>d6z5 R/7bu2xYxm,pd쪪ƮZJp r8;@$muٹl ܈:BgQ^ӼSM&A`u! R&s\QiA%H\}ލ-%&9r :҉v[4.4@ZMKy:U$fjZ z~,Saa6gɈlYgs\nmT1+T1+|Уwz;9 pbvJ0"vĞȴŁKץ(VczpM.Itl1ʇy/2҆O!f\3zyϔt(dzi@ Au>Bgrz5UB)+٨0plM9F/~"_YMաtrkoa`Tgݗ_΢[- GL>LQO򯙐Mg i;=tf#-8I Յېr"[M!6"!mqY# HEU!t8=j '\p."$]OqXEÖ_υURa+vnUw.ebtTm9x N#uJh3\ =3R[ћ9r"v\7ZUS,Q_t^(dG됥`l  oOؘT᳗mfޭIދ O+"Y oq%vżk-}73AڏM(2Ӽ>!hUe;RБV}EtH},pǴ;̟jE M]PwW2()'3!2YG-nZxQcge*s?m0/ryNIB7GTGOcz36]X/!ĖMoҍ{)u5LޤƀGU6Yb"ksЩ^:GJ#k-Txc]iV9h%M{_{p wQ{jft<> 05U>}`Ģ8GCLjyevKn6{Ł 6ל%qRzDZ0淮bAPlZmӒ8ٔoH ɉ D1} ~olw$%Ic=kLOU ʍGU& [JOE_\ӥ- Cwp&J]Ê5@9 ƺOUnةAҹ&LbAkLԟk@~5`~ @0 L_cwT7;HAFr̸ oD.Ioq}1x"&FDJ,r[0JtPMO%kku ,n$?=27@Kx XnO6Y)#~ŬI} 04DyBqa(4tH-by~JT֏i9l`Wl]KQeL|icvv@<(?ʨ/N~srǭY_@IXL%*q§O7xHjr خ&.syL]7g Yg[+U݋<kh%uz]RݝGGƷ! m՘3Fq1ՙoz)bvPu7R }6^5j^J>=Mm QӻMqtz=3!`ۅ Hu1Oͣu4(DlD+\)3<~emx-^S@$2Vf|K. :+:և0I< @B,p(l>|8H++4nvu]TА4]pKJJ)Jtd/ V9,!<3ȅ@fy_ yJUꦿ=Z֡+bpb$EG'F;E54bY8x!Z[Ãq: n%TgpGn*u%u/ɚU/vdpG]]L{B$͎E+Ƞ Fs`y <ˎw0,_7>V<i|4)9iMard%Enh}+d.P2J_P qF  ظSmmTloExx.g% 㭦<F.suqtL)&s6s\a-^Elqk*ODu_ _罻AFK(CZ PLVCwCΡ|CG F*q%A_Pz _9 VyBaGsdG)#fN@m `T#hJ   9c_CfeTdz<\ۀ*9<;bSOeP~ AG\ݱ/pL$L!1Bn8~1 EcܚoUC%ߗJ`q""{zfR.{7QH:vNkg 8jŸuݠ+- 㠪)x~:޻fcg<.i@|^8`juߙ\HjFe]H>Vg&h27ޟݞ<lr@6Y~\ >Zd^t&< '+U"VU `SMlK-oljg%O;4$ &;i;4RjF:B§1:)Mv0~ۊu mY6 Lހ@A]Yp5mVKϊ.|(folܴ14i>Jb;W& E5Bۅ%0T[:T1}B Q> ;AG\McP0=(jM8~(aZ|>SVYnӁ\[fBys`tCfCo[$L_>]ښmV |5zLp'pX7N6a`PZ]A#Nq`é&@D٫O\,)SHnFDAweQ#[p%vѸp 74" s!7X4$(LM:H5bLLКh^H爬E/<fܚx: 7_# KO0nFe`M~n|p`װ̓<ï~Ϩy4FtƠ9^_q~.e-ˋJ)4  jXV!>^G5E ځ42t#(Э^Pp[KLЖA\|X&@P$3@=q x5W{ʻ)`ϜʭVuTN7iU06§dKj̘+)X+&;rbjй /64$sr2btǔsRE1 _욍'xuxijFT/{Y2u t [{#U8׶/"AjYiCiӕ!}%ݹ4MeȾ)T0{ڽQ6;;w՚N}vtbl@m[nb ԭJ'K,i mG7mdh+h-C͠F3vEňXh#Pҝ[GŖVeJ\ux9N\|ӥ h(YBÛAΖn6pVm1VC3RuDG5#"t_rլO<.խ$~<@Mc>mM+o.R]qι98?X"\s)Ƴd ; sϞ=aUhM`{||9w:'] a'o:sνӹÿwp8snh%O78fO;Ԙ%M0%`aRF;Ð) 񏮱OZh|M5?ܱt(`gA3Y2 ă} q|ܽzO/h't@ZkK2yiF#oYp6)K: mPN )E߲9&JIb"T Qpx !6-]bgo\nnN껱S_>*$VR4:UwŘ*NNhYB8wccPB5l7[k/-ԵCPzQaU%$OҸ.-Ϭr{*'0g\/֚X)BС e9dh8c!1@1Zo:btX @[cZb]dG):"oNơ׹ӯ37 ka@C[e_9I";]u 0fAkuI@뗜e7FedoM35~b>^[\{|L-+n̗rA*N)ԣT@|99|80-Epr"oRQ{ڡo51y\u;e5l 8rB'3,hʕˈL% eM59PBVN]2oAof1 l@ZƤXw}#[RXkxuzD#=tߝxj8|k>Ud< 1e-Dő)u޺0lI#},<%T[UѾ w~QсgcLT#S{-&Yy3 kGݑV!.օ{Fʊ|j3hǤxތzk-sGN[b:2+97HklI r˅V{@+7j%ҸȦz5|(oޫ^'o.¡UZMBDXG>y4k/_kK:tXq@).őkL|~Djr` #CVq T%^쓀51:V!JFuڿŧςD/;幪  l݌hhN>Q6r-XqeWp]IAB&"@,dHVjj5r8Vh SQ.VТv!Vx5鸜ycVo|#ixb'{f6:Ee9b#bagn9f V<#z̾CM( ՙ|Hw9/MZVL{x}TxGٗyl,Vjz6.bGrD?Mf8)4HnBeҗP(E ul}ikhFp3DNw* tZY2GmuXJw*iQø"=-҈{$dL| ]8du,G.|ܐ>â/cou \"=v]e ;"RTe*VHS ت9n7Z+J 򑚊\:+gn 8]~NpKpWwKhuBZkFzIPCx]|{~lHer}|JQ[c5v_Gޗ1 I: {;ÏI1I+ܝܿڻB 57MtNZz 3 Ϡ?q_ =%v!R/ddЧp!zqNPQr/UQuwPrdS1s:de▴kXFP^{ת)!X1h|TCPGnz7+ ʣӌD9om em7Zq!NRͲU"sm}]-F㥡_a_{&ɏpJZ4"WCa񽆄F ^d.PH7QknBʓC ; Xӌkutd'6[44ހO{N {k_cw0l`X[mƎ.?=yȇTк@oG ԻDž rEF&}xZ!3aӍ_54O.ɘ&G>RL6="d[ZxxS %4؊_"0~ }>q#\]㗗l{2tpe^q;WB[quҌh[yRZLB}o8^JVJY,Qhx}"4ǵ-84b+N!pӪZsw/iƦbQQ`)JhPURvq;|fKܦ(PIV.sN`r87v&] )jQ>*D6tpzG5yJ*(9؛i,0L#@B#orUk~40VTcB{^ vy8-!a(Hlf lkr*ȼ-l>mܽE?lSOժ#nlr".* T0 >=D;P*r2m ^IO$A&*' y)!a76:i$&$.رqqC5aB4={㢬 YM=hI]^@nrۦBj`yB[I?J}㮦%[0|||\LQ#pUuE&Μn?w;A 0 )f6ѯgL4,+ͺf95Dzz\^t svR`mq5b&gsq|q^L`O 8Nc핻;dM$d/s~v`'YwX<_8:*Ѡ[>0WUY%9wk0 C‚]1;=/d3Vyy v5'Nmc03 *{/vfͧrR-kbI |}kh54n'#~`8"<2؇*۬>|p\T)CPl@>_oP5ʆq,Bq9fh;5y!ܝ/Jz@Zr40 9'CeB+B=c֝ R<爹o`M}:,m;"]weh&5VeY b)V-W?bNDkk4-?Vo3qp 0jm#{5nX+:3Aȑ}F}I5r9mBM2KOb+ ehX"A-MxycFFJ pB@mҀtA>m(SiUM6iX]K \#PXz shrcJ]{}&(#N7. >Gwfk@ 8~POlNU*ϚYKBq4ߨ?y8R>Q;|3[Qdkё I9ŀ>uliS9G }ɉ1O1Xrxe.IYyq6?>vPˏޕƍ~]إ| ˼@.RCzZ  zT&\A$  Jq/@ 3ƨ Vq0m#]>| oNh-$u;O==Г &?VdIk)N7 F_Μwd܁t/6ji XtL'B忡UE@=V6T_Ϫ;eLY&]drCƈzOJ *ou nt2j)ݏ,5_S^ qt=t)b뢵 +AmӉn}\MUWC{[ϳ"nO;%֦sq^9 'GKd*8Pw'Q`ٶ> 6Yɕ rTsە}k)9ÀU)dh 2 ]=?3Ȗj{ b $i(m#>0ԇq0õ;3꺠h\Ak3:jd? U-<ϗ5 V<' ʯx=MЄ94lS:eZL^s^-D54Fۨ$ro˒V6 {5x_6>'>~}}'Ċ83_Œ՗R3)6lFrCFo4=T%~S7]>>^fnƘ|\ޠ_z"Eh (e / 3ó/3~;Ywg˭XE8iuĬ8̧h4P 2vI1QXmU28 gP޸+YiLm@$ZHx. :I +iec]\T,pcX +(`R#0KdXRzu|خ{}k5EPоXYyx:)C4bDgϦ|NV)FK RY)- x>qT䂚P+búa|nn^\\ चl>nno#-E.BT 3lncY="yt%^`E⬋Y|.Q|nzV~ w |)1L,ԦKDb >r''jn^Ǣ&T+{cibj|r=H7obALEL0sQ8Vo?8@]a MkFQEVNCַ1ih}&;Z6q"=7Bw$>JUP}=5ʟzbz?dUm3{*=aYܟFMO|2}MU7Bm}>qw  @J*zs1ցu$ >B^ԴlΑpuؗN&Gט*5%aE-1}/E1*;5RԈ:2]$uͱNjӊ<+ϋ+  ,<@F23 Eticw8R' W)=AD KO+NZ8ruCk $cNɨ6({q}ub.2 +oCWEsGݥQʸ!_`?GUd'(69+~7+<;Q{S/܈@[D 1>ƀ+{r9k\FN豅H,u7I8FEW!XªtqHWX1zd{ge5y 0Cv. G'cjbnyO5T77GRvS)'TP5[}l;;;;Ν;wv>7[;;[;ɶ~i[:9ϲ\\\l]?߽/ҁGݬuovͭͭ//vw>Ϊ<;x7}, "5Ys,^,. vNRWk pdx`%ot =%hXuތ'/7%dL1qb7>1i "D,pR[J3'E}ϸZ=*_cd?HXľwG2 a+5HJ-eQNxr vfnS+x;_8OuE+rZjEs2?.BI"(>[̏7mQvswf;Acf~|8H*TS(@p΂i=vygt1j B ?%⤜l me:d;*#icu+EPL.R0܂Yc).a@8VT+&F<]N2huwSfIR^ӂ #yF]XRB2tx'T%){a2!E]:pk~=^yۃ?=g&LH$9|gk 'ħHڷ"gEs=Ic4)? % OkR/QT2w/KfC0R(ޓ*R ɤ7 `zu^zypP&_Ch>lk{k+{xpx"{yp}Law^d_?>6XڏYWf,P;HIo>cHWAeh"2s~ k,*! pq>A=Q#<i{sR6x݋K>MX [+0!9o$+p0GmjhG4>4s.C{:>25k01 űfZ,R/=QaTlw9-]=5zK ҟV{O9tZ.d*4ފk #1V,wOܧ.Oo[SήNA$"q1̉of߇ec'F,#{ ݇9})UeVP*0*Mx 8iu"Nq+_'Ѣ5@=c/p{n&QD$/ͥ>tM?tv=^d^p/zNK/Ƀsڂ0R5 LrD`FXշ#JhٹeyPf,PJb@JW&8,!?Ƨ\Κk緲RlpCiȁBD )RbIIgN@y˰/W6ȘZLQCih\DxFD( p?)%K'JS]~f -$} bNq+Qr\ >O 0QXdl 1@ ,W'=wpԝWcOp}fϭ#@ScrOj}HIGshF@w0i<0 Bm]2濙eOW c*t\d^XuɦIԕD<WهJ hD@%Gm|Ha@\9ϡ/@M 6܀Zc^{jqt(`Q='W4r9}=9x'f?{mQbu8j$kN:XPI6ѡU4[:t6HMU\J,-j"ƅ3?qz *Du?h=R A1)՛4>e).G[KP/{<^.LU2p\K03s"(/KHn=.? D3?K(`V,ܰ{SC@ٲ_YgMgjN.:U凷AՃg4I@IQ+cMWGQO~}dcwXW Dd$)k^=q OUǦؙ/։ u>z0+B9oc\ -a|'fĤ&<9H>k?e'O)Hs6(WxLpm)Q!fttZ|];yw^%ŘG n_@p݃{2cMGO?{qp+,Z35U<rqI 蠊2,m^[c6_2\&o8ܱ|qiQ|ʟ~'j(@`daȾ'/}lc'sD'UH:,i0>YЯw5N3:!(79`SHm}u¡5Ŭ{&қ0WqZA$|@Ӏ&`W/}cGQu7b:2$.)j]i}"I%t9k3:D8N QH)6(. 5}Z/1cEfroV]ptXԓטVa5dJr &4a馎&zA)sreJU 2R}ְ;C|Ji6Ζs,9.$-M˴&f|\,Ple9'5! 7H@)3 ꗴFg7=ͽmטĴ:l4jYzOf>\ /kRnU⋤Mv VpAVjA p_g2h@TXcʩ;? u7tk[>z zܐc=IhGW:f_=}yal-?}/N?,;:{w!gw>l7[۟oݹk//ͧx ͝l{{XkZ6mgߋ.4/!Dh(G":R&/Na}\P=7y~>Ռ|2-LxR;#p䍟/:/(oQ廷J?vk_/7kO CUA_k i5$THYB+7bԾҢB E&(?8Hd-SZ^mN햟SbhR?@ōItdzV g՛@hךA>" L׼-kPL/a0!^bLW.Y-ןSgH$88I$H+ƺ4;N/1lOZpcdINQdFWGJm!孧#V+PwvtT:0˺G|V”Ǣ,;$hM Oljib^k*}qs"D1'L9`EMs=#=ŭ#Qk-lh`Ik `Pt70 `ؓv6;Zح={f}P_m!{NVC=%)w`'LH(dKH)0kd\[\?Baz!_R瑯 u`XǺxWP`T; *]Oqm ͣ5MO-&.^{!s8P`>M; K%y|sD٬uq yo݇]|n6z=:jD}'pmܷλiS+ 1l{k˄-{2D'N>bC!*ar$)Oi̠_3lvoEq5.y>Yzu8#}XԼQU>Xq[Z3!:"bnxh#s$m"u0qfG/ܐ=bBX&d3 /qΤ MSD1JQH[%2⵹@Q4Pk^@"J*F"*5AU"X> "aji"*`dkdM%9HMg gernPu!ZSo_0.I VA~^O x)y膫ӭ|M4y\@,XdunO-05l!1\%uºm;Gyf4L*M+ot7oWQ΀cߘsˊ_؇Bt2TJ2Sa{'5QDiŸ pDf!2,7-q.g$ߛ]hhs3v\GuNףPOg2v[T P+>1 DG35ʡk [/;aͰ2ua&.ɗ鸪٦k"uW)QPW4wMͽKpL=nۈï)Tt|z>E~fOtUDkjP1t9y7.v$ICàsr8.09i= eORjMTU>&:#}R%kٝSИ L,2 \0b["8_R+{Zas--W/1^cMܹ~yDMf 3P Z4c"l|Maj9睂A\hS\' Txtԃ >㤨HM2Zb~ljW<{d=`#fk؁<~nmpQT Vc*gX &oEYPH"jL90Rlr;}f0P,L2;M-nhj9nj5bF\?~gYI#$cPK4D;$g$2埍cMRkB#V޹ÃUnUkLnCqEw C-fEAlzvP8p8U7L oMw.qF{*VdX DK |u4 '&TL!݋|ޯM2nXt쿆%k糓ggoٺ^ cgg;o[q$ ī0 |5Am9n~Id$0zxgR0wKXWWԅh4Bj 26,۠OX ^;ڂw4] 7ԨntYM5tsv'{_=,-} F;R-pĜ^&P\rzP]cчA$F-Uh3PmJpکKḦ́(3M *k(1?i4f@b+`]eBfH|WgW ιmPe%҄uYlϖVw!w<%gmCi6@촏)@1@Rz_)-μL,ǿIwCmDT8/ t#_ X2_HiXr諕Yx|\1[q_w߽>nPJ˗;_^Bj~:#ob6Ȩj4$ 3D#v܍8rQGd% oYt;JyON4#nJt"Q q1]{ ڵISL izpv7n|sB~ SOO;R  9@ٹb8p^2x%/dא {A:xD`6lWmB}m9z;8UÍurBkLNgBp!(|Z p,nݣI(ꙻ6=>BQ7c*aGYPJAp hTUj &JHjE.9Ma%\P",dT(u;s}y5&Kp̝e N/|!U1LC|3\v+#OI"AA;irDd&F('>k-?ʽ QOreK;s[s.i{+o+nBK#$:SG1G!M~ Y n3(7C/?񈗼Io?O|Yc@_YX`F8YVtDt=.Gbqޛi[QUsT(L:N%QtTsN?G/3P MsU`%<ˌY@6KO#OyՃ786yS6w~/V-Blt EMOK$T9[x #i9m|ѓhavsDQ_Ŭ;g`Z,zs~z2@C@z0;hgkk{D"ڔlMP{ȡ\q 4nzt_͑Wi$.+,+]Q"Q8Jbu}G 9<=JqϽ:vc԰Okr9kN`Y(BRQ {דYx-96^כ6V<mx8u} 꾛Є0kh@sb_i5a_o9->g;_AۿEf>ݭ;[I߿GPH#_ЕLCΈ=Y wf9#n<{x0uW5'!>^)L\%9by~d(3rlWK\/ T-)kF6u tUik\рo&'H WlKYSrWT̔rϥM7q? J5g*9;XFhj+{i=fWE*Ps '8nm3yReWЃl\96jQw?ѣ tOŕBpp 9dK|(p+>B/f-Ns sOvL9Vp -$L? `Dg9l8oy\ߝ%1 JP()Nx @xsbd<^a6MjJSQr>}ƹ y&jtY0V8(A xJx _s{r>^="f~_  0W Q9ű14i3LqzJOLckad0ԋq W9 ]r"zԎA>-ϖ4O^rZ"ޑVT 젡s) 7goo\+&㼷^EsmD֕imk-WА]r],wj+]L~LOM.'\hoҞgnh1YwW ZOm\n'Pizc/; #M!8(L!zc)Oٲ}p&6?)SN#)B͋\aF~1NcykHLk 8xrp1eT8Ɉ}`S崐HzTGn҉L7\%Y:{S+#k'o59m )<9%}wЏ-bs?=yaJz<w1j̹H;4uܑS^ynR@ozmSw}YFs[C01vbp}РҩdgjvQH [{ reJN?P &u ,ˌ7(FlY ]CzLN8AULhMx'\DXD.MDNG$5RD|z/cp;FZh| 'c23FN:.n*R!~!PG0ɺC$MH'J!Lf$^;ջvZ?mcP)}K  T2Qq294pj\a*8ïA@yn{GS=O{k؛ƴZך^qd$*9%*r|)i;PkYoIYr&GȲWA.{2@:yE5t=^61Nd46]p=ǔٮ{[a$ԩ-a>>dދy=3FsðϞ>ztx:_cLCLINa f7F2xaR;=u`H Ȣ;y*%`ucyX(,h*4sT t668O%ָJդB9Rt|aM\؁<9'֡O.`%FFB>/_Ʊ }jȈP?%I g\2 8dKky0T( īiZq /FZv7 ^;"\֝3^:A4#ORx hиq i*`Mz"i5Dx8O${{ Vul|vvg[~^ש[_4QzmoQ?~4wX{z^v_q…[ $f2Khd:bž^Bu>@UTI[AzhsɤOcՌR@U^k{ga]R!o.0z[`E3TU*ZyFal$q XKSE[dKxihr kNgƍ l 8a! ,kS[uѓMtD񌳢=āX }j8Wo 5=)8.',^ @)//_9zlvrη[Qdj>g.9fH`߯1w񆻑U5=h 9Y.689_N߰?Np{_qP*'}X=Jya|isj|!١⚆I=MsY1q^]ګyD;tF./lmzM`3N`XH!(~;nL 84q.|qGR揱NyD*{εSY.uܻ6qR K? HQ]. ־{5]O+ ozĒ/ɖ6/']2!A%i[ގH?U *!PB:IrԠ .ZzZ $ՠ3`R͌d(^}=˗??_?_~s^՟vsM{asV<2eh^z |T %9n^ vv+_gŻYF1Ol{7{4 (z^fNbZx|ݣw"-Ӿv^cH5',PW ;?-ݽ0bmIۦ>VUUddf-3$>"HBB=XbЖ%b9 r>gKZkblFF\~[vA'z;^]#KY>}{kX 2'}P]QA.f3w?Ap\<֑;9?=-u"/F2X%-\:zA$cgnzGey&jj}nmEYl- Kq9IOcb.@<=|tॄri3Րgf/ F|z}&];Y+w,b^uqπ:|oyt&}S^*I! Z /.ƐNorDh@`@-.J.t˞|[p&8I2BU4+QpϹV:"\ O=~Kޝ\B~oh*[qʹxx5E߾$^sׅP]o٘-R@,F{1jhpتvOg/)x",֧#t%yym Y ^b^bnjߧ.+g^p ?qPoӻgAfa;7ƩhV븿IoN=ژl`f12w-q4Lv:˞Hb$zkEÄy&gnܩ@?ʱ9 g=41_EZ-kԟ0u;'@NX2?zWTͶ"iz\ QN-e2 +zs պ{Pbjh=ۖgQg[~z;x*&e)b's 1pBb=V}Ws?Hr3p-m D?Y7ߠLA1aXm`lP2ʎxlӤ02 ?}ЍC|``\_/e[4u6:acV+ȶ Ecw{xJt>ͶYTSv *Ac6\W}cborblٚ4ʢ4>]ہDPŧ2'4x,aCy=XE8ueЃ ^L&i)'P'rewv g_Ͼ_ܯ_/\+mcwn90JT??"/|Z pi3@e~1*NPIoG) Ss3i昷%@0 aՌ|W3KȘF4+(h*.N9{<CV%|b(ׄ$ ǢmQ:,F@G]d3 Sc>5PyƏ}ASʩн5%7_tfv/'5ǥ={'&kR0/%>J®j AY=AiĐ~v6-98C%8ո)\VAN آgj~ 3qu5xJS|Nif|pN\CR)2]! G*E7ᡐ]8{bP)Ad۔T=D7sS@-v~y\XŌ%yx:^Მ4v"owiqrh쬊|CYeF̉7^hQm89Ǩذ؞V;{8 0}_ "6WO)p1w; t^ {سιx V2:[Gè]zBS>[lbCvZԺmܧ |;^n$P66|zƐxM:S̃‘2!A-)WDw9ydÓRH"P PSi=$'ρjհ}ni b&r l>~:$:o <`ý5[+ee-tbɅ2heF>ֵ%TAGm/p&גGxWW}`% k~ul=j0\d)Ӎ%HǪr6erx-v]j[zm/k׈>rK>ta Hf[XP@WԮwNBi[hiN =n|bй\(j f6&I@IT ]\`}G_b*1$,!|%k!Ր4 t6x=cl|%PdFSי#IVnmCs72I 'es@$F$ 4 $Es"HHVe`@2AAw[S0_óI{1Eſý۬U|qϒ& 0sћӮ.EQ8 퉖0ؑlt#,n]A'IM(hϦ}:X͇8sfop\v #K>aè8ѿ׆~ݓqu$枌--4Pp?q6'?Ν$Rўtj_AxfV/rhQb/rsoQrl#2:~8m)) o+ 9⧹fhZWo\xdJLNu9 1!=S>=b ]{!;C*9F57!P}ɺp/?e+{viM_!~3~cv0żko_i#)B?oΝΝ_~|tl37?Ns{>YJϋ@ *\D93Q}C8%bꋨu$tM!̡z/1-vp~P}CᏆT;3so`g1gO$ƌǁj4UAC󽂑\PAj 0uRivD|Ϟ?z*pׁ,ȯ%sy(p96*ЮD'$Ei'iwׇ/^Q20 {I,ơ3.pnB>=wTt\NdՇrlEtGsNa /\. Q](Dp8ǼhE13947W f6oȍ%xz>Ri`ZFB0/wjq9{?s﷖ Eakºrs:0;'P7rܥ Ts^@,߃hmɏzT@%&ⳣ4 {jM&ҷZCљ0Sڥ4 Xk+FD,@~=#=.Y1Ҝ3b`7,?ǣ-grX""UٽZ u5^CPD$seOq|M]1T u) '[ʭZA*)04\Ye94PpuZq0(U2?9h@?mJW ʯyg4|o;_|__ͧɶ @U@N2~M`&g[ l6.i߉Jb&( %z$))韁yȡA %z븽ـG;p> .{e?̗ !L؉ɾ)(` *=`X"p:SS$KJ^8E̎BnӸx>jKr-=n#7&PJ7^uD܃Mz>+ 1PZY2ϚwkTֳ1X(:uq@BPsJO.eqoî=}'A%vtJK !hy"uF ' IT]˓" x!HN79b FhmV<AR( e^8'ֱvsɋstB~2 *}ّъwB NYvG2atb ;A@j,;spaE -!|qIc… pwk; `6,A@dKLC-!i7*ԼF9)o:LȧRPnuRE臜Tpi@&؀6Gf 5R5`@a(u|)%ٲt҃ju8 1A7Y%P,G%`~"ѵK!Z]ŨhaS)XG` ^U&ܭ^%am%׋Dy1Ig>FT NPtcZ1cCm0#]Wڗ/ /_:Do? ƨmPKۑʯn-7d 9>M c'S. ʸfDŽUؐBFx^X5֊ y1 o,^Ab0wV[¹+bWXg/%Oh HqV&8DOY>K].XBR )2/";T;B2ՏYF*L71P Cf4r}GqnD*,&julq;BN]QvGSa"V/PjzL9.)0PQ9_}1nI@!v䜋z )ʎ Dž's@^A;C,1S"M%6EZ 3 ?6@jѽ3iCzw`+T(ۛ0.oDoxdѝ.\c;Y n/P/^Q4!ͨ{xSHHFL,2nr`_HFg(Tvأ8meuLs5QpiuAwgj8Bq':u ÚkTK:Di ^E}r9ϕz&L+$L$BDVZk>1`t+-\k%=4ԕwT+z$V꠽)}KJ#.2: pyx&k3L;;>%y>TP$FqW$k*B 8cv>;4"6]pHXeUf I?£KrD'!3!uiscBcѱnnɯB^tSj!$2iTŅfɵϷpN2:<,y$ݤqґ{O^VH4˻Du4åkkPmq, TPܲC$|XBx %X%>+ǭFկw)i`-2*Z]' o~DDĨԝ,T$n:sn-4n} ` zp JR4",V# §p`99_1VkY:'oѐ&yeⓁt8:;/3,.NAGu22Ϡ+! Sʳ dUBIzJ`^'JsgeX /= -ܛ)&]%EР3r  @̨#;F,q8p@`ЮTx;!%yl0X:47XxTj*JREpG`(45s SX aO'R"3tOe'u٢euw2hhWp4ف~S^Y^"= ,h]/a, n6F f mE>E ˅Dp^.N+M_̰C܆%Р|;?[5G&2N)wThy۴2Q50%>8F4eWC+7B|Nih'XBdQld 8p$J[KYP? ԑԂҪrWIPL qᒡҚn6!X[e|Ycn(sAt6pֱ(5? )( .9I&K.Xe4[56O/1`v_Eݽ]i:=#MJJ|`q{$/Ǿ=47pS2} ]T +CyBymedܔGWQ7,9k;5m}J X tx]]vV W%zr\PZX4PwehPi]'2tpBS vZ{!BqʚZBK(I1))o_"7 l %uͯ/ .FNi}b XoqB X P3C.6jD+*>[6Hw%pBա0$d&T< YI N@0?^l3ȣTEފ:]T ʑi` D[N8& "7S>y1>E7+ӹ4B66oR: TiYwA=o>lΊW\ ȣ!7Pt^hPd7?` %{Sʉrd*H..d 1&Ʒ\\XAZfyjyTZv:SS ٝV@QqN(Rje3`;du,)1lEǦHQ>`0F5:/!d`R$Ds Py&}eQ9ljosʺBMf#ac.鐪$9ol2=h^|1s.$/2ְslIf( {b3Rir}/YMf5j=n4}4K +(C FApl՗{nME !% 3 9UF1Z/ fPQ- ,nf|2֨z%?vrƐ-1vܼh4|}095C c_ bTnkQm9um"X"2!w}0z Z6 7I& d+E߱ #Q ^}xY1/HB⸀#D9Ź$I,-irڅqƅ+I,"@i%  -hdӕ-OƧOxVV%HbQk.,6JiA iPЊSƯ"HU>&< tQ8/G:}}\WѕMC-+) S=KRz#$$x)| Ǫf]X1Lt4THJ6ge՜C"I M%YS Η>7s1_s=G))^ɫ7YrPޢf DαlCE l`Ƹ1C :ݝ mxr1p [* :H߼J''܇8rd DmZwLj֩It/-MX;"{eRtLnKUԉ8]]%12Q\\Aj74SޚF<Sݕj;|' meϱԁ W쯐8yqa}_Jpha /! p} #[N4"=/J%N2^ 1Dɘz&kZ8BբU>8D2m.+]yrIp?5Pj K/0ZgP8-حnj*vMl8:0?<;< 䳌5pEB*yx),ĖL~nfnKm# 3$xVgu#71niVנ!&=-a>gpcW  K#'2iXq)qL |o J<16Ҩ;K5|s!/r|%֢gA @jt`Aۈ[kņԵ}GQd(d6޵DZsB JohCH+]^M'nEMfY%ޢbRmaS4ϣH零dX y`n"T8%CZB._٣5-`^9/!^'9ېwi9iQ8ѡ!N\ 2__[߃1>ff'@e"`i}h\~9VȜ4ըL rWPsU͋ur^G,q*)jTyʑ&h9<~&m]:7_J%UbKzfl}>o1eУN,G:[6U>GYI'NjN4PB h*+J]׾ 7eYn@8g &ڠV/`4H|4u aksGjϴqv:c1ڡHX H( N$~M|fa>^R \an#v;hr(DMX?fL2m䪲Hbm!<<ڍ~ց56LbS;#xi;ħE1tI)m{NBI +JZeu` Y=E"{ҟAR!]ӹz< ކK]cqlMW"P<*7S<Ugj< ;8:<:s r{=|ŋg/x+whsA͹||iN/S?޲Vq.8IMTp"X`bm3y(s# *Ze5@mbQe"n8b#Dסn "P?/͛G++@z)"P|5a/ oyMh?ZҼ)nb;r;J~{8T ZK΋X^ɾƌ~O}x"fST``*WڠڀHרc_pҸ x Dd\>cIJ_j}0$_Vf|\)'PFYq&{T+վ0CD(bƭDdqN˵$8BH#W9J|#:\Cm+ ,pś"td$k"rԌ C_^bI; qZ{ta89{epApy^;[ ABHXrl-(Q*0iθ~0Μ 0'xPa_tJ=bs3 ǜc N59Adl|%$B#N@]x&g!q>Ii ) 6 h@rݯGkwj"o|j1bD.tbbۚ 3Z ? .+Chzs_p3e;ee& Ox0*HO|4L_ִGm Y0@⟲Ԅ6: ~ \@ I'>d!WODﴑUz>С1uCwFL֠_߆dwޢXZ:j҅]9xeSh  ^ku婩+ R>2'ꚽ qu@rgSDF(=Zkǃ #ߨu#`-L_@.|δjpd1uV#K<U#OͥOLQn!$E03}4?kH;%!/3L>*hd-D#ljNhw' >0׬IйRҔ Co}i+,[[Ҷ8=iSMMg†a~hQ}.(Օ Xf1Df =g,Xjҁ@ᨰ ӯ| hT @w{Aۋ>q`1-g9^{P80EQ/T„–V !-+8Mkqڲ@Azؔ@ߓ؁(@<0̜' LT_T{6Dߢ.xYYTi~v$];|O>whȦsY|ERx@XdT4wM|+,m1Bioj9rY1#!MC=:lXl931<ΦO=}[|F.l;}IJ.ьyᝏZLI9y=t%s3f\_sA3%TCqLLjF ɣ֌mjI#ooM%2j F@73õ뙞K(0;ÉIJDQL(P^^ U5ke_ }@Խ0[bA0A-yFp&4w\DL U̺ Gy^{E@cBLlr9.:uXQ3w؋.ɢȔl}ͣ/A:r6< Yssq1, &R"  # fם `:6CmGFQ&cHT8HΣI+tA%"4*k;ԝy'+HРΝӗ佔l)ˢCp;s@C>aYtI+ {5L@Jɮ`m_#0֮J 6bѯF눛}f`{~1hrlE_'" zơJI.ۦZ'u-F6>Jۼ#0rL<\3?Q\~: F`[P) ? Eɂ;Z֮JiX!#ʸ'/@ 0dHD} Ш.@ #VPueaJ󊆯|~ ^ czJȰEM̊Kn,Q1=$N$()悆IU^Q4M}ySN/{a[Tq{MyC?56ʆeSQbՔ9J0&8 F|ۆ,٩)Vmz6.6x0&N 5 (f9)8 CUgm9ҲSNl."+reNHeV1wPv/cx$yd }̉ "3JKD+sO&~u( U.|s-x'$C4挈Vk@AA>RWpSqȁX:4PpZBMeV6umiηQQ!X+r Q':EC9Pq\]?b:ÃWQo^lRFeU3DA^K+%XT-(ڪ*O_az—VŃ5ZvNX^t847')QShu~6P Tz]wt >K8\$\>pbI uɆ ɎMu[5EP "3twxwdQS")G 7|>R΢*T&]ĺ)xBwfGGA'jipȰ* mlq\-| U[{;^ĮzbIO Bߠ=uaF~ZTܨK{í:wpzLjc! T|L| : #6tKoqu̓B#~Jэ)% "/-˹0I~z6ذQ]W MvӾKC~&kOj)C;'"S E(oR kTyVAK@|Y2J^QR'yTp/$"p1B`ُ۴*9_@09B\yo ẘx`&j5zӠtr?K@vio@||_)(cAg (sض O@7pG",.PgZECiML-Xh{5Ⱦ^nrJw\Ȼԕ\aPZ*xhH4n=]{m2G'[/6t|(5JLdʗ/>r LL>Mb)qѡz]h2 )8)P=7<:K}n4^ĸD&W-$i|= e HM=h08ib-,PB@TB6nw^j]` EIi}1 U̟w?,% U .vJgqM\͔,-jZ< zV U; BxWpW֪˖xo7byɹiZTcݮ>dHcD6qM!u }|gR0W1XG5|\ eX6-K9V]Ez_;DC+Bts Ti'T^Z7ֽ[5RMjO}fn415Syۢ`^T\9poImAnS}$;ÐJ}vCPC-HUhGMGtK ! (K1Mԅz[RR PwJ8 ؿqωyߔj^Mz}JorB|jHzJ,<8ϧgE\=LF$|l"\AųcԕacdcRn_vO*Di3MZQ cҜW\؁=ε~*c§l.y#N%B^sӜs:@K +} ]݊nOMk5*yy ޟ@ Yъ>edV.m`QgqQ:P\B.H{Y) `=TTf8Bt3y@UJ_, `PLh20K T#ɰt:zv4U/sYDPBL6+GnMd.k2$_5JH8 tzQ 6%ˡ Xk 9NZweB*#\fVnsJGx\HP'qV(lj"֪@|D^B`A6Kbmù ;l&Ո5wmpڰG9IB..xL.[r}^΋<]B3Ѭuy2ԟA`Rcfߙ0#y[w̓x*ʀUR)z9akR)WnGT;dء6lJ\2zֱ<]n #x٧)Θ:*ۂmR'IѰTN]񣞬)Sv樎Rd]pP*$dKoEݒTRx0IflCJw@)_h:c9|X1llq nHan@nLN)pHdDOqv']gHZش]K[NGFi@0aJy^C(eAvWmCfZ7^:^F/a¬rlj'Y]NoSbgD5c>.q~C]Xa*Ǣ3,uYEWΛulqEօq:P0w"< [Ag^j>M )A95N!kᄹ"{*.{<+0Is{u;zM3dⷊ{kZhMk~O гr C]V|·0 5+lLЊX:=Ͱ3NɦiWE6i bo:JwvES_hjs&}pvsOsC9>J=q޸8+- *wbay%v9*f85r-C}gX}>2aEŊYeJm.5]KRa"~fݺ$YQ(4c(ƪmhG)7A)mֲmmj)nR?heJAd kC[?XfzMj&8AOr܉9X|i[}2N.>O#gmQ<`qGОpkf&kn;c{]tD ׊c7^!$l~ /( 7?ڑ[Lfm^4>3u1=9S&nzTa M<+o.G A͎B,ѡp@YR%/7ðHLXW ؏b.pcTG=)ݢy.oGE4Λ$Rt.QI5G0 \M2 z|z<< cKreR1oCb(ZwF$/"FXok^%?S#"GC~kWlwH&ᔫd }lhf9BIsIo揣}z}ƀ0kW77V~T9рqxQA-GMpnij]hFn/ 7=߁,9&-Y_EcChZaL00pDi9bkjyZ?#T7v FlM2 0 _ۇqvm;4)FuDvONunVPwC} \~uge? "Reuz7$끋o\7g2#EۋUo{7akˠ3E!(oxm\#Z<&ut6O< BNq[bhQz_qZx."=Ļ%g$[h <q_lD^}0lkR'oV4bjm\]{ .\"]PۭxM?$K/ ~EȎ4T.H cy/v{ͬv@L'Fow|[L)H Y 3/wRQ(ViD/Dܩ`葵I(3w<ž :]L\cK&~V5xafތAYx>:olPrH+K_ً$?oƬ.+(nDO^a2(!`$a:h/_ *Xzؽ#EGTqZYApgu>+űM9h$>#n@Ct߰2B OOOpVb)ɛiaPq)\#ĴQj zk=6@htF'rS<ǟsQA=86a'Tnd99n3Ćm"QXCMc0 rɸ*&O7Y{@L@2Ǥz3մjA,GaCS'LBOռI+ V7gvN*z, w__mK(pMCz,[ V5lENJw\s$;2[jev#ܢ,]o Jn WeatCOcCݙ c.K@ !1x >$_J7aLB=7N >MÕcG=fWձt]kk:ɭ#q?v 2L?{qtb*ҬYk{=ITऍSvnle Lfgy6ẒoNz>܊@)xwWs+ϙZzֻrǵ[/&@VctsHYT(7j-F0ʃ}M6%h}+657X19ƫuQYML"sA4r6(..th% $9Ң+An J3`K͎^a8O`E?dN^s'ZؚwEeQhzAS$`?ge滦G/&۬ru~Yw]t<,4N{n]c9꽼CfNŝxnFTӕ) A蛦i~NĐsH)mK6u^$!7`?AGF[YqRqd~]Gslx g&ݚlAJcR+d˺:`'۹!j#B+Aj&̬ .jڅ_kp$#fM"vkmdk2y9\mI o=ĊY\ k0qjy%44sZD3Ta~GUitjkDex\L« ꢃ\m|H'I#*$A)2Ocئ@du%IT$'1}WX܏y%y &J+(irG&:a*]aŀmVj~0\Ue e @ޚ&q?ԅҊڃꨒ;!i3 ܫgTV oP8$%E:ݧ3 Z(Kcޛț7o%P.&k[,Azy_N^73NpZ/s ] dS,G7LT5¤v9ᔁZԧٟ{ٖOO0d&B3Hl]!S=9ONb<;[߯6n v D݈ rL]ﯵF< shę_ªxinx`fM6||ףkFG-~Q5!Ga'h&Dn%" WFv#xZ-]=O@@=7H @U WtxÎ-l3쐮nR6Ff0y(OGs V?3&KpP_,>׮"F1LӐ@E;w) TԽxu=Gobc_wښVڄ-C,45YDS+R]AcHLڦ&n2χ +&+"|7%k7f*[$/o j+)~'KJX"$_ HY8XgId,>a>hwƶlNj,{o/ |fA}>ŀi»s}lfDƏޗ2- tXLK5e[9"}*o.xB/9Jټ)t_aJS "p1;ԣiےj j3҅#AƜ 8BX%e2)\ Bf]޲s,헵u5oN=IkzLEOc#wx;[oއrz/>dvm>*%ƵT x=YEMW/`:@K@s bmpzD6pS%*+|hN`9וr U@5>mx 5p蒀locFX bgSmM׮P氖5UHGy'B+A`51qH$B_߉6-z!+MZ(}#D] ^YWH)oKnOsJ5>9بuCCŴykj`0D_Jյ>hTjCF$tĽDdOB,-LҾԬ4*La5<$1]nO z=[.OʅM*{PI͡yMCfǙaB1ʥ0CDw%Vc1K&04rU./K M <õahCe=$:ghGn; uDLu !U3>4\4AVrdiW?:>%A>+|hSmpuabaDզq.c?^zl/^aA>-HpE9:-ΗuV/DZp N[bkeϿ{A/+nTu'`ͩ  I-X #Vjx>Xnw^Ny [tx`gMjZ@ ((qD6oNǀ]אcִ8}ZM7.[0|1ӆD;c lL!K˞j1? ?|HPN ?4/;ɸݙ\ZK^"brrZHN0]37_M|1IO#VH_zp]Rd3ņ tx7c-*8'׳]|;.c9*rPfh k R)8.P+D0n{qe9z!HH( @ ӻzDmLf K1S*sG5 oY(W FDo}yX ndLp9uoVmTg[,FL#[wWeB4_$FCځ s u;/<8}<ʞxx"d僵uNaq,2@:uGh0̌(h 7|zԙRobDnć+O' W%ThE$~+]ίL! |fb@ˢ'@U.mbߓ\Ƴ҉K0A7d߻_aK|SWѭcݿɐs>? i U9B n{],-rڊQIyvs|0a`'LAryLFQ<#c,':O2#hMᓗ jmv[jCnʉG_88' `ݠP-`]c3RKgϊ|Ouٶ׮!5?v{vl~A > nO>XgL|pPyk[FZf.Ň?.mf '$PR/@u. Q K,3Ӡmx:9?8 U; Z{>XAYI !6l2T]%lT JJDYէ )ttPK))rz H)C@f>˒$_%:3"MW L tAj#=/ƤQգ1YZ]"{ 7^\ϼ`-LKW@s7YP+$kTGWQ"lGH@E$j2=:fu;8/3Z:\ GБa)%s'n+Af) ;u0FJ-}d?RgLzQzj5!ly#f7PR/g < xGKKzk@VDS}mNƼ+*qUJOzk]3"+iG'G& +bt&Ѹl$UeTsKcf߅ByP)1|!.EhXЇ.m׾nFFWގ>mp9mW\j>}50 ɂ`t$bQM&Uav蠎µ|MaLN<,jwzym2>: Ղd *6x9ƴ e}؁v9kIGC8/޻goֱw]Y/ɤ^/ Tx|YofZ"P;W6 W"h)~4S o#+W/{ 4/΋ivrJw#ن0Wh>.O5e@Ͳ_<y<׸vŠ3%nY7I֣S)#{Q2?u?0H BdIHW%eO[6$G9}jdN~{px.+( |"g#Pc0]pHEd!<8vJA9 ;Q;v?p:$y:rف )!Jvape?xZk~I+:?pclu}@V5ݩ_oqj)t{@Q|;aj}MZ#sɂU^/P̱:T"E^I`֟.'Z L+k/N־O9Zn%{}:Z|erir+Qj, EܓTXWCH 9j(<8d(v3 ~}>91Def]ݎ(S?!] dI9VECnZ]Gs!^(y5r4Wet$5bHUea}zf9=/!$b:æWOd_ oWѰ E % H_]7??X->n}wgw>;w~d[?V[go...0UC1Էq[4fۃϾ @?lnmonl|w:!3[ JW!T'7^<.y>'pO/S1wo'=}x1l2Vۃ/q{=vnG7W/Kmelws% 128&|6|W[O%H'*ٽ?'9xe9ۧg>|R>IJ]o)xgWlYkGg24%I v!m7DzC/EvwXO>ñETV+vPdmqgGC Bb_ғ8nTG]1- =s\Ŏ qɶJ6^wfh!)ӵM%N\s[sm3A(o$sʸf_f\+Ç l.,\c36Bjw|,, ?採sؖ {B'-]]0h&,r01s(vvb$D] 5]84MǰSYJ4$\9Rfq4 ^;}v1ɠU5NbdME>B؆jyOQ n"·M5XmW;wn,~)-%Z bV.ҋE+O;VXB0[%{1O/UInm8dI$wiOUt'&&:AQ{MP-ۼd.h ,ҊZۈV[\E?6墽E'; Pu+D |\i _@k꼋q7fP Ϟ>zt`g>zmu:шgR2Ê;: cԫ)wDߣԼ LMb~JWyEW WAg+x٘oqn K "ZI.ꔐ)7SjE2Tm>(is (6^QFꆗB4GhdɳK5qwǜ1%5z&R0kA. 9JPvk>.boÀl:+ڒJ8{k[B~̎Qy҆< X"'Yjhj8?kl;)G)_"xE'F%L1?,dO+\U9`'ne";jT&uU(7T'bqq~&ū┢u]^J-wW #yRiG]ӋHդ_c!H-mr:y9,\/T}u#HB aDfAH> 5cbKA7U\}rX _ZTZy(p/SĤn"[<>v%LKc7?)@=D 5|ӷ́.Y5k=蘯]ޤ(Ա0NiG#L2$@QQ+1!ΞڰBC:U;_pkn mm˱: ޑ( gcu9 JI] z>K:,} |"kJE I~>ܦM(Q9k9 _~Fc fMsÆ"%)'4Q҈ԯ8BN1 -%mTI G]}mȕsz{n#TWM{޽go8m־hӏ`zVj޿uhU^0d4\!\IHۆkTE튯F W zWC>S&6z7B? 4葘ObFmDZ{#j# CoA"[ٵVEsNi*PnĴL?,˄ed׉$GD$H}.y'~TGUoIf6yyHl ʮKxVBum22- >- gMG/A!tPḅ.! n/;;{6hhdWS_PWSXCY`6FX5jR&>r_._S="bdM nXٍYZ @}q" 7So~aM!ojˍrJ&=L7zq1ڂ ;- %!M8Xb N:9;_lb?Jv #vz+:mKYk*+X"S|zpOxTX1馳BM bI%k0vG$a]#~풃ʑ볟`bs۲.kQt ?s@ 59`rYmӄⳭlo}ٝϷo_aoc1 p"wng[ؽݝ?z]ǜʯ<4lޯOO/,Ʀ/86H*C#_DsfՐ>p FAd\`i쒙ODzf&,{xT TJdfZ%Su2yft[̰A|ε~2 A f_t7`8ػQK K@W;sz()DhnP +㙯o8ryP6!Ν߯Dtk@@W ie WTp564i f\I{M5:ԝ)^6^ zۿsnޜ(|GB ؓ܋?*8=(ۼbPh8Ii\.RNMHv~IJf#o.4w;.3_lFe?WA+|yI;Om߼;xsh۱͆m% }1Q~KU4/vnmPqmx}ۿW6վ I}?n}ٶ;wv%:N7R#`)7d?#|TPn\4(cA9Zq;Oι.]M27W! HpS l"3O~'h~'?J>u!֘9}Q/~Fۣ,>n8+(w|qY%إ Zeн>wgS7|F՘]L :u(OUށƲݤ$YXZaE_HԢ䴀ܔ!!0)0-7h-V/@;g{YRGqZ "˞R~1.ַ)Hǜ N>ە~e=zQ+Sh_A$H&ɤ`-$q?C8WLS0.Y2du9/j".OkgxTR)|b@P|2`]%n8$׮~I$5(p&ܐyM.h+]R5dTʃESiP\DG>'4Fo[$Gxo8M?0GON `T8ocھ(!-TTAs ;wiOd-"A[;"q׼xe פu-~vS]JrɁA])I`.3rE cՏ v EQ)KZРMGq1}W[EXAKsi$ě((pȅ0 ݴBqQp{X~ b%3sz`Wg%Ga~~N6 m ƪJ-SH]Ǘ`4)NY\OþsU\p+{Wxh,-;~ yT4Z |MUy;Q@blߠgL.xY ׉7}s.&FQ6gac:Z:hwZ{xRiG+/0N?_7@{;Y֋jn^^Na|'PZ:KKn)ѴHP p->omBWc ;PR3LQ(Q,.H9֐mr&(XŻ*u #hc!ҧk~dkTM'GCBۢxEqmoSLuiX/qП/ B``A-'W$2q$6ifn> ST] ,Va&1%xC W*`GÅ"砪"X]7 (8Ϲ7A f[ś{ʺ/UOߕF@A#EpsfIOWS C xDLw>M!YLH~2#JXҰ[d> =DŽjCZ+"1g}}QĀA%Yt3@:lJ f8,|駚>ԮNs4.m 7]ʐ-A͟i"' 8‘abɏSdK8LzH#:Nrx[i,nUϡǨ{%a>4*yTѝh J:mnmJMxcNC )@gX)#Ů& -據X涫٢}wf5ǖ Db8Q,+{; 3ˇY"Gc )yc|B6K[-i?{5Cj*-pE?0F }cxřw0??^[6>~_Av60/jì㛯_=xp]0 }-mY-k"A04n7;pYSPdD~϶v@ϝ;E~daskgsl{k!gRc,9}R;;m݇Z/1p@q{y6M #`:|k yX.CqZ+Hi@/#g6a|r7e 12G|4fC*vO,PVf)X`./^1ʧ{Z[H Bł 愪1Fԥn#=$k6{D}/M\Gm6،yq:/j4~8/М 7Ǔ` "HU[*m"YsStA㌗'37ʼdn ,D7%(K] K)SB]GŜx<;_P!w`ie"-AtSaWXJc;-9b I^m1 od.=HD Ku]9ȣـ=k1I!z583$+}9<>mi,) )өv@6/fAѲn L{QGhy6E+"$2$tɣ:j%hXϫHdP% T A*lBM bek`ralnTWQw`|bGxrq56+$!c_"N޽wWFU\І/zfUr< k@O1J K>ӓ}*n^_'| stCsYg(0="LW&HAV#x"=* HYk5#vv52idk{ZUwk%MIk6jz-{s̪V*Ɉy{ﰳǛ=?)EÞEz>P0V l1.&a~o8mpsa^36 {Q23U+ Pg);n]9%pԻvSajq̚d9ajtv>\vƶgG1 08ITnWFnIeQj(=d A=mRCmX\3R "\δ c=a4$jeV}XϾ(s$xVT岞\IKxPܹ c{FQN|qM7,5A]!q3ЃpO) ;Ė8Պ`JH^w3m?Y4dTܔLw]7VPurrATSQfBJܬp;m m rDL:XM$g?ߗ_UEf^IRi_+UEc[v}C (P$)Es<تTe"lZfK ;ekG`SeGd:ܷdC85^{g«ps">Pr`t0ES7]f1]cXUR4P>5; v}gp/)kU&3I b#KH@D+$ݻ8r{C$w Vy\Jԇ^w11yEU 0|ة8'HVeu d<=0;Knp`ӣ*J{' P/( )u/kKu!S^ZyFmT}.9)SV}aT#fLeDExJCGSfԒj E-+K16U^b ™s bi s%)ӪH謆:&>^!p7Ko{O?2YsV|\ym Ou J c{T%#?QlREg;RU%(]o3ݬo>dVOW!+Ϳ]tng˟5ՠ[FtC$va@Ovp'l529PXp )`#zb*q8j5?$_1sGjfeScbٻ\W= PqWk-ݥ7=g:rW\FEx$paw`mU 7 *T;R3C)4>F$8AeTּECMJ evtF^ Ք! I"2` G3DFG$lqY$jݢET7h5'Au/-K^bqdtw#P(R=04orqyeٚ$Hk, eec*&Rw f'hiP p$,!߃#z[5&;޼+f 6U:(3lѪj폗\W~ Mv`b8r𹁊>K:o]@jܢd$݈Zo)? LyXHKdk>ۀN/'G쨵DӍʹ:1ܙrs41 B䲍qTE}Bn^jfF4iV<BHߓ1`jc@~q^s+t0 E+3=˽c9ƾ.ƩnJg;7kj{XnFy6y 3Kuyx.ذh!` p0U\P%pncە`aQf'aS:ah[J[YYfdLل?2XA6K]fUO5| ʫU ҵVnpsJ Yrts$~)dFҒZ\Fs`_ɲ1P[ڪs`8!B#fls Wq>f-B3p4 mu7# p.އ>nXǐJv鮏-$|R;X܀@5J>їXqU$tZ;Pk _b଩iR*dh3HZ-f >2n3oCʽ58Ҡn& @$\qrp E'J$9D+fZ?z~% 7&#W'S gzfy Z` oAtp$z%%_P^= Iy 7Yv}ys)4 NoǾ, $\;]l/ RDq54Fza4DP]!(' YTc(Jq~Eib*RR') .vS?fԘîdmPMj\*(Nd@pN^h69eJGFu`?YPبi A4(#F; W=HTԃ v*GUi2n}Rœj%ڜEPiL]`gʳΜ4"ToWJ4-VLK(;&,@W \=a#'fBי;l:_w&mާ`At՗kXg)HRmۑ7R]mf$ ⏏?>ڻ?VVkn|pƵ0u hx\[ц di[O?aE/:O-e~kZ!eWe WL4g|PK)A(7߅{-(ub6c+l>zdk{mlH:cnUcN/וnl< !;8-вM`CF8Fsw=ccGޞ*i%hER("4Ϥ)5wmJT'tx +5Rm 7De/LqkzIk˽PHh~p*XVV2BBԠŖfTk1NhLRkUo eƲbVϴۮ& j%R}5:)pBsϬVe_?}Fkaq`hl _!7?>4'R(qWx1}:k^B`ZZ"jVLxYt mCJM۵ۏةe7Nz Wb2I􋌐p66|Aސ UAYF<%gL| [gfYe}(>'MMM>1k>t603 /ZZ?8KThwg!]e@2M^\:ЈnIqY|<;eiGk/ )D9f:hm97aj31QiuoN4tm9u[fq E֊*ixQX+q~ii.c{ǝY#\"j5@+) zAHr)旑uqelRmnw{Gp*T!SLtFz`C5yL Wܷ Tײ1qOLbӹK٬̀!sl:4[]ڃ^:x,j&Xxg Vض\BiN.`-Wigr%uɺdvỶb}YA8N\{4KR#Q;H!/MqrSe[%1SP D< g/Ul(ޮe5-ϙк׫=-KBjzJBYǐ(TSo/c=t֫v[jWM4%dԻIXZ`;1zM/^̫W*PJ g=$HB〮 Ԍa+QIE-֣tp.^ꦻQ&CB˳4pvgx`J.CټVSI@Md_A b7&B95pP̴0Y `$qMc:𸭳^AUp?GCkR /j(vA6EoZ|I33LIj֓V٩նsW9 1vt0J3z+YǷV8w3ʒ!J̜qlZm΂D8aƐ&NP7!1nRKr~ݠF a:{6QgF̨{1,- o7u~jVMFSY:~PYr3svYOH:[o-*?Cml. a_G|csr[-!`,/B'D% 2xPH^+2ݲٮ|C8W2Ħ\u-LJpd<+IFHN&s{#2U|Y0ȪAp. W^.ӆR~2!4j&:%@E9{K)G2T/&3fTm(P%NũNI >3A V4PL ׈G5]ZJ.ms=OBkW܏(NC|r}=ߚұ[/}R3PU^x4tVNۭOׯ^ozp/OV;ppD2$a8ӞD >hȍ(jzO) S Q@Oi|@P5#Z1d4/?nQї˟JFC[eӃS` h}ȿYl ˲ b%B,kZ( s v+G[s4pFjrtͪL.wo2clk{ق:|yb80~&) r9C-zSJi&b51Hf7L%xɛX n"aaVcC'^m36'oZvBhqk&cZg8lY_(i<G)Wbr_O Y-Q\ x v0d\INXb[Y`+,s&K`Ȍ Ƕ\PC}N!\|+$Jo?";|+l"! 㟇&-$ӑt4E$JbCmTa E^#iu]&ǒ}.`TGVoRs1hlɆ)y8Yd+ *؞>{ƽ*t6]h9= g[قj7[$EۮS]=c%2D#H~*| v$ 96h1Ԇ1ΫI__5"9@B2"KI5gZ7S$ 5x$W}_CQ4r=X$pК|c& }K5O).[aġy_)n`>TTЋ ܽӶQ,o|LNcuP4aLlW 2o0%|{B2{l{RFY1Pͦf4VѰ]13|y qp 8Y{&.2rܯI-_о7Z 6Xڡ6is, HO)iTZ2-̟=Չ NkxJI> CǔDG7vr/xo:@ͳSi,t +B~[2&م ,/hYQB ONEYdנA5}㢓,p'dqV+rڗC9 EB1"ɶ89|Y-j-FGXvքP&3&E H:#G'ڌKĉ\o2-rވr[oEzbw.:ԾDސr9:=Cn3>ӍKEq@,E%ⷒA.7`ܖl@#'SZ+ *y/eZRzogq2MgK wG\o+>}󄜥r~7+Qųq3>ѿz}NŇtQS pw]1D+X:u'EU370/Bj"[:+% {:tqh͊ݸiPfӺSPY.U78)S3|,UһDj@J68[QuP E<׍ k0ooșM|z䤬֍YDg.D0vB8;T?>ϳAj=^%>Lkyҝw<35,,Uz{=v6)UbK:XƓ1 7[CZ9;!IJ/*$Ǚc_Hvn`3 1i捊oswwKqM4ID~{H:/&2.5j͟6l*% [ZC fؼy-BgaĆaxJ3'`fBcpߤ:}IBE :/%jm:M4sj8+OoE?];U/'XWΪG뛏^ѳ.uVS94=94T<8)l-8EЀ#Hh,nrBMF> D V=;^8DxѼ=3w zӠ2(wniR˹_?'8a9(hUTO"eՆ|?,Yx2qT*kTg5 mp]JM93ؑ\jJP)BR e="^-džR  cSW..:e}[{/vS+O<{+SIM0IGIuVM;ak٥wl;?rw+"0-Xڣ:L4] @h R4rdv&aOYݷj!)A(XSՐIz)DSc<+ \,QKUV&h^IUFH||'f37I˒:0X3(]5sy,σǏ7_zbBZOJ6TlneK-o{ͨfw葹]',\<ن['U1;CS:lVVj\r` &(**|Q}q;uZ/uZv8Q,;h9G`8[;(Ao+vJAt{QR#,wSaƅ҅lZ2a>> ^)IxH(b0C@6Ӹ̅@D {mz UO(oϔ^8T<J ZNHa ScDTdAԨNq\kE"撘șq 2(ٗ qpZlrJX[о"NR);:F261s n>2HeܠyK`^ 7xW~F!t-EUXŲ'"vubdH唭RT'8o3/I=e~_Μ5o#5B,L8g *u5^dWY|+07wg}I2ҋd ȐVUȔ^uFb ;fP7 . ~1yg?~ӟJƯ̈́a5?_sF!HՍz\SrF^]WiRpx S Q3ǃ,>*:߶VС#Ag+R.hFg.bK,`92:.2hyxBmM2K߼qa?' BGbsp%E^`fY^7QE)s^W8Bъ:墽)VٛF;;BT3 s :%1ԜsgcL=&Bʨj6s!x M tLJݝyY*IƨAI!JxȜ)]((z6 %!́ ql!(HØ{d'sg{g^{#E ^XHY `37}*&ܱEVu)o:3لvM\qmpzp2d":.\R1Xf1O*/'&*&IFd\/o* u!ԕ]ͷ;\#YGɨ18ˇ&7|sO>olm=jo؏. :JϼD_BVWj7òJ-zYqVz+R}±e'op CW2MN4 } {8͑FoxmWڕ]OQ:YvtQ{kp7?H}H%9W"O0Q2O~քҸ{owh^úf譡O9фnGF#W(s9Lβ]$P FIm5$WnJWsmb@nb!a"pU!֎mPk4Ww{1qIlֱ 9FZ 68;DΎZt./q;DstnJh)r e] [( >~YHtbb0;;gY#t2tc|o7 L9/9!Qɮcwህ=sƗfWQ^DKir,+E6!l :rALįƪuO-ry@OtꁈuVl:Epr21iShE|a"GOOjZ^k{{98יiT[`i[FӢy @a(Z2VY V QZv kY{n#όE*~b}7I~:޴zPrZ,,O̜oU>~ȇ68~kFBG!|a(`N)X+ :HMegw2ջB*DЕFL|1_E_(_\'-lT)+Q/7j Ll,55& ع|ef}m1ymv(!Hю^ʹM]hJSS4=9G^;,..X+j .嵐S(j!|KL㖸5U[2*1K 0:)JR&vanĦmm6$Oaڳmú5t[8ԘsTA2%o~h^) ?rɜU^cI`G?:^&LV< BL c4 {Tzq ܕ%|TVV?Smogw urkG^^Qp5.dlg`@7+I #8Hc}G0ohb6\àxc~S\}m#b`l޲ MHFQ |  x Je`s85!ԂcK t\C"k0)?J&y:=m)2\ݒ4jlN.j,L_;bj4F#r,p,W"מ-MX =zcctFóS  6 xPB@97 t8q:A8)0حh-F{# TA%6 zji=dr4Wu^ƲGCL>׷!Ԃ-;nt!.f^Y?~<>8͚sni~a)\6KI.ꕫXE)$G34⎨Srg΃参OuQĵIWpƃ/5=0R~Hk74|7f]Lrⵞ?(j:|TlJv1iW$Iy̙i;vMrZhoӴUP%C@*HnF1K(0͒5q";r?\SѰk&Ƌ{>@i`XkTx#h )j#.y )mW܈J8\MɐE$EWgG(6 >4Ds=|ںvXU+'@F}z֜'XޫY S%ݻp%ZPzɴYR;PgkMD/**s.[v1ߥ qlR ums~1wݎc{1|٢R (Α 4F%wwzT?r!֋Vyo  ̌EmnA{6UoMw88J*׺1.Oٻ_v",q v._~}"Թ .BxDGS tRhdZ۩NV穗*@W,:iߨR:VZZQ}N?)wD˅cf;w*[Z̡os+ӹ ňSqs6"]c}}Ӂ{ ఇRqx x x7*bM{uz-`&>8I|!Mfd%c˷WBǟYHHBY{0TzZ2нjt}+Amp-;') gF7_S~出vtgZ >]U . }O!s֓7~䧐.!Ҝ: 0]7?ۮ hN{5Q]蔒 u`ζ|mW 7h0TRQ?&- M)?[y³AⰊn`"WB!MaӬhWz5m<' OuͯK%*a-_WBm"B千rj/lfpX0%Taؼ`etyxXF Ƽ$sb5UZ`T".JQ)؝ۓ C]/w9e^2)!X sEAZ^v<g\-`"$E0rpF!),`>HWPYΚO~!p`zTuG2 oZZOAg /H@,3VM}t]փ;Y'Ӵ 1QDzlGBR: HBdA KSg9e3$u?&jb:έhXױ|ACԡ(sΤ6ݗ hH*FȯV݁%۰vXS9Sڛ5Qm6o`aui XZ:Hڜ_&ovõ*az3:y&P9W sBQ폦I` {$̨b$Md|ˢ}Cuo^r_Q )=XFB{.i07!"D4ΧjT.U!'$ NcXS#zIx氀6d;Xa^TșQռt\ANYO"P&i%2䠊#*V U S6J9%RWpK n[O:;l3zUא+,ߙ=/>ӷd'>t}/~XO/XK?[?E ިj).28.A<>ybȿkF>& /ČX`0=Fxn'L\åL$PI(Hץ!{X66"uS "e1ZF gq4M_xz\xb\ =t[mJkftǏ#|a?z?0{?^5oƏNj-t|c?ݺ~t/'s[#i~Dl5 6r5(ܵȎ uT-nϋ`U6E$Af֭$0QfōT xsQI1{͓h G+آWw՜Ć[ "­i0h43=0m[I)d@XԤy tYzvۜE7å4b[shfM&6Up)D/h8Ы/e,3ۛ rǎ4:N"W%P64͘6jHF>()nk~'Fozp:;V$"CvvPbTv]!2R;^=>yTjETuTѮp>]^kfXy0vei2T}k^˿9U-u"|2J1C7 ,+ 9/]7.k4+ى)]ɉo I&SH}0΄ft$Vmι*?21rx=֞x #:(@ulRE8,ɫU?(sr`P #oz›]k*!߲å`#kY<n}AGx5T%-t_-Em\v"l*HJ ڲoݹ+52|vK c Q^[Mӎ)_)}l3v賵4Q=Swoh p暛XZPޛ;~W{ϱ7Uk-p>'SI AhthβJ:'v 8SJ |bG1LKaK#ϻSs0x](9Al|P k@.'"%8de:F ;gJ&\xbn?_ T'hxiwșt4)g8$KuU6-3AYo1N(PC|U{ *N7>l*KAP `wzbe0W `2"ݴgPOt)b}.etj/lܭ6F_Γ#$lP|mZRɐlųm8<ޜ3_tfvyµrivGt>Ա``:'")j+lĿY8Dɐ^+@oHԓ8z`tmjQc&WS|tÚko.ED+7khZ^$&3*7V#ϻvMλLGj34F*dNmڢV]}V7K_3bz~ґe%H290?m?"J!  ]98wr0%i( 9SyHNVF\ًɂ`&)w*Qr32acp0B~b2DA"(vNGy%S()oy,!|=< вw^¿xypNKO6W=^cj_ze+gXK5@-o=/_DِՁ{!8 ݉ו\gS)B-&`.Cs g[FXYdܤYSb ~ٚP6Oh2̻K*N P K(Xj>b]`f8F;P>%r7)@4 h:#+ &}[Bڢj˧lb#ēDN_#bzlG!8;TiȯFqxʖpj'z01TtM Sb]!S2O`r &Y_&<0ºp>b@RtL#i>BoQbB~<)>z WR+=lF,@0 -8JF;U;A>>o=oC+tVX.ɍ֓_{[ۛO?Ϋ=|?S"Yn}y< >ps[_a,Izҧoϊohk#dv#h/)/S| .W>/ixT ƍjj1w3 T`}l~LxQ[vWLM.MN:h:7ַI]?gx?lwezw6>ز;mW Uu5[bYkJ]h2wR1wJv`SC(y'/Žxdd#$S WZKYxỷR;x(R?Ɲs{vy$TJ Ϩ)RHdg߇FTݬm?gY\B>jufUO$]XHM/ T~\V8\dz/գd]%PWu.QQela\F'jfeV_Vퟆku%0VY`/Iͮ.AR~A P/ڱNPC4h$9W}za(l)2"?71 sm6LvY$=Ps㥤dr /t% 댋j!Gtyt:9IG@j"(Ex+OI# * #-zӆV߶MQl3$(zibhp{3I]DT0ƍBQ$~s)T _N1z5H=Cȧ-Q0 ]$冔2+;+6!)Җ[&uhBiϔ8&,F&tV\_+f@SLP{is(OVE<\ĦC |p"ˬgތ/@9Ōw6 וh\mt\{I6KP;p.H:!^'.1ҶuEaŎhђ=+;YWA9gE|F5!Jz5CS*53jDo*G&b~8 yX }uvΝ+]}SG0xԴW{Q>q45ꩌ#~J\=4(u"2 ;hb H<7HC p` 9ek<]~1z~׷×n/|% ԻǺkG{'`*WV@z7w-vY EAIIWSw7J (v{0K"53FWmo6ҥKK[=X[Uv仜{Kd/̃zmB7zH$Y5#@~Γ ܅&ce j\~.,2rH~8emV/ݹS_[d-q,F4sbⴥ6!AQ|jXr1Rt%$LYD]PM d\oבDeO jL$xKY,xo͓Dh&y`z„[ ;Q݇~L. Ѱ/&lh4;}ݠk^&OEtOv6/193;kS폷[<9  nĖ ;Fq)gQLNR:s`C|K40#bb n_}6s lO"r VY.+Lq܅jvr q|L+`Yދs*PGJ$9[! Q:M17ؘf'Hכ,_6"*<]CUG>[tNYMn k@(u;c[{`έ|lX55q ?4*ʺ P*vK-ch0b6VzXǔeAo.gNh43TeBb% ciQ|{\/)OċR|/s>üv4e#H gNxm].gtD0Q@,;8xfi[ӄ7L%RPz0b @C('QvMo(鏇ӵMTZAi'T uӗf>l/fssR|H^wZT@3^I-3˭M!)t-o6K!]I[X8i *ްJAȊBU}ݹkWlѐ| P 7|a kȖ ހ̧]vHà|~co >_heJu!hMPJ>pb(j4}+y̒iV"tMYSh>ۋ)UBM8y0x/ҊW]!Aqkb) GOfve&pzZ/4҄3֖qAj+Q_&(9B $0\^]p }ZPBU߬u.WUIh}qq|+Ey\{lV^Žza-cPXi}bf rZlKd7<-%k òVtVᡸl0yǶo-9iofG"m.!`Gסmp s{6 3B`aY |J貆-K 9ݕ<X7ؽ 6pF0gL7tra-9Rr}>q4nPtWpb-Ш1I:| c0L)g#F$)G'y _|s?P8|z T>q[(~64͟= Lz{A.zlQȣF{NsS+ w1~\;X(&7VV&q61^6=XZaokb<'ZӎcE3_G<=ae{'?OOO~;O'^WBh^O6QdX{~=?үGC#G?_S4+$"[&|q4=A??i) Z??ڟ7go<^9k؟=ޣS~~=2EQ0/cԜyKh((qmyKqņބ7MüRl}O4Y\CK09N mtpVI M?8LA1o0/s==r#=-BrmOя'_!_ gg%scn[`:f/o&{Ͽ"=N`6pHl4t1I*ߞec7/b_G>iOv?(q_2f`z)(NF݃SfցOf E9>prק3E_Dǥ풾|fxG>,ޛ' ƿwH[ )_ï~__oK1MGY1sK03㧉lzwz,ugۣTՇQ=Dz?O~O//Ɗ'>Հ.݄'C9({)a{OG˳I9:O<90Ooذn3mc{URGER o~)2?+{=ȫ8fYvܑGMf܎I(?u0F'~L6V*3GF˒0{uj#o}ӯ~_}o[ ?M;ғ8a iiY4IG*60.J fTQQS2Gk Nٴ8 xDkC8jL?+H.zb F^'10nv4oHGǧQdPoeYh:p<;&xg'nSvDgL8ϒSUG1OTmKسhϭWOMyOZ~ Q,?AF˓"}F@kؑyWwїIӛ?N_U HSo#/oeFK8 a2xizeVe>毸^_f5}03'~F&xC6 >lTR+FlV++"vԾwr ؎{ٲmԃ;'r,enuo6ZPzawy˾}3>I=Kۇ?L&ܴv$1ٵ(6w ja [fHYqsE)6icVLh\Ǖ|]j.yj/8`PZV2 ^(=u MS_ŧyݛz&{>_9oe1²k X \gqCD;ĭ*>7yMW09ϒ{u|Q0E>/bé-<\aLN>"-6;,V"*rTc@dIT[7wZ'C2gfn]!Vu|Ż! ?qK=WOyԶ-εº,]c.t$lj* \k T5u劤IC.gNMe8KBNyRvq 4F W KH4̊|kZ]ڕofM[''' 5;?>N.F =zWVW?ZY[]YXzc#L6^N%$˜3VF`$#,c `}"mQ8ɦGwTSJ]L;UAFw* `>8 XD.iXd2&6@<܏q:1 X-H^Yvԇň])?@3ߒS &3s a§>M\˰lRַo{No ړC8YuDYL $RL4 Y$G,xL>ł '9sY(y(kOjCedXj|C}wos.7İR4@d&_T@:Co𗛮9+a^#VD@D*~kcCqLK\?0ܓ;ĝX[WFԱM^L%a`sUur\gE,bVB8)B )o@(ife{N!:K-|=Cy2^gq;MĒ9 %o0U[W>rGaQjZsWpj?VOg-hhNk!e[-2%LK- j.,%Y/'Wx|PS-).d hS=/:̊0滄PfYV,G;`|z\4xkehp(m/&!!^ĭ؄5)d+#Scl2(}0o^LöiRq*L6:WMSobМ/1#hwyd3}9$X:HȬ,u| >wt:> PLܞNKtӝ q9 lٜ]fGS pM!H>_Nz#G1h|.Gǝ̩w&C#>)i @L2B?cgy2!6 ?f,u}G" -_-ॹb2s:6 l4+6m,!Vqd]g;?'Qvoۧq^lFF\HH&Gbi :/UHe w###/.E+Dyj,ӝ2׬ٵc`8̰i% !_SۏvRniXyܰqCfOw 0nwC8$HA?ɉQU)kUzbD뉖{T\=薮ɰ\{JmND=fuH[2mdB&TD)5sʁKP0Z#X*(0Ц[Oa}6S)faYE r3wFx$L M4OJ`=Vg^ V2˗7Ka9z`zjU$3`:TG33SKVIXMh7 Aiɟ .!FʜõfבŪtRu4<%px2JSQrKp(z/+kq,W:=uS[f yy6 =̋HMɸmeJt|9Gh)S9gTfOn vᤗ +q"{7[f<3p8 q[+Y(;BJ)$|F\4s˪TG\z͔MVMHQ$K-}*b:cu7KtPgl7-}-.lrvmu!<}Cq]QsYx7znUݠQۭyr68 禭wrv)CQ(=E'$u3 YsbiHޕ!S5)`ȶ!Y݇uԞrSЬνpe≹3fn *8 &N'lM8JdJH'[a\Jg iוt`}1XզI0Q >IWOQ. F ܂SdUprEWf7L#UŤ?J1HsVy+3@?~ yY~zS}*W-q\֥*N5* ۴&}|d/4>=ǠłOyu{ 9ƪ%Z'C w^^j`i'<`0rZT!]&X!\ܦޔ[ߛZ52Z/&+5'Gd3vaj")$*@nj`'Kp:c iSu(eAی`ap6ϝ;Q!`W1h-KUfKʻ;d;6rF3w1N.ҽoEw[Vb3k75M(pf$8e62e§a߭|9\__Wkukzt0ٝ{TCtӦ㨜ћf_fJ "E`YҘtV6{aWrBTXMUTx@Ƕ><tc8iAäH>Z.sEEb\ E)=X =7yXߐ:<\ÿWkumtbr~g1CXDGF]ENXKyӿٗNO1ٟҚɷ2{`*FYasqbȓx4Zf*A^6U \mL EIl_yKQ9_PȋXlxkcC.Nrgw^E\87UTCj.Fw.MІ$ͭaoYa3m ͩ FT[}4SwHbڋC9Q}QҙMGyn9"A^}+1uwڽ1I6O}_"R+)&V#S1S{RE@ƈUü m|42Lp ˗ë2 {XeH _]J^=פk]xJni]:wwntkӇ;7?y!@HcNr>6; W[>3&$O шi0.CLRU-30bQIt*WГ#m(aҰET h&It"Y嘻PMR{ Py-S6$\@=(e}&ǰR/\І5ZYy&%lDl4]:AB$y6 g8^`Q09u0i:N;.;#qΈ:z5\wh6;}݃Y2cNٜwNa(0ȷUbsՔ-Rx]j+U{6r"k,;눞_6 R `:$F1ۏr]UR1YeN>;j'/P9N~B!m¬w3g7jM~:dUC+QD˗BƅF >ᰱEt9#,63*`ae5~.}2L2X ƢЃ?ޑy8:5/u͌i_ "9p-@,?52ཱྀ ۄe)hzȱ 5Аwn2S 7Ҵij /H ey,`9W'9 d^6ONp\: hqǷbZ7[\gaYoE]=A\sM"/A/ 3'ao Cá94!ʭa(kk6vM>aV."9e'{><\T~u "׎V_ia/& 7Nc N4so~T]B@vukZ[[z~o]zo~|'u`@}ge`mƕk7VF(&}h7!K%U[lskSu?_24$[z] ~xp+;[>ܰK|㕸Z.+2K<~ez]3TzE\tRY{(~g!r A42c=́SW>>U̳S\m|`5_wx|-QoQpHJ% rh\nAE;>6R \>y6 FD7bJAx^VşNKC~R'gXmNHԷS9-v1~W ȳuvUi:NsSSO ? y GUDA/A]}4ez'Gb٣{MP5DlHBOoC~9.,0&ڇ;viЏwz`'r=/ΞMȺng3|,PZtA纔KgD)-yt^ߛxVՠoj/FyFNK0BoH-Bob杅nM=Vn{cKC^QI:-!K1K@+D=>M쇔'WE?Qꧏξ iOb[?Up;5%6i4)aΛR4@A H7>w ຐA*vxJX8cF7Ib@K q8KTx4\ki7&}<7 0O={e SPEY wPȏ*Cm`쇢Fjf:7~t!|`cPrB;0i{?^p~f@Vms @Uhk0%š2)ƶV{E۠mlBdZZM^|FfC*ZH}"PQasHm$dvz卦|pY%%e*ҩʤM<#Rݦz^"/Gabٙ}| O'ҢN-`b EVz RdY3\%ЄZWLb/:\3/17i))  Nc$5oAmKR-íA.eCJmZ`BјmIX tD,'@`Ѕ/o]g5UŢnыTq33GNK_ 3[T.(,#)`2@HeY-{>^ @J;_JVNv=s;4D1YYӇOaJ }2%vC^nF.i%7D;cNB>[/ڻ˿_~q_v;;7, Jݵ7Pvtˮ~ēONWiaky:q,y++=ǣ'՗^׭)mŵ+F+Լϗڻ?^ Rb\pRG.S0E6Kq0MB"ȮPwACfz.hhk"AIPC'1*jR;ҫM/j}ph KY =)'66.1W9mēEՈgHZϗQzzg.f>h3ܜ2'b# jPo]Dx-}%=)2.SmB@hFh%&לMzN4Ggv]u|{o$> +zS0 jUTy i%c\(S(N-~M.(ew݅ʯKYk׌rBeLK5WV A* hY#kH&ќ?f놼kD[)'BmˢSoPK*ZXQbj@l5ixJX@3Pd%[_yf?V:WEz+,47ZxLGPE vQe-;%St?!@k ӷۨ%y0f L- kiXgYӏ!Dz=cx<¶B8_9kH qöߴUȡJp@7IKf(/ٝbˀlĵ+Iﱷn<ĥ,1+nYj11`+?e(ЂG]R8M5VhC>rHA/t [R7w Ǚ 7G!C&vg>;;~l^+ HvCRrsi:^?J&d쓵kθaf!:}cW$N e`FNVWlNLYaڮ!20NQj-SP ~S_CS}SUgS 7\^[ =h $6%,4[FZ 9JLFOKRrv̰nW $8N{(G)3errG6lF`dQP$X 03a<&UՉ jmr\ܵ+85{y,FѪ 9CۺJ*XufYZ:;7" xF+džA+R>#D+dk 6S :D18__K4UNjH.Xr3:G4Hﶸhd>!;9!^ݽpiԶfq4#-F8RKݫh*U4RT獭`!і' )fpBIp]^oR[lc}ߗdfR';Wq,'b JI,qzL[Ϟ?}dg{gki$cgUx7vqRfImq-M1*18֒y{=7uՎr޹ȏ*lRӸ9)cw M>q^G#0"e!%UR,dZ"7zߣ`l$1ܲAA^r"BEAΏBR=xhWO׷vY?4G ٻqhiQ:wB%(it443CZv RA. DWbH"t:u]?,G YxQ`5xuc+}CY{@:8܋ $G`"rH=bϦY )=fN X14^6 A`ѕhCvǻK]hHt*.}KQS jd&ᕂC$>_Cg%́yޏ(?g i-fy {w ڜ \HL0I)3L>[ EzX*3?v=t)CWGGA7Ҫ䤴viZ\Ӣ;j]@y^LxqE5mFK?.a`ɳֹf#Z/ Z+OR5in/.{Xd)ϥ +k!Vnsj62ߜ"u/䈿JDu;ݠ~*T3.`b>QwȔPD5`x7b}-Ը~A೑Ljb`;2]-j o%0!=I5dHP^B7`/ z?yYe*5+4W]_ƃ/Vfv&4//YϿ QcսE_|Ҕ[Q?´DgG ? 0>TQ.hR\N*Dz-Jdbq˜Y^_#Cf\$u;ǎy?D=_\zUz^"u\re:7ԲXKK&om8F*u28]*Y}aJ#TidD6IGYseK>S0,Mcܠ )r/TGNzFMޮQ3""F.CNe`콘` 6M.9 9w-c$x)48.*H6(0i̖FhpFdH$#"?EsvUCd_pu !6سkJtmY)JRZ`Z;6?F29ݓ!%),/s{ß/Sf3JG Mf> /UCekN1?BD 1Gbs@SE\x&p PO\`zv\ +jдW/j9NIPbv\&5^m_znRHՑdΛU&ŢE$3; $4rGYJTs9QP /a _ iibLrx\'6Og÷dxqZ>.z̑=gHo uCI:D7CM io*fӧoY-UkJa6@yP}t^i^ltxR80Gv0/F!`K@7?~b:1o8Ae%>&jeT0mf½eE>K,J{):ч >*ݵ}zÕ|hq@0I!M!FIg~g}ih1X/nK~m舛'JY'0tkܵ~20l<2"½{]sC*a4~HLc&} 7=aw#S>{7oyB ǧV.1O19@hySGa~]xa=F<ǕAՏ`km˚Zv=0asWFy^ʬ@_K-@柶۾.ofhx(oz/7.9#:pNR! 8g,M~8珣h bˌ0(4@1Bizl } /'t>Nە#P"Nݓ,[v4ۂv,8÷+w!8v1\d.򺋝mHpRE=Uqn>F~˹76UOwCi #oOc֓x\&gHmye$fou%|[L |:ј@/x\#T\4!|+Bz"SsQ 6d|%G R&J4<<8tbNBT& ^zS8CIY!} 'Yч";V&]")̾yaS-$٩%Қ~M t@_rf _?o0mL%Y\v[k^>\Eյo|o}@pe`;7}tUODy`Xv `J#/BIA}OIƇC>8YЪ(4 ta//ZI z8G;VPK)z*5'AiYϲ/RMQM骱хCnZ9@52OfIޜQ[O8^hvI);EޓgwwZCM|ri1 %U룅MC)؆͛bd\Zm=W$|"NiޣmPAug59^+(6Fg+mД?2=9" hH[RL\eBgf?Phtdr(ј2甏zaKӭ'Ϟ!Jjmz`_J=##aOjSz XWۊM:Kd(qMҫsgN:;~o=kgɽ~8 }iݔ*Tr^O)jJlGM~Ch~y!{hO, @8?I3YDo{B5En %*OwcB&;̠F~h: .S8FFfyrD47B_) }f!hOaײM:\kr w+^VRoNri^R8chŊ^K8WNC]ͮ7K ׀Y6pN\zwTH pDhF=.0633m5e=GU7dر$p5E'u7ӷ5R"=>tnKSr6 h] !H?#FNG>|goi;˝c AӉc#6rk5n?|}kk~+jnGkk~+xUx[w[, ִ]yuO {z+p`AHeWw塇"MڽYR: *}M9 tg.z҃$I}}v O|^(A PѐYf`:"PŠ@l;wP=̀82pf~||uJK}Ə]}XJHz&jy˧ +iςs|MSS:v[g^ 0<9\[gOsy*J&o.F vYOgH\ 2L89r]U|OB5 b< $5^dc3Ob{礮`?ّtA,X`5a-V[&ZN7hY O7Mh`'kCFohߠB]6.`Q($rmus/jl7+J/ժwEQ3gi`QWKRgeEky-'oq4j6CacH298:CY1 ;%# Oc? ќ%enAAߡX2:ݮnyu,q߮ySW5puXL#]ַ?~z#իr%}u\pW\穗^ xYšv{37ͪV_7g+9z6w2o//}h7foq6s譸7vDH v?As¾s5U:\׳*T0ZBiU$`+j+e@k'*̫3= sSZ|z&%p*a8IS@0:ֱ|:g*p}.7`0 Z.!%,q֢D+,$>}DaKHar0\>j'KU`ʢ%ePxD3 ~¬0u船%i =ʼ 5Jy.9 X5*Fwj!\D5Raڤ0#Ex12utgy72@m7bD 97S _%"BcJMу:lPs1 :66jZSN~R.Ә 5Qet2tT$9QpYG&l O0.'p,%KxkxazR_|͠LK[IG oסog<[_P5=MaD=@ L>/ Uq0嬘M){gsF]iAgR s+>Ԁ}]Ci)V^7[p36ߋIJvf?>NA:*r+݁$S 2\a?9qXe4$]˓]33\V:%ȫ2weI`eyMeLSԐ 6IQ.9 /9Hc{'GMA6oؕ1u"*UXUOI('k@#^vkN6b\~^`y s[3<6ժ5JseRk}s$Q{y >BnUj/[]S8#BXS\ĚЄE٤WnإM/TMV85HӶ~ /$K7VV,]b{zWmY=DyOW-% $YYV?04=NjbV3\t9(y 'NvAQwqϜRFZXP?H`a7EpGXA&)*6%{ޟ@A*;6Fې7ɐ>X,K': rp_u6tW"eեեK.RYw 7]H/=|Rljdk@~p߰!?quc@5N 4/ n^e(,!Y 2ы]aҴvg6|:ASF# Gn#Ϧ>-fjy<:y q6\O6qc&lb{Pi7w*=X0q$΄}oq5]XB FEغlԿi_M֩:DlM-c&/(.Q<'% gRFrɲ8<# {6 SBA/Z={՟E)].~J>$[3z}f%4t{9$KQf̀l~(13e69-0!ƕ~0 OpxZ6D`n?hw^\T|DMPU#WmS;:_#S^P.}{zCc-D&/YV>A勋+a9HɁ`)Xl^Az.O+^rF2-1 w%5cgO.9(ᾌN)oLN@kY:Ns3b#kȘWۃ*qfmḠbÄ'X `f.e# JQٓOAڸ>옗K Irôݶu+obPyٺiXxxdk>(P|;v%~}&ݮe,{>5|"zkXP {:DBdC!*1Omww;;f!nm^ P[؛kdxj}24d8{\3^#ׄ#HvC^aĖPdt+50;wZi{%. Wv鿸G6CxCHc%<GrED.Hz?YGzDsœt"] .9[Fў@nڴNܽb]dp7*=rQ5K22HFk}N]Nt8g<ޙQ40ºNIF)  Fp&oN될nS#|HxCԱY .JrDdCeP:nv5ԝOhCaEbA  tZ"'D; ($]y0ܝvǧ?xDbrn߾b\ک%xK@܉̜i Q) sFaW^rnQpe_cR&:Ɂg)f lĒWsˤ+=3~|;1n~j>vԮCη]9"vNz&\-n{E'w)hˆҟǘt @ù!!+-`Rَc5lC4Q F61lvA@)<̅4CW l)kx,<+qOCi(qcvy "Fz*ײ.ƨƒO jHWWi`D0Ց25&/k0&Eib6ٽxI`Fj7+/iCY*XOzʌwPa#5яteMF kf"R q>{;Q,%pոQM;O 28nڮʐ4gt&h3HC0,^CcqL0sQx?dcw Jv!`WTx$k`-В*5Ig%.P@r,ȹ7մ4۶71"EZج/t\btDbc醣'(p7 mUȎ_~4B/i]Wrp"%eejT㨸HЦ?!! 4 ny(X78"U,D̝-N2 c b(XeW+!B-'b·J’ ' ر-lHQbm`vr~|+ ژXR;И:ʵպţ"Lhj pc* I&DCNJʥ+ص>[~е20e0Zfﴈ2\J.M$z!(}{3f}DzuWs;ܚwP$u GX/ Uu Q Wh$ u+DjeGjFN,-w͒کwORK*SߙKSfNCA+ӑ/1G]3ltX.Rl4EXSv8'*nKhVK5qx4c}/Kg&Z,KeJŨLUQ=D72ݜ{t7N~ULzxTٍvd?4D*hCC+tDVTgF$UZpcDi !Ap#:/}r 8i@J67m ߗͭ+|权=R@3Exm1ˌܢ)5Za].;WiA!`# Wu`nlд>Cu&^vwL<)D"7OK/G [ԣXoATĆRQџ#%VQ #)ngT^]β!4!4K DZgY`/ުXԔFa3"e%Tр*c|◘F~")x3ʂ^*C5JTJ[)b(*" yfCJ%sA[YfY۶ eEC z!ɗI?<^//ڟ 'Ѿ;O3s׮|kmmW]Y[k>\ ⏏)؟kWzWz󣕵Օ+wn\rヵ(,tcxlx93c|->>H`E .=r2A*%Í'?~dv鳻7-h͈dMe6a ])aonmy=[67<ޮiW6w[AS>\Q8+*big'G]s1Eh {w1, ^G29gVJl)8:pZ}`D cP jFw;?1T¦cyl 2Q .(v'qJ .%(uQ#8i.ufQt?w:+G $u ګݵGݵt_]ȇEYyx:R`t P;L<|I {ph<SZ ܏iP4bzXRD@H *0ov| (԰&q^ZԼn%,z}")E}]0&b2[M@hTFld]k{Uꔻ T/ S%o V{N难t&s:Xc9ai]HgA,U#?7n݉;=oNpJOJQé'L:\tvD@=(%9{[3 yMUUd5zW}`e);kxx1o-2C\2Zԗ*ѡݝ"76" xt׌=DB8@yHɣce@Гa@̚BnG6t- ZeǸm{K$|nYkv0EIW٢# 阠BWTbm^Pa'㣆 <.:jlǣK#"2(S]չR Z# MoXkB=!$cj0ŮapHjP77L#s%Z O#XF Uс1Tz"EX(4h#p%Ȳ vTz7YXw[fLAl 6׵ uG' qOɸRaBzm^b$؝-`wh-+?ARHLڎ/d64Fol[_Y]7=OL(Tq=}:~i[76}mY3Cڟu-Zm}Rv 2@_:'STeݼm+rk M}yySd5>r#f13C4JS9%br%v  hh+rSV/6XIIϭɣ r.YH&RuA"#\I͚$^[W9"Be`|Is z{٬7A:+I5\]IKu \݉m 0 w(jx*m8z]gw7m[#Uf:q|GB(m٬ ; )_PP\5RIS~8V)$;"pm\8'rlc 9U9|1Ϟ(X`@}hT˶74[?[-w/6R6#05DZjͯ[n ͏,#/v^3udXv](Ù }T1lzgʁt"*{-CEJ_V0$Ծ)[Nb;KE*j@8QK% srwgxpv!U!j ,đ_t2VJ]%i!OVr+Uw;Mguv3o 3 д)gs QdT$P%{:P}O@"V%K`mjyi,!Z+zQdn Y`ʂEl= kҌ%~bkew+"õؼdVJu Y05Ḝ!ey')쯍|^V雁rNWYt;,l1U El_FDwH:kΉA=Yao$,inFE 36| 3{Q@GClNe-㏆c.*M/U*(d3aFҝ#zƸ=%OF4_=ԃ("tHQ$%R{N`6},i\d͎U'%I9ɂ)Z6&䌺J!?(w L\@zgS`5屺X[ۍ:rae$>ֿM_^ P?z% oiLy^ig3uLW.Tv/חŏt~>xi-t_{ޑxԡ9 02ezO392K6w[L1:\j [XbׁN#bX${ɟ"3a7lxH+5<$ri&nM6sj%93W;%z_Bns,%{]g  }G,ީ7urdW%lqDXA2  >K$E }(߆HeC7sCɘ1O6AupN 6w(CSjflJ[]#eʻ: ^ %%sUx/Wf9^{3'HrТ@G?"S,Q07p k? ř(hN`9#B+&~a0/:xVi^n=duTȷj4å-U;T f&^JfD|urJ) + :/:1hE8*ԒSx:,Af0Fl6 <`ihmrN3ɐp+66X.(ѝ=9d,QćJ4Q8(;D v|XZm-- X Wy%m`23R;RV՞flqn:Ag-w!(3l`pN\ib; rBM(!.Sk@>c:\8k&dL/v0Z_ojDD=ߊXl>ζ%cv\5#/*~J5(Dq:.unen "_K,RU*Ɠ~M-SqPcM0L )#Ar9̧[Vh6#6aQLn3s΢JmfyK:ݣNS>{ #`?;iT%7o7ޚ?x+0t ~E8~<̃K%XĎ1ll2wW?w*D^ҏK){i?󥤞aB.<5C"| 3n1['L7ybJ75;KFCkFhU`0Ab3⣫J p'M}I.d֡xT] h-mGi4u9@;CRU-`Ib"vPUfja 7 bG'°Q5Ary01Ü¿C`߉u)ͩM$g&h$I]R5hI5DڅJ5 J++$i㡩iL`aU>yIP6f}JaBq;rMWIX5945EM>4cWI`^_N4Td!R$jk&_[%rv"y=\f;F DCRCb~? [Ir}V/o0K-xPԎ>0mZ/^ӝ-> R_C)ԯ,E><9a)·Ȝ!DG,qHAVL,ә/eZ6RMjp> d9]Գ62:cT9%/B3/ J09%sX-c tcC 4eؓ.B ioEG.Ug~*r,|*V$;k׬7 9,̞,r"5Fc{qR8S(Z}MWY1,Nx9Yrq[)#yko׫9B "-uW4jl$)"< 1elДe7 SH{KA0qe.~vɍONGeAPJ꫊Y=ƴEo9i(, :,e^>^ Q?p!J x;/Q>6j)S>5QS ~5fy)i^7ݴرڻ3;v9&J;MʰӲ"3d:jwGqZ{YK]fB3k,3֙gս.)gKxgyN2Ury:CӦ꼣S'؁#5tN73|ZRu % T^%lhM_CGy }ޙ|! 7O_ _wC{Pv?EԮwZIn:&O&A|>m 쭋8I{ЉԀ|HɑI$70nxz pU&?߼66?l'/~J?k!&Ugh#ܑ P@e:hCXnl\oDL3mtp/üV#m2,~o37os 0U8c }AӜXĖ`LO!X^AJr,T[7Kc@+^KbLn9@dY`DE2ܫN!X:c^0ob- 8#0V~D3ię vG}qV5(γ#Gm ǟ1zyn+nPݽy1r KpEZ Ne<} `yHRJG8@UtQyE웾IV@=U N}NZJނ@p_O%{u,`Q4=ozS_:8~Ϋ i+d~=[m}Ξ5gު۳g-r ^&PN^ R [̥ID'/>$ɳѡbdeqʜ\ _~Mm՘Ygz'FX6.t%x^L3sqlv4N xObap g'< Ö| q2WEÄRzi.iàr¸+_=,K .h_>pc%1h(bK?şwTb1οB)ʴcgOlx퍝j]r|žJN\֣#irs4!<>M& 5N%EҵQ rsg=4Lft6ueӃӬ(_\_..jо q'o%Xy>yUЁ||ahѿK @d"ic.ޡ쾵+W np8Kr H1Z:jVr8ۇJ 9?b^ C{ar}lB/A QVP9AOgg8gqEX T'$eKLPP-h8Y0N@fl1omzFkϬ+JUʬgxc դ;\TVH rS2CV~T:IL#m1ũ:j lvs )1P3`[dpgƪ E#J7$6=u0?tgyA> K#M&D.k9+1I+߹{x]do3Ut3 [$/./(*3A;zR<3~-+v+6ؤ r]b_4Քu`s[DR1J`ĔXHHA_WĻM@5PoZ#X~m%=U5*:t8 f D\ә*tZ>x}&3#>)[þ'Y?c$$^BTxNى J|1yVQ*LOq|87s:n<~BfqVoU6-%9rclC`CWb^%'$Fæ<ȍJd?ɸ1' 31b-W3vzU?(FګL~/t 7oڭWaD5fK(Ez$e3(|Vœ#7 =v^Ci% m@/椓W%vIޏ.JCE*:޻7nackfU#!5˞Ϸ\V%Wz##7Qeh%gû0[m݁[BգQ9Bj_[srleG|I6m<,|r7 >hI Ӿd̘L' sA6`r1~3ncTs\{e?^y3JZD/ՖitW $nWAfpK4+d[ft<g WE 4BE\z-_Da7O%?l\S~ʘ/#t>=/ Phjb]YMrǠJ"gO`l|2zL‡+m/iqMFT-ME͓$2Y8AhxX-_VK^/*'1cรaVd0ʻ^];V0֕%fWìI6BzkٰfGWUvP3=+5WKjJLMUtPCP,ORQeu&|y:L[0%I )D-ܴ*GWnGNhX*UU+__mɰZHzdrmnk5W%2|x%븄.eRgz9N^ţ"ңqMP FrMQGf_ڗjxٗS(M(rg@|3_7%<@G ES|*LW /$XY6ԗϢ<{hxڃY&k6G 4UdÃ_>FC:^j#EZI^ 0/1Y h /XKYFva{8mΛD_=5]{x`.p}/l# x[QscLyGy'VHlSɄ^nWɼ9Ʒ@''oI8"s7&%8?NV.j[򎢅:kw$hβ}֊9$5 =έ謮PEjxRly̍J,&ume*y=AV#Cd܃N7H82yD8Ak  ; '/?uZ4:4{'׌~i1R;' >֚)_igiϐHy֋אLiҜ mA>T,Fmc [T^--˦u} 3/*KZ,P݈=u:K"C$8 N{/ʓhңǮx@fXx)|&~vW]?oH(shhͦ&)!Cs/㹎NƖRK{mu["r_)S+Mid¯VW%G5o @+Wa zᴍKEK3y.đj2)s:e!M̺Eh@̊(Ls+RiZ"~ZNoX5 +[~ozWѹ뙦v3G?]Z1޿01S9/jB2o=Z[ ah+GJJ_cRStTh4B2ˇy9\ܔ濙/b&ӛwxGvw{Gw U2^j\ W8ÂNnj3+ZND/)Pjb;`T5sn9xSog7ʹ8_8&Fet,Jf f,(yY<-w|/ӼOQ hc髇kYyŅOx΍tEVb@<{J{:bě_'_ I񹡗{7xZܘ~マ ʙ'Qߘt 08s%&a 2v>\t!+SןOO&i̗LPw $U`+恪*UW)K`MD)yˌGAbIVH{ONJ3䠮~A+9%OaN]-]-\]uEAL$P|o6ʛ|1U♺;̇.O7Smz+}ԎZ7bp^{}mEӋt&1*6ӝu\_Fw`"OjJD TbAG"zi;],RX, C+dF2]5qHv͓!kf0GԖ{#_7FY'.h¥7)eBi~  us><~y_)3E{h-/ȫ||kj7޻ ^Lk0~!. ウ~Eb=^ϔF|eyɈHfWɚg7M9 xw_^T9օ(\mϺ/A9'S_w#Va_n:zQAML Tybޕʞ[ .y ̝^[SjKc)g7ɒʁ#iY5|P7*.G"B6\H U އy) =V+4>˦.@T gP)\fj%=<kOK1^]W ba̻ǽe܁Dԁ?]ń1^tB1 J>__dAv/ltlo(^} (gR?;Lq(DORsVk<>5уڃuߍ=k٬X&e spbna͜*/CS9d`Jf-~aF]06<%eI¬ ]])"$aӛr%I H+Ҁ4_2&ǥha,"􋌲3K' C&3r)D8>aʌ$CC --Bi,Elx yc20.P@ ]`0i2cW&E SȄ݇5 EM[Yg"кa?5C4)ߤl[ uf܅%\[f dMSf!Tsl9H1FЁmm!23ڜ4]0| @O*3c {^LN*gWPl758\,;^SrUfx$'\l>XH0]"bR)駃U,)h f''*aPz,Ū"j ꭩO& Dg^$1L c>pnѼL FvX`;$С0J #?LۜAv)_PX}H`fS"Z=BX|2Y Ŝ@| 0C)D0 H^znijs$>d ͘#8-ߕ jJ]Sb?뚱Zw& 1 Y wH\3 L5 5k9ceu:#Ε )Lq p!dL}HR8h25X!1I|9ʙ&/bAG'$q$d˕PHcjYfbT;SkCIk+ w yoIrMf('(n XaƽC92v0mjɏs }rv> F/p iQ h1ZZ ?[_%:ND }M]Ļ$K׀%ݲpc53ϒ.+LRd<&YjVFeLJJ='If2UZ wQvbz.<#8.  8RO ;_1e=:2SoN@H w^0Cꡄ޷&?;bL$u*!\eBAן2oX#30{(Uyt(S]1#G>ZisgouL!_UG5Ls]9Ds̍PhFMpn(JB 8op+Vo*TO |[芊dN&KH]k`]pp3 gI dR::O@1!B%X3t13c پ O'!YQXZ_[_Z p5Z1Bi'I~Q /HOIiP/]x!5 "9`&tLMU y>DXjoe{-n1e`8Υ!Ai><+c LHhLkUKl\*2 V]Џ[.-JyKgqң#1ܚnjt<2U, CBy3/6Jo*mm$:P'LdDE,Lw-?yIny}0*J'|0xrzY{A`O`Ew Z!Q CHW`byvr+?8h0P@!3;+#Ș-!}q>{ž77{q Qsd]aB^:M ePi2O aiM#eAw]$:|@ǔ>UKDžU$0}O>,ln`fz5](/9v>NaZJ2 | p!ZeI1Ȃ$Ny-:\٧K]H·r$KMݪ8 )݁J:.AM)@F'X<:<2'KUD8ZLf"Ro^o]&>EBjr]E9RP xPOdQ@YL{@`'ok56r~(+A2-.c>n᫐3X3[ Dֈ8)]kžfh2#{%֖El-y0zrCǞr"JrKbhs%9}M@]'V Fpg0of6&vCZGgD 円3o mm@Nd>|2NX%d.=$sUIRIbOj딜Uic,}C:?YIW}ۈ<B6WYo>Xwy8|7UMѝKqTi:5Uqv6 %=E+3CR@m9bF0׊YK(GtүP]ER&SzxFd=߃I|FFeM0ͺŸn.W^Q .] IjUCBn1/ýwD}Ä%[+H&pK'VW/S/1cBkqN"dF*OJVt2E3 Fתe %s+'; #{[ R]֠wn~Q-TZOy=IpyuϮ.NU}l!^ťCrnb\Z$>243(̝)߉#O(Åد U2-NJ_ J2X?_C<] o-Uy BǺ^C _ؼ&9az}bj0pV=NG6B&Jw$IF4Qm|lwoL,HOZP.2F&WPlC"'T$yphfRqB'Ocdk^$ӌv2ӧWB~KLձe:h%Uz|Au 1B6n]̳I+܃[Z`"ѕtb٫}*$N rfhK&))i0s.գrWSfYd*׳r<4 gVTx8d56R re$YaQ$P 6'yTBL.}e,'fSyW2 sg 5frБ]u2BTȓeK@Mff 5 i8k'zr4l#N-VӢlɈa٭uuUD֭2ʂ`qfά,%6M ϐ˨Kmn8vYL<}GA c)^>ֶΰDSb >_%TU'kf}7_̞/VnNT' w'U#]Ep?qVBR@(!䔶;W46BpDQZɵ9UfƆE`.̑\ZS{6޹Y~&,M-(XЋ,IIv;fF\4!>?][mh"!2L^1*lyy/)Jgቻ_ژ$I`πB8Xd@rk3낊?r5{x=h=j~aB|f; ;SdMw?~qDϷj?lh A6cRy{ ST=4zULt|BG2)'-},a īڵcvG|Y#f*JfFMբfCn=ãO-1S|U<AFpіRWSMQ(Syet 2c.em@Q"< 61ѥV]XWD5|JuSaFmWUЮA*=c $j;pVDlCU~o;?q"H*(rvJQ2I):<$CCVm5mѨOl~x'\%CIQm@U_}$Cp2.!,`6 +xE'(oR,!0HAuDzb#sJC+eݘ1v; b-uG@exm Um(rl1-SD-<:+!=Ȃ{DzgV_X/<_y&R`PƬ` I'8N)`?B4݅b]`|*(iv,{7a k'?x9*EGSM MJx}30*- G+*p!iȠJ }B]Vk[ݧst]`iV;E'#_{^%"@0BIL|xREy_˦rh# %i!&ֆ1sWH=bhc\IŮ6xM4%ds X]!̝1lFڬ#neTš nDfVIF1%>u?)XR` bn.UF-T=@<+U+Y7FСRm.ޝ X a+ .Lr{ .B_ {3s4Z͍{Blh3o|4cR|'GE؍` ocE?VV# -t)FA=8eD59%DYy$'ݦ8FnS%/vsu}NH"f FCdt}J(<&2Pj* ˟L3EQ-ʚA΢VSF8["bI Y*E髆HݪCEvhI `^qoJ""JR' 1ŋOC.X6#}ZnEO{.3,ŷtDsV#tb\=R^ "a*>@ZTsw6:)۠U_o=4I[a;w[IΠXhf={X8IB`܌ F1֌/wVޕr~)m!>a"XX+FGd$wq w -kmG m4{ 5o!JZb̰͌T|:Ќw7I6u*EۂHTېfnqh!U W0$ O3 լA M92%,1 uc)ZQ JlNH8Vf$qm/N]݂g 0w1ei F-.Xr.ݮeXL.ɞvm\ȶ]btݽ0;tnOS)VΊk8]υ@fn#,˒=s䢰% @E^yUb[vN_(aCa #2 ܆ߩuGmGմAu~MaT]|&o Գĺ!?TC#U5x*bX$l=shXav7z-nI`&:dUfɧㄛ7ĕ"2vWdPV(Rȍ+Jeلׄdy}- UMiFlKpfeךH6$Gc)L |qM:q+,3%.bTGD\^7bĶo'8uiɵrv޷f2p\b S Jq?d]VӟbCvȏ} BXp@Nknb# DlKB0si9[2>(LC8܁2Q@X-ҠG94 PT]7⯢VPK;i{|^skC~ 83U*vN8Fϲ6@O3#{ȤҩV) Rii.:?iGHoRA>hd3|1j \p?Co,}H4|u-%b%)t$a$=PvU02RX5( B'7خ1 ~]r%;h}SNԙ0k*1bv^8.] CC0C)Y&fÁ-y[a>5qkkhE UO |l]:SJk*@΄Zni)|U‰ࢰ<n/@a)4CWPZEW^C-zE3^ȚTR4{FJLG#(_{܄v`i&oŔXC8J~6fFITdǗǀWlQ=!dx+̛;3}G ߉6wï} )R!>RN qm>ي.]C29z1S7-HeJ`oV1ojc d:du@ 4Ω:Ԇ-0qo.PWE9x49?d#~䀈곀ؗ5ǜd]dÖL`:$tR 7u+,cM:'uvR!}Uۉ/M_\v)=Z3 MPʩN\QŸ(ðdk' ;89i"^Qew= A0:r*}֫gNzk /kgUҖuUs"v}q灙lxTw3>W'bruAw,;"32';5$1Ө+O =,Tと^6D^]OGmhv yٟQrBŖ?t?pe^zr@v;n"-d=:n' >}>¿K"ݶtlɑW|waQfbucjƓʝ6r>b,9R(tUyCIpj6KK}lpOVM5s3“҅=0-Y[EMCu-hNBʓ88kkw^g%A>~\Ow t{@H'n1&~~Yg>^y=X#Z_Wݻ&jD9U]0-a-= BN,C.*пОfBqmv =UOuNḃ%7nc',בK\&Ĉx+(9d2]_Ip( Fٰ+}۠|r|4KiR++xO^>m{cA/5}e)?[?ST7:V9 zԙHy7 OR[G .fs .k2fO'I^U7: * ZYLddTZ73V.UK-+<4-+o8IḒ4a/Fݩ|@ {?Wz|sqLeFN'^}X̄%<|oї[ӗvAq_C4Ë\6B$5 `5Dh{§-Y~钂r 4 8CwbۊP -Gf6k[6hyQ9Ϳ!BtPu5 AU&cmvZ}2gae)+$ɩn**/3$LzEZ#p Z_ F &iqYY3`8Ȱ֤vw %`DCkl5$1{4r4-" ]! F$ h9d.`oV,&u@m1_¥^;?{wxYw8bhJeu ۰p 7Í- ի[wfXjU # U2\e747܍' DCf)bV;֨Wo*Lm]DegRz8GF0RNNZ}67O3F:#A6N{ftHS>`I_et2BOnL4F*癇Mbzfq[lV(c>9fXszp [\ż^0&n;w8xN ҕfM8-~_<.-!!07$0M/&nK|:R %(ȷ;ƇϞElJ6ϲIe$D6QcqY5.iX{|iz`h餤׍aRJKFR.`NU'zc}NWHT \0f-$r'ߒ#7&Ѕʲ!8aעQ҃i5ԅC,@$Sq DsAȔ@g.xmA|Ljdw~σułV -q'ݑj`D$岩Yָ~G)i~ġ|c-{|r6 WMмNIv˸BxQrXzQџAGY@/Qs P[w}eޛ}"|,: NʯM:\} 2:KC*V,XŖH($puQArfi:$uӚsavOPj]šϕH}M/">V pzeA = L &FNaSUꪏeWeΓ߅D-kՠHWlHq]6̗KJGkh__Xwt}t>40`K-F1:H>IIV2<!s8π(vƒE.!Gt}BTjկ,\>lzǕHd Y̓2QODNC[gu6y剕rWG,^VYhWqɜԏN=/(ցӂ3>5@aU1`?TVɬs'2OALU'̴}l52>`:x "T|dx'K/5tb &(>FcɚL5A Ũ23QE iS#P+] ۲«/GrFs#dO{o ŵIky,')x?neHk(`py1;%|R)ccr2A:ޡǺx&Tl9P9(jO3ѣ#N Q Dز!"]%0(uI?' in[q>T6i:Eb)Tj][`OV3PE,p0[~- $}8eJIPfA/ qPk՟%Jܼ{K$mV&oJ$ŗ:k oK*BYD><f% rn21C僵yHc#<6dc<6'?ز; ~zkBA찞uB{,aFSa 9TNwǂ=x` oJr#V pBf17хTXm j4fŏŰ/cWMjʓ|9T:_ *'ӚN ]lA̻#t'~K$:حo!KGnHj1ܸ݈$wSV yRdD|5aPi6$4-qnv]G+VLnF:~O +<Ȯ USb'nw[;8.48)@߮:*,ꄶb.sO{*mG6Tړ#b,YvxWa@N9#y3oCOgI!lX%3/9ϴp??/q3>gXt~Ɲڶ]ay+qgML8/oc*>^i*ݬ8(NSrP !ܠ wwgfkgTB,bq GO"[ʋǿu:]l/ 5cYwuikfΌK'%V-Bʈy>xP84ڣ#Մ?z;qdX𤵱J2ԨqؘHZO9BHQ򀻣`sحkS8LA(v8ʚ"c˭T 3"Y<Ŵe3ir"fK:"^4аhG"ޑa5Դ@eA(,H.8ڿ6J"0#&+(EG>܎+⪲ZAH# f1s K" U&Aln{/`3@rZ,k1UN0Fz%)op?BA2-JI1i9I HwV-hRr p˻;'x(z*ä6.b'-HNhxwHh޶ gYBS}6Ek¿¤R ݩM+Tp H?Zm6u^p%r+J֩=8vCӳWQ9BH5z#ܮ/T3?fBʤ<~_*f)V,%MC8Rҵ`_Q(0wAA9&Tw;W,X$:† YzXT/;F4[g,nq`Ө )ˌ{b8.9 ^00 JʍG#nGMi̲:K{KֆЄ HWY?̈́a0ko>p V: q!@ZA^~܂flW#2sqV'oZS2-I *jUt^h֙O`1T0_]f^Cpd5@a D CjU 5m#e(_FdMse(ZPЉ qc3F!@j'[#[K XeҠF;ܞ+y =kp25 qh>+Fϐ LI_9йghV*`T\9Zt6  .kJS F4#m?dtf [[Nm+K=EkTk#1mZbu-GD|\LG!rԢR :.nP=**xދki S@0 i;$ZtѢ"Pl[ PuqAIt 37AH1G쏹|8Gn'Gwz U2s,ƫ[/񨢱[772x"A>xNqAǫK~b-ػ b9qςWS5 6 J AKLegDs(opꗖi > LC}nR&S/v{&]h#a'] *pTG(*`>Lo6~BʧSW7HI(=GEb5\/E Y;Р{t4t*6)`IS$o7]-E g:z1w Y*G= *H&^L-Fӭ^1i"\pd]CHLirYLy^!f ,s6Ln֬ 665C)q}uSqmA/kHi}+.9u (5+sآr,YPKn_sKVp.puF=dhfSa@t4@3pTYT(<0-ي ,9V 'hIg?ܨZe!OK0ۤ@ ll|D\L)-= >;G^c‹A3#N?teb PemY= $ri9GP:8qЖ^yl5OxEL 꺠iǨD/ǒJTq7ff]Rٌ̊a6i:+ޡV}}{`IAбs aS~[hWu-S|OS9Ѵ91o&WZ;+5Ka-$ePϢSv3zs޺iՁR,r / Qئ.᪡zy+f="T;l> K/AnxCQ`#@kPsxr%j ŊJ4RAIA`\69-Ҩh>`Vy;̌H\2 9̾.DYdDCָ̦3N MT}w%&*${~Zp@@~#óp zQ)L׵1nί|EgYb,A`"9w)sySUW";rEtrZVJծ|B p7%)Qdžz:=¯}mUIZnQN!p%ɞY*D8PYc%ym?T5wGo6V3 c?k?&^?¨Z 6q^Dn{DW.˗YT#>1iMOBS6CmR_(QĊx#1%d!1KJt:+uܽ3 lYЌ+:Ȅީv}*s辪B=4$zFHt:)EcUoA*Cp)uuh e1} ߩ6Z"$ᡕq=gOߵt=m.>@]v_<.5uzmZBKq P3tyj1VAr~LKǍUv*X Й?9$ghC;0xZm1>ت`d|[qۉCtbsUBNEh$m+".%'u[9񲶒V2"Gr+֫G^O~'WozZU ι%XqKPs\yISvv 5iKKcqg( rt }%]$cV@ki'OL;o_XO&Tkl]Rc{u!ҫ|4iZ 1-|g"W\I+(]lb%Sʽ|u%H@5sZkiOvj*{Ā x2Tm݂>@q,mxYCqlrR1Ό,ݝQr1#.kmg9:ܦDɌ(z#e!#TY~$)'!oݣݣ-EhmcAH7A3m3bmsQەО"=H`ņR]m{"Ft6< }$0ֵFj=G?FHn7{۩gە 1 s}xУ{Z*R{_ONdC~/?<7÷R*HwgAk$; 1*h,qI"dއp!l______ѿ|4)BYFY,ŝUwTW"\iܫ̂oHGܵ+,F$1PΦ."j*vlaԚhko 6 י 2| $eX)% f!8Mr$5My66x%&IwėL'c쓼pV!N-]IXPu#E&:>~cnDDz-ڍ_p}?:l&ݾ#!+v5?m4V&DX5Vj9$MΔ% rY~$D//&ϊSaCg3kM6Vl+@~QIc$[ߎ-Ljhj'HdRfbh-]Xhi86̱eY0)Ʀ\l3I%z%E1B.BM2~isJۂjsЀhnVA\/ȨVzzw0~6~Z *b++FȮXݹQYfKe:j6j@wrvRF"wmE+]sdL@+ôſA?٧(ʍ#lZpcQ6S}U'a.#fb=sĩ˩-kʾ/7m>f|rno_MRdw !4vcRZܱ] wEN8:0EDh$5inOV^JlO9WMV՚J(pʀ#j$P픭`vo.j}jebڧTPua@^e98iN;O+FLѝ 6VKGB|^,k`\:`f%̲|6lm~[fgtWi|6arjxj9R}9j7WBr}jM)U/kwv6\ˈa:{<"v'Gۼ ْpyi}AkF|p$;;MJt(aÒ7&ˇ߶ ,/RAL#[b"}2; zBv(xgn6>fux.r^p:4dWYkhj^rkE Il,b{[vόQ}:bұZHbLFvQ:+YP,W1vzZתAp.Ĩֹ Pm2Rdņ*`_ BdpQu3[aZyuD2^9~S%q wy{)<)<%ERmť+EԼ~בLV6CXg ϦsGњeIgV ѡ9l&K{Keswtq /7'[+㳕~'k+ߪn-[6Oa\)#'>YZ܃Dw;GrNE%-w$׽L/"FС?CN046͌mN7'xj2>2IV$%.!Qgؠ-,m5ºhԝ;̱ԪXmQ^!Pe&}o|i&9gI<} Wp+EOճrtPƁ(Q>2p}^nt5S8 Kp Jr77F|^F5buWʨڃp$iT֢AnʽgWfKSH+ oLs=]}-Bv{wv߾6߭u^hZU ؈gZa7Ngu`_{=+XBp_ fo͠b;`k [Pgө9W=7dw/AaЍ` 9 鮐Gd*C,ұ9u[䧭w^vw}m%Ȳ2#k1s?U2!4#q:G3`C49[ &5ӓw:'݅t^Yޯ4)8ʕ~Jb ZJ8ʃ<&):!36࿕9oküg(:T,>$磡%;8CmWN;T(x価jzR/Iňx8f !,X̅M҉ 'L >375"EҘ1]h!{\9SQ>Rͧsc4_aJ4Tft6gJt0*sËEdES);՗ZRdڎRH*]&i`[,yd|HaLPWg|zqL *uvc=< /~Sz@O>Dn}HlI˻܃ܭvn&z6?:c ߜJɇv']X K:oNEiuM2 WV{{qϏ%7SBc.}/<\ 7xE̹5qPR EijFe]+yhb;V@rʸ2/IוUaYAYw,Nű`|:p)=r9[c%ySͲJ[WW̃rBHo @>-%gr_QzY߹J7M^eF [߆9 ooRJ{[CXF|Ŷb&.kL0WC#e^z9ʦwi>jwe*\ `@N k;['<!}b8y67EB穋-1H1onwQοL[nON`=_ce6 YԘ͵5 \9Bz /}Ԝ$lZqrTGͺX02cFguz;e*fB="Y!HN$Һ5VA;r!՟K7a=NGy/i/adA 5t'9.UGY|F,^#cyEMBó<ݚPZ`m0ne0'$f%"5W9(1"ŤIVfţ"uz=G+8_Sy 7W}x{#9 ; Z;]H\Mu$bOI#Kӕ>,L~`5s_[:AMz+|DM#Z=l$} =\) ɳe,WzY]6dZ\ 2AK҃$4(M b4YA,!`'m:ci&<ʺLIh0[7lsg AdPp!T/o 6b)R93}DĕI6#]94e<%򷡐e[8{[ÊtH)<é~}0Ytbг6:͙uw<(ұˎNIW~/_|uǙ5{%[G CpCg84N`-p\6K>_jAp=˓ XeY2~N RO Kzaf YdƆw}QNbn9~:6è8 l$67upfcXy)hfzHooVd5$32(.~$骞ue3ԟhemי h9N1AW11e6yFP~&}i߳7)} lHi=ͦ;^=na떞Dʨ9sʕ׆ p>  zS_B-#P=#!oS˸4YYG@C%JY"'H9`{ӌs$7k(iL+XŠ ,WO"0 wYnMLh=AcSL:i|&uͣlޟ,8:Gy^v[WY|XYk̵Ӫ qIP;WW"Wvvad2Q;.{PygwT"o=v\b8HW1M2X?՗]ɌO}!L% X+NV5 ;ݣcqeA5 `k_d[_v1ˤ9}8[ ogfAPPa-W Jq'B˅Fxm%Dֈ%6Ӱyߙ nr)2X\ 󕖎'֯Ʌ> hRCAUGlP5{n;$̏k҃ T. t; LD>7L?'k,:wS .BÚY:#3yoo&-NG ;K\2D%q^ eE!V›* DAKrz`f6SD^gE C'ed̓8l9P̄sJƫ?HPbg1bX7?=~:m#p}7ᷛFʭTF0uH!;;Tn4XE;|ĂDW%ܿW Hcs4"eT]/43sR\Z.0Ϊ/ðinf ,@x$ecOfh-"ZpMNˬk~N ;ZX̣N=8Tf uܲont,D|vp$Ss=E^s/VC?b;gP5}O=jo^ޔa;b?n ofۯv/t~:3λƅϟF^Cx ț- H6 t&u&L;x0J!+LD<?Ϸԙ)`Տzj&\ {j>TZW${u.bA4F_qFdN T4MΧٳ%iPoFijvH}Z;$v; s#M:1P5z3Z<:"M,8Bkw*Sïq\e6=ª -]o%Lje/ҶMŢiwpщR͌h/8mOQg!*¥CqAI;h8STL[dSbwm}u'pͭ%[?g  s!yF: : mB5G>S ~AS_&3x-?tQ _lF3"ƿۄlX5/!`Q"/oX`e͓z_aa_I\ۻvBkI~Zs,[HXK"  &]yS6ˌb?[?kZ__X[{GZ[7[Gj)AgƷ,B+)ׅw!z +sr(-& ?%W@yV_XKP&$c]r(׎0mF!V_ 0aaY<|^ҐY sj Cf`ٙa(dT0 &K QH+gT!!F".NֿY3#6>c!!cv@x1!>[\h4 @Aϓ`wgjLbՅ3V5]Űt;O!AXf9{LDiϡx; Qj#MRu Ǔ(L6qʩbVSHpAt,a[V a#h_[,(rib*d8EV _fC&).þp.It''a% ]v㛵 Dx*lϽg;&lQ T 1N""^9(쬒,ep9 ]FttL3`h$Sۇ3 \f$B=)DKPe%<[m8º8p,<5C: Zie2MNRpJnLyӒ,GnR9K _羓8VJUA~Is<"uWu.`<2z48p&8tON e;.ŕ`i啫eO`\ G`ϐp;}ݳYִ*uoas,1VM6&ow#8b%t]}Zڍbjx l̊R*۽̲:zqY PBܖMi|?2262؃asqEKV+YA;JNt"|p' zpUPR!kF0}^'dޕNņ5=icLfQ> kkf$*@u|D`څwr4Fh[jsp;w'@jd\|O4.ȢJ"L-!,Aؓ`c Pi0` בk1B a ۟4l ^R5Ax l M*/ tXsLnP#¨dk%8)C,I 19VZ>`p) +eArEM1`)1&dJKuu+e/}=m.TVy= G?چa!ѣ!|_u F>W'k #~S9lxe離 YĽB̃R[o,^CP! 9ay\0 |$m/I_C'  Qii<f#2~iA%~x2).4ӬiK%lxI[>kԇOxy 05#0+\L;r} yn/y$Ņ|DWCc Xpit:rD幺 -Yp,w~2WÿvG{KIIέJ6µ*j%6̬Y5#JdㄨE]i6ގ @4"Y҉w+|\@|Xsp*F"}NT6bAo@ԍ"d `z'> N^P͑ruCX*.g/[)hܠf̃FP0WnE`vа/`1}$P_UE>&"y̦i ~?8O6nGtc=+P t`TR$&Kʮ9)̎".@^W4}l`i4[Ij$8GSZ.\gy]Ԁ[fq/3>g4x3ڈ(ȘKEg?/=~KKA;c3tc\cnjʯf~62ݬOQZ; )-PL5UNb*E=6JCٌSszd쉨y7*"0]ӑbtnG>=KU4/)ż;3;6}$vi}xU( NH6DǯP|rH$_ ͜d|YIw4O1)΄mC,'#.?8䡣鄆M\33xZ{/n eP^-^Ef>;͘5KD. bEDzޜE%wE" I^$؛&*~'}~I$lkf||;8+E^jU 3fiJQݕl6;jrS؎kcW|=[pi'j ÞT7Wͭ~̆5cOʇ=aMm:{3M<}`{ۻKggԂ"ihdZ4П2 fWC<1EGyoZ(cpBv !j t¯7'^sAB|ne0C]X82f@ YĥKbyS`ɭ3c)L:K\'|Ni?'fbLIt>CAd^0-D(S"S!l q5#Dv %Gu}J@``My@:Q܂e穲o[su6RS! 'a8=8\UT0M߸LWe #VZKnõ ?ⲕ &@fL{HUۥy؅03 n?viJΠuw҇/UǼGY~Ey=d떚4NRBr.jgͪJݒ/Ng:]i K&ISHu(A=!e:@:;TCPa zb\"Y#W-\ٗ%4EtU[]⸈%Q.'DڳN m~'bI, wrQ8QDu=gE>M VJՃuޮezlݜazkn-EнsjۃC/C(#'p!cxʬ^Z DW*^B者 m^׃{\bl&o/hu  C7CB#q4Sb(Wh> PU|FUPm` A|b2( T8Aʅ^-ivzH^d>t(olDgGص]Jڇv<b$#ϕg9Xԩn~!9uٟRfذ.v !"rn5VT3+ܒd̒e:헝 28Ȁ` 'DEYB.D0[Y1o;CJ-N>3>Z_}0.*0@7=媈^-&؜ya쇹SK;;8*1cY!Jw_98:`*۬97x_w:-j:@{:j.' h7$`8lnU]zAyMpt0 bT]v~AҪvOMuzjVݑb2t j}̄SOy3J?hJʔkPݔWB͔٘# O31ߎ8 ål<nLq>xmz #GjՔ&t3>DϵD"<2͆~ʬ8 uRTI =݄?,Hx?j|QDCސۉ.~g&Hg|(+.c_i~v-e# ed>Y$ҔOj=݉(C%wZp61D![Q@Pξia|#ᇗnHa'%gN,={NyI23'ߙXf-6I^˚:ϧåzE'5,ofNJ^P ej.WwG?xWG`39I<OtRVv5pLAIfOSH8|B}P^pt:AM8ՄSX:k%)/% wLCJI*|,=s'9$|j%04T$/P_7^{x{UvT(SD!ubF#ͺ>Xy%EAܐR\~ﭣ2w"zænD.2<ZsO~޺s 'IȧޗΟy8-w?-"7/޾q{/o0w *n^:nnͿ#3d݂F'xBʭy|2 fyyjKzkF-G-U ^W?ޡsNLO&2*bo\ y?ew\_o-(pJ"m1k~-^OT^hKU7H3 ΐ1.5t*At#<脋" H"sz߸֜XKrbx2k d҉eKUe†eˮ\jK9i* DKG=gJGgWCKLzB52=Լ^*'ws2G2e9SFx݆kPeo $xx[4Z6ikJe`_DVY ."av#>?:>?F2kw[K$onl؄ W$L3K^osCKgKOU6jk=2e+ d ׏n,ʆ4=[%CAj[hlLhWPFXgv;>ˋ: v~/h갆tEo5%q$EU3梭)uảSRȑ*mQ",}EINq5~6 -\+7r=le"!PJ^ꏅ7f'Q!ԕ#%ێP(g#CcOѢ+oEʦb׍`bhMdbK5wt\pa tS$iA]"$4 $yJ'Q fٓZfc Gε=3~}$3K{sBor#'եz h=Cy,\WH" .!+8uڪ4_v J]w*{O}Qo$޾O3D4O.!6Eg'N\]s9%&rŰOK%k0nYdJvEH拑r;IeDL48y"OPF#8xIbi$J dKEeYHcMђ! |3% oi| :LA6n9p|<2S![O=b^33㍉>2Dס(LnP{`٬k;W1QIûFPg"BO&zW[P+9DtS2Tt۟ɻv5j'fdCܲgפA&üHf@SnV&(A.(PncDGʀN@?R]meX}%z95H uB/f+pW"D7%C%(HT~N38X/*?&-ˤ`=ifH=e:UIMoK8rPAX.ٸ/]iIhtc⽉CAExJ@7Ẓ*6 hǟ4F{*ЦXΜ8x&fܵ*GBi fA`馌**89l@-cԦжo}Q !wUZ%FRa&0S+& h WA K YK{4͜us6[q@4Ql@nr>Ll~s8):S&82>i 'βZۦ/ 6-`@4nGŞ e n Ȇ9Rkvk,k{|PZ^Meõlp.btz7{oiaɍIԩBmB\tՀvdޕ~3 63Иm 5ZWgDlD}xJ7ö ?K78ǏutџMGGo(CHx¤o/w6 %]N%XJaPPN lg~x|z*l[7VJ3vG! Hr͚ RdVrW3S?<b^7?ft0 SKAgx3 m6 `3.m0u5ꯓ C:V̓|2ZNPEwCb -+;!%y4I~e0 !ZA|8E5k}=" oQVOvjmdJKO VQ!CWk$|)@;[_f|MʃCc)^3!9awv4X1/}ӂܺwYRxKf1I&*XE_.uEJ} aY|S<36AL~[+p9 kw]#hsIE ]/ aAnwwDwwY e$\wp?_SjKDFS  ]|ÎiP.3X sn^C]iY;u22MCzO z4?;0<#AwHP'^G 3c?㇮Б-M#i  F&9eb'9Azy'x hcvsoye/+Ëkꫲlmǂf`-1vV2~հu&q4m[I= ZCI,"*U]zoE'q,MCJݳ7}Cxޕ˯Ȼk(°@v|ITFS\O$XO1-S"Gb tIO(L0X8( I#A J9C1KOyvO%?J)C!5!B8Ҧ]SU<j쇂1Htao_[&&BEj8>Cm&NrT$(xƔʔ@e`32<07!jN()&"S`ΟxY4AZ"9xK3#LX$D0+3Ofa8̹Vi/>X|Go֢җx( 8jzv5yڪvu=ɏmÎԄޝOͽ\8Aĸ }vml`y~2h'::{&#.t.E)?7Owef9Ca RQ%V"{ޕ٪␾WDd$9)Nj ,P=w#+zf7]Y5HwRiE(Hy=O1Ȁ9/2GVGxtbQJpGe?{@}t>v55ҩY}S ܄]?| ۫@?k[}8kSa(uB+) D~9Y( ْ-lw+SFDd㢝("%; HS1@E?{NJ9 &-:(aNڱ׈8*9e%_԰nȩObOdm$v& s&Npf*6;mAZ tn<T藊"JաD BRiÒj8D. $2'1=!E8V)t> Cx3C(ɻ>5?*^49Q]ryotNQW6Z&$?W kg_[qpuU߻* e/;z@&rG}aL@ a@4r6Mie: !:cB%|D떿btr n|OVM^ .Fm=4tRT`!S_\3w;Ki1"8uGLST̓q)Sk[5RFJI&sJNu ':/C>D;V.. dSpƸ`_Xl_1ubmBÿ6W@~Q )bua5Fgpi 70ўze^s`05+hY\=O1 P@?guŔ,<fbI%wHB|j+J9+Rv QTY[h86ܭ/wkᆌ`Phmh~LF# #K69K+;t:=F虈r,٪~@NpL)]l!}_f mak+ֵiFB`|j[}u#x| W~UM)p+(G-jm'ߨa&Q1GiD$SݰJ數p9!TZ?냷Gý`{MuY޳OmF3g7~e W{ז(%!Ks 4n3q/OTz*.Fl2P~^u%% w'2@P鴸Wv,cs]qd`w ȜivXszko?䯤R@[.ހt EpesJHW9m믎4GH٦=h;<Gp X)ͶM.{ |9?l^6|2_BYZift|1tyi#@uϺ9-_hsz%\Tm(M IJ^9Lg<3<|ʒwdyw߃_}J3dVŚtn9֢ãVnnYqk:%brA̔C$f>[5f*SI1*6"g;qś|4.(9ᡏ dk8=DoztҒ&;w)䷏XgO,7 c=a ]x@J'wj~"4qL.w%Qf;Ùǡ4>7k߬ՙRM294eNeZ,S%wiBu`ŽC#ف6*KbT{,NĢnzkXhC.=dõ=. mntݛO{aX.]۴!Ħ ^ =sή VtB=ET }d,KIv|JZ&P'* WN ;I,2[S6uv2a_$ ۦx坃 Lh0sPhdDl`WQ(82`E8`=z諯/Xj^[!3Ⱦfi` _wOGۇ{oҜYվ٬yjޒ|Zn <uBݴ̚ܦy55ͨMǃcm@r n.Χόed'y7Ғ:Z>rUPȹgؒc9X~eaXey Z~l1F)Ky 9AMAYRB%-+x3< @_vܯDz $Ej {HZ. di @le 6w3>nN,3{3*Ya6%d;pO:{ /X@NAk۷7&H7Nmdm̦<:KU1 cay1]2y>hœŰw6OҕV]/_2mZoWEa,"~̒O(+.;YƔh 0z|/BPyw>G8iQ̞pXw[ $e)i~ !B2:HށEuSRlzD=QN@xU|é]H]j)~}sf_x|?+9xbU_¹{S򄆚n,o-}6`{snuw֓kkF;$c 0G]mP؊7 k|,3O~Fjr 'oૻݭWUh4 jOTD ]\ú<4x!9F ڶ-`g]`ni 6 &\k*[Lkr$nOޥoR#{ ia` |5:ssXskr;8Y.k@(ޚ4j#CRI3WX♙%g=aw:+vFg  ![m6S@"(1mk07!i0 Q#$k'xz7: 1ľmp +Ą\,6S QPbF7LBOyʍeaQ$[>n81 L2<^0V<@rcGȇNAU!D*mX"M~td&e< %"8 Za E <) 5ӭ8-@%,ܒ3\!hc[ZJQ:Hz3/&v0 J $pL=!\IoX+e4EGT?9YcŁp9yBI1C⌀< )ΐh=m pO3"97F3`Iɂ7>2~B>w6t9`D8 Í`qML2[> M;~6jxwӆLFX,X 1GڗēlPZl'~cNgFzx.taaWÎ]zG{/Zt-x;^ՉqPLý&{X1Ld׈V5D^: ޥJW/]Qpsƌ_+r"~/3\wn# i% |ܼ1eD'\W6v:.G ka//. DJS yfnD`9&TH2 2^p:nTsnFX(!e]m4+dϜ]P5uIcV[ZJ, ;6zOTuN,gØ]7E+ta/E>F YI:(N/2ܴ楍gEx˔24j`FBNxdJ*tLIh~%- Ot%RB-JEr) Mt" plzO09bM K /ZdJC^9m(nU<}JǎN׻?찹L1R)xi/֯8i p_@ek_9ït@'5?@&$7t_MSr8v8Kj4  8X ^`m'qC K8c25VO2 Y6n(]@6ZYur1)DS5] /{^^EXXaɁS2YAJ(9[O3F\ uqYEwпZ yup\k\žiNVZ8bU\E\Ju7ݯG+Y)nSƔ",⸲YEz v1LCO!49`n6#9j2bT;V6vdw"0z,;QLC%x-Cz\U%/a1d@ >TJ.7B;zRgʂݾ\B0\T7]lB+h;.}"I+,"?lIsRlAqDgI~t"pZګ VR82 ʀbVcgCiBe(uj>QD+3:{`kD)"dCLCF!dS ?t`Pf6I{0u Vs~<#*c7⎑_T)cZ”-`9gl&te'dq!x1pajM)*c0DL+ ~^L,$h{ TPMImx b lt`lD] f&GaДב.HhKzNvx($P 'h.iLZ oMu8T~J<>7 FzwBP/&Ǣb6˝),(SX*zdl]m%+Ջ:lȀ>0][L`*6KD2Ѕ1ܠ988^"¼=L4Ѳ&ۼ҈Sj?:gv߆)1,4m~׬9^;W"'ȇaNt@UZ c B#qy"~LfgfpD[!{݄7vEMfS&MAWs/uiNmaIM4}U+;)DR$Z聏'g"PfE (dIaHff3t=x fO3ӳ(_t{' f%5HqiYi1ϙXBPlaER R%sUu_Q}C-d^EDք4NHCًA`L $f|ZGay3ƚs !LbXtpTw^}>"ltoݣC8}|~=O kos)gb>a[[ٝ[S ;WPygӰP1DpT@a)ZUTGy!77X' h1g )q2aH8~]:/| nܒ^h8C^.`ҕ"3Hw|("˻eބ qybab5<[&ߺȽcL0%TZ AXTq1~ya.{dz$nۼl#z"^P᝱?6T52sNy!R_4;v >2̆|/=%HSm~bȔߧ-ĻNq|b2R@-yt4^!mI J9Rqjd^pD$ ě !F]?P1C3EìDZ;O}ۇvrΧ7ly!bjs*C5rL >zӰ(d٪!r|f60XJTP)0vf% f0< 8'&ճ4hwFw0?KY,;a$݉(–)R5 &6k]U<&ˎb_zǢ!l)V:!K%}Q?^TI}r;U-q$_.SG§7Ni^qUk 9nC%^%n*&,Uq.KBś([IhV#^"a`f[SV/Ui#|6)+kHMؘ3jkWpbWoڣ8R[a#Ĭ$no FY&K zܡt>-giruE{z3)\ݶZD2^{^t[LRzݾ)Oh(]*Ca 6$Z$hs~Y, 8:Ųf1j%E$^΁Oa~fr0jSbu|󗢍/Ӟy<1j|5 YT!)9iQJL`mt_H%^Av3it28Vw +!F;-V?kWʧWGSw2(?шzvN@xGhc#-s>p #L>Q|p"u  ;2om 8ӾMw*cȤX^VG6 r=i=ܟł]m4j o#16o&u\U$S:icZɫZ!(`]zk j0gwA4pa4A2>]O5{RU;W/nrBn\rm](js3cwp|"o YXg=UҗRXCsz)^kj}-SSl;OX|s] ou% 68ݗ=T> E9ޛCuS4< BO6b:KQW&XzQ>{ow*&M{UIL }z)J~$D΄ fmBJ,Vac,:'[,^A[eeeطd"Li],^ cf ` `uݢ\4jbP%io%e$7 ЇRs+*yBAŻ};GF^MF^0Ed-A+yKy 3Zkn>gW﹀dbϟI-x8'qP[jJtqO_[][DC%64l!l_oc|-k ]1umWO"I 9 ? `X>dЂm$lFXB̏4g;2W eGوo#R B =R1jw?%?v2rv:/~O.)l0 WiAʾ]Wg3֟Ec`*n t}ubyz#hHl qNq;cE\WEU m ZK}'ືnz6'cc Yv 6q]kP𒶠 4G Fi7U8Q1^V!~wOmVTMOHYZ.3[L2p:_zâo` @?ʴ62w%Fۧ!;*I+pʨT KS|@ aA!Mb#riCVq]cfr,隝f[UNՀLᖞ ]lZR`6FGn7z7~}:tΥ0;tqywNI-ʉjECc,Pֶn7Hż('z>N.iG<{矪`RgbH?RI,}]B0B}W %mZ1hn7[!yNW- Uŷ[:{KC`Qv\a MV^_.0: ׄ_bHvW8& i ]P"ʰ3m Ppk*SUG<|$WoP H C$+>i:k\S8q=ylW k}b OyiHvҤ&[|\WA LKA[UyLmYo B2"N)$g?S w~gM/R2mrz)M! x=+w,;y8),5-2 h+YQ"y}pl;; *:}P#n"HI\)đ ed0u^2?z9(u/*q\ ϼjIb7 Q2`?m3n~Rp}lfamݸbf09@~y9x! % ¬#{Py9B1c$rgpSwӮPfѹ`9g{iBd]S&!@aaג!Jq &%5i{ TK;+qӿ8=Z>xý?wfWƚw㍇Z_{pƣZ[?%k?sPz'ɟ.//]m4v{[E0[oV6V%n>| ̅{@RpbrMx=Y:Dt,rthNWa=6K{P~ĭ'/ +R*uFbZ >Cl>D${?x8[bߞXighDK%01zw4~{w{Pxx(1 (Jlm}uy{h/{g80#ڽIgq3I&׷A8,Q[(%wM/ =v2\V{osuu N1rW/pe; D.J 'Ԝt\@zfS Bd랥kϼǪME8b\WAH~hbH tN_ nfG-hgq>C>!g[{w> p Ɵﯛ` 7#Oopua~Fg+录Ǵrw3sJz6Uh}Ҳd-wH 6{0#yA;΁ LK$%*5 4H: F@`SmDDʵ |P__ǾoJI8v֭hNL%`FD:x)e' j)'gƅ^{N |w@ ?:xlٻgh?!yS:|g&7=錳{&FP?=IoMtiK^}{.PGfn^Oϲ_2p՚߾6Qe;{[P 7pey Zwߟߍfc8*U{u{x|63'CL88|j5t?9'!309Wxuj*yBBYy<A{|gD/_bCj<,yJ~44{VclfV Vz  W  |Z/ Q^3Ȧсn=4ZH?f`ɲ!Xu}+Z֑#54ҍ-/M~߿`UCUet:L^Q6Fb}E1T~5?~߿k|*urt] -OYsKat=|*#fئUG}> :߯_NjlP5wW}ښ~ Q/y:'17i@OvYliCvǛ8qx>Mxy*V?oȟߙ'R-d}}<-@TH{Ȧ@iswk{wA/E1kle!=3)&JאF\$y4kcQ>G8kEe0 46_R&d Rg_Ҟ#p*ѐ(^:&X+lnV+&U1=u!,huɛcC1^eLtE$=ӡ ~sM|OB%EF+]yϛ+2" WcMztis Rl\>ݹWUaC%,LP}X+O&Gɽ4MtL&A;9Fҗ]ŵ`Xg@9$5:X,!CCTˆs1H̬;jieлPd#.cagVF Z8MXҬB%N|3O E෠ʮNt"Z[I{!a%dqAvH9EeI}+r3r&6h&g{tN|OO\ F:akő9{3X-/Z>rvB>ʅdJ4Gޢ!n+jȘR "90"1(}";,͎uPj 1RN* 5{]2)7t4lkqwu e] ks1/F4Im9wf$V(' -9ze<"rfѱZCiO5-ߡb5IC)ԈAbJ@%7춈vD&@ʋm׺SJY/|-l&,FmMU")o2?a x xD*7^>es}Z[M]9'PawaQqWF.`X9MN\v>ez c%t,/yXs Cڭ7{?ܦ 秆[xJ1TZ d'@Ywp{'E P~6vjsm@K? (?TE.yσHOwcUp=}J)pN+;6@aÓy0pHɸGNoNV:"@M5Ts*EMad13mM*}&2-w=,>S>]Mwl|IE`U1Ɔyn<^Q\ƍI]O*e )HfRDjb9k}7Dž@`@ϚI4Xe*n7.hW,LcI|P=w8efliVQcsbr>!׽_u"7O\d nv/ݗK!Ia%.Mnv!xzȨkנϥCaoLNehe1,ؒwYhi-I#_E3I)[\]LPDtw"g1fPƌ 8 p <^837 y/|Le[-kgUKX)\HWL +#-Sc_Hd#)h=1eѪ߹ aIk&+}­c'N'0L&?9Nt;&rCcҽ~6Ӧ4JX\|\ -itExY,FBzϼ2ЊqQrl̴չXZrpN|ɿD5*11$Q0%ʭVT TOG>&|8ɹwl< )" `5C+T)jއ~]<-?R/;twRsIJGZoz?C]Tz)U\'4!3L$AR-*A$-*A:ep ^ -P}V4wЉD**y|nKX+*mì͹e)Ub+`RYcsuv>M2/fѤ"I]c˗IDV W3V#Byq GT{^;(=.9y[}*1Q[aיĮxײVlLFRfsC\vR ސ7%}Eu&L6%[io:虉 f(hF)@yfe}H5=iUn x5UkgE7~y>>niAnGʻԐ;=vr(r*K^;)P';$iqa&@^I`E  ffIܧΆob6Ϡnk"3m=f^< OEgK̤cJӜ&WW\R3!|f5ubq?a򷂪YO-|h"3]g>QrJn" ETs)nAV{X]ZK2wm~.Jàoj-d5NMjF?R\Иy>+ ۽Aqsi Li9K7><K#*vjXP :YXv`79HpI%qXV"jdb(|0q7KYREyGAsDhx9PF'h3 ,GA9uy\;JGT; SN (KwK WØwW9. U-!G(H. icyF-ӄzR9DC rQR4@-|:f)h1%j㭚ɔi >a|X1^Ӻ^e{wu1B,h9GהdNs9zR9:'{PK/ֆ/yi݉6#\4 YA?{Y֮asH:%Xز!a3U5S,Vvgz3<_=sSڬ$tUUd|ݱĂ( JbFH7P$Hl(0 I "A9YZ?UE6gF&^{w{Z  AL bhUTRM!yُpH g`-Ѱ9dY&hQ$ɋ.өa-W! t*r礆R8nFT[ D󗥒+Ru9P[\ `X` -R4q SEiB65 bÒ(lsqe zNAD%2dU Fyǻ ¯ lV }=>V.Q*s.OcH@=c;|hAofҌڬ03L̮rF1V =y^ CKL6U3UCP9_%YG]\j+=Yb E B,yLaN3V<^)/gu;]xF*UA*Yr' F^1HOh^/Բ?q :(DԈ)N1TG.tw!E_9+i,'節ͽhF:߽ӏeC0|5IZ&0˝#廡7 2(|z2{^*wApAX4 3Z؛X&>F(y} sujGK͘bZÊ,[LN WE5CPvNYZTa-`*ZO_ ^11*}JpF0.SVI/vb?8=W_^_wa~y3~d׮sj@8,5` 8nֶ2 C$ONy-NR nZ\ {11bZs'wb EWI]tmH}Qld}/gfz(=`.wlkFBױ|+v_> Z(+9v :1p*;;;OVO^XxCŠ]_#Bx]@쀸9' ]!e:A8@ڳln;HԆ v CPSJsFA1C+韯U¢̥?24()/I>!#[+x;@reӰ&cN^N. /[>pmY8.7ΐXZ596b4qX`ߥ!dAT[ t?飜IIx!@8 K*?~G3:"ëW:{x LbZ*A[㰣ĻPps(#oYZ|˜9]AGwZ8!U)ǔ~2 p'C>I0z5)5dQf+E<\5sa7jƐ:((yl}HH.k6 1VQ9b*\>Áp`L̕!V(wl]Ȫ0SᙇqA$#3&RͲUhhK- vx(]D8XK@<づvoZ+-F+tLƎ4ӗҮ >1$A|* 9ZFfCi6D b<6Lx5 "F G7 s ˷>o@ź 4pq $7-=E9P-e|i_P)\aJ 3/8 'SU~VJi4]X-z΋lmon,ߕ3H5}ȦgDP)x&$ N@DciPwF)<&,$%~y vy>%1ǎ^ҝBzZف&`ꦬNAp9\ Yfa, 6J ;4=:_&Śl`($LeּqKl|E$gryd!㔾&4''S =e$f5LWnA04*M)2g[[aP^^z P  '@J<!P1g9!1력l"hY2g Z}d34= ;N(S&!uC(K^H~+x͂3PouN3n|v"2<(:>NxH˖ 3(IL/%*O/)WF9OY\쿌GJmpBYD<(סSTU%bLДs8^Idq9mF)o|!hj %ro/Ξd?~7\u7ҠCz%( 2rV\x<H"Yu u>r&@V=<[ Ƞy@ٯ ɗ6cnhd\Zݺ?LWS66_̶c(N-e3CV)c>n1SC E``ŀ(x؊՘FKx0&@bdd4>g]}≟}oBFRb \"^+x/ B*Y)_C%}!Stso-mpC3o2@f= ;B`,l,zaYTc2x.{H2m5\!(G4"UN8Fp2OZζKdƙuyh{1;'᧲'}WUfG}ͶtŸC4*vPيm͎ο'{Ml˻dI31hвgB*(gB`h @B! O)G)^&YK,`0@!&NQ%t/]W7S; E^FpSMB =ZA̤-Xv]&K8~|醟pɬ$[=@E4h+ɓ ;;1a : _]A4-Bh9Fn" eAҒIsN-ji?N1 BZ ̙[Fk6 A2r;iF0݁_}g )6EkjZ{ҵYR[+wzY9ew+c,Hb(6MSvrh9J^EekʹG(e5?'C.ǣ)zY7ԅ=ccdxr,@nDÞG;k[fM[B#oH#g`x1Hϥ*iZH!mJ]+Kù\_Y: t3,y%2>ϏQԦ'9kRSna\<+1֡gH@1I~4uN1\Љ @ d8PшN|VtJ0oIבA BMv}w*D#EFFP 0| $ C zR!Ha7l Hi`.`קi@t "b}M`е]g5y@~mv]]{dk/geA m"|gKR.}~XJ|]y}󠟠9! ڋ"ke B=G ۊ'k]=GS X"0 ǖ!]&`غH៪$9oN=_͵鄴?BMy&ۼm6ojv}AuAZR|p~~ ܼПK7{#"^{ͳ5Etcx6N&' '#}ĝP1-Q'0ze{G5]ԯYx x8GukU߀Gsׯ&Quz6\?Bg5\#SzgI'Qb؞d|z/I&“Yλ!LVP_EË;knTwaV(y >.OH(B#lKOI{ѫ(J:H/G )XJ__ AQ12d?n6o#ᛵ:Lw))GC3a2\E):b7dg07SvEIX%(>wA mҠX?n_Ѓq a7 a:4 '&.~g93L7.PSs<& {=[=ir.!Aͺ;Ǐmf{х5/aIܦQ74!a%]o:77 2l:&VQ} }`7h(ϝ5lLQ E^rN7ŋ]S7%fuOt0{ѫu%@+X-b.D|Cq|v=r{:\²q=qۈQ2TT-bRg7MAB|X'c,$⿡r#}uq(`^; ||kvМ#?`A.$HxJKyipਗ਼+5  #بd7XTE˱tې;4:s>\\P0 M݃o* >}~D#`'O:TEj63o >d2)&zSU I@oq\E%AP V,B]YҏHh<**SL?lAA–MBzk?weFԝ^doV`3lAl^k5ͯ[͛[ς4uT5?ʏ\ocgbn;_H p.ShcQ{kZHlk_H{[MD5.qI`Cp%-0Bz4NSr_.fU">FjՃb9Ƭ0֯bvluv2DdtMA$*D5HM-{Ab`(Tpk wGCuG Ma30HqJ.FSc|I?QSVQ_CTϘ [*Ri|Fj +!;2(A&3cOp‘toIl k`$0Wf_ YP;A{HT]#/U+>90Wۓ;O쀣o{?0ͯHPz1p0%d&G< 2&և}b$-q.@) )NT>N%'AvI7 7#15 1Lrl`~ηz L# UNo4DSX#('ѡ%Hq<X5=|vCgWT|lcDy>W=/E?)A tB P:LFsG4  zk%˃9tL*xfIDNtL|bԭa_:zC 3Y`{)&I*/$H6lL2 fHjy(+X*H1U95{raTb첚Q̈M8-%H/TQ^[a y(Tl`B}Z8^,Ns5L0 C'.zf04'7Cɵ0(&C+=xsփ,:@ 3{X%]˦^NJNy 8<|*v&b/pϰc C?D\N J3XOnwY]Ȃ;9% {$,~& {udzӴ)ٯEz;)GRsfuǯC{7\U9̰n| w!8/_KilL++ue{%BzBVHJRlq 5*"Jbbiw{ͤkX]r$D+Q)#bR% nmV4,k;b p]5\88)# 0Zg KR9vߎ z=SP4PPSr4gf dzkGɨ ' NT~\9M1%L9 7 a.#^8,t+~\3Kе.^r"vY0s*4F׏~exRQ e;gklh=p5{=k${|2r/k*=Fy3Cf7X^C)Qgʘڬ0`*UԝEO^U4sNJr„m.HTD:tg͔\~fXf+";n /rǪ V.v ,۪r(Qy2e>2Ysa%GKB Z{ݏ\JSioe{.QoJп\'ٹ(h>Idu |꥗M)CDlϛqDo(|Q>h*`  .y ) Iu<&|>\CxrZ12ƷlIoe"j:I l| &ebMQXKZg&hZ\FN[HaK֖*[ucڊ&Ӓbyh<k^y:4WZ{廻:L:?eAFsN6|@ͰFG!eӄCe\h[.,T+#v=C: rpD)I4K zؽc<#z z8j$ŃU웲uPXDyۖh [*{*ɚ}A0;B\c iUmyϸ0K s1l:QC(\.@ ׵_ >dϭ4K&ZaG`юՐ( B6lU]+ٛIt#bI4B, [x&-hr6ndNr@`ԉZr?FB 70Q1hkUM2ml\X / O#wmkN/QoJPъV 9ޒMBl?hഃ\jp9 A V}hFúWae•KOf~:Vzb `>")ʎ=Ht8G@/,eQdƉ{{ROW$⮉@짡SxC4 sMQX*#Iծ=!_L@<5ʢrK(ISgO`!kwEȳ++xTJ= fbgJAd`T)P $%lIje &k 0֬1etYr@UKDFیPxd>_}.8 ba<07 WK9%@gq+ϻA#sAXB9Z]ys#߰pc7ۑWcy,> WPCWQ)j%A+{O7+gؙPT]:5z >Ԯ6P7;LS"@d~ 2N p~mo.겙G/ˤ)߂DN;>IMU2<"fwǨ,uC4\H}jZxb,GDK@T^n1]N%.5[BV㏣iT#\!ΊR s{ψWM'CL({]cYyǛҏWP5W-ho=vQӽ~hK&VDQ0WQQnׯΏ-+g*tX !2mS0 cߎUMzf ›Ǵ^Ќ"@"OĬ1'(IB@YIXm@8>[Щ2/oW\m KQuG96{ P\ȥYifdAC*"K dL'}ea} +~j&nZi鷼BY8q2|vmF ~FR4"$2SOHFDt:~bΨH! ?fqLJbe\}(5UZ*( HpcL}c:֚}i:""n4^Q##V[/ .}NxIcEAo_ݴh| hmXeK"pޖvz1@_?Ԕo·AE@MPQY:X!PeU*0@r#Um bMyѨEFCE=d3Nj4etiQevb#41'BX7?u-5߻"$ tL]qܵ ňP B9οnVy_f/V.vywf+tl̋M3ē `+oKh2|8y6͍[2Az/Y8A&H,hnlm46 [D~ E۶ǒn?apj"Ap*l(! Oyj:MO| l5j͈ AEp C|EgQqUK)@aU5  V/Ba ba vК0Rt2SŴ+w;1@WAwT 3{ܪ ,r4/िz/V[(M`0dUl'$f`ʜnY0}|mнOMtnWN+Ne]rUBxzEotJq'9\0UX^?輌pYe3{AmsXC?CR stYov1|wޠ:.&)>'iwr1i)Y`}aC^.^ṿӏ¡722% - ,$;ӣ>|Aa޸=0(Q82ݵ M0S`l.n`Ǖvm@{|w|/$iOeki{m ߋPB琯1ľ_~y6y6A` ]#*G2GZ s w6d"y\kvK·z-Bxi` n2@1u,N Ho\Jþ`g{;=t$Tf A1$8gi#PVA cde_q6mwqI7T #X]J-|rZŨn>FyHш˪MCa@lKz߈!u`Q{`. Dz*y8d>ejθUi776o}?yXk0*qz rĭ|ᦤ϶ b}^DsvO6[4jG]-*I) `~lM}1x}`^S*G8s>|HBʇMa" WlcwWȱ HGdlt~P~ Y %p͟ҨIr0kJľ/[`픛Y'gjo'6_arSj GZn/:h==/}FSrڏY1˨oN{Gҩ] g eQOx$SRX eOxB A ɳ3FBA&4%RoOHHT*GNHhޞ/'/!/=9z k l۳ fg  L%/bmg o賂 ۟gdr[5V ٨Ve| 9-F5+ ncLÐ_Rd3q8/R|Ҟ">qMCo O'v`M6Fz>L&G=b=iP뜂wѨ4 i.&],-* 8BԠ& +rv#%.R {,LdIgpƅ.eٓ;[yTGDԺ](&gc@ˍR]cnv&pn`bPzXV˔#{P7rm 7os%,u u&!@mВ`Kn8}8'(Ὥ#iM+Qo=5P)=TĔ'o$枻}Y œs*]5Swl`Ad rP0{=0dP(ˇT{qX ̴ن'qrKܭџNCT/>;lv-967*"XRf}8:v?/f6s . Q1= ǀI!| X*Jy*_S84Zx7AJrIn\[ϣEL3/m0Cv4QMuCL: #)]:]w3c&Ge]+~g^@Q AwP%Eܑ0c}Ȼċϯ.qX^Yk"ܸPv8Ǭ OIvH,o]7ꈼ=dFA~w^F="_T+?w3v|4_e ,<_:VQ7yZ G=ka?awQ]j vϺvԆ̧|#Q6{]-8f#hǯ_i"Lg$e>[h3fXS-d t)AsęƕYAtb2UBkY9ޥV!ӳzc"kB=8I^'8!]q*nA*{K`V3No~KItJF;ٞNW?#u4ͩsNcY)pVݥ;!xÓLyF| 2b5NNw)Κz<2{n>GT4DG.wּgT,}zw5Mn/ pwQo{820P2e\-rVPWjX#g"ujΚÝ54w`.n֜op;! ԅ6jz[IѰQA>) %r<ŕ_0 HL*rP Z; ˑr3)]X?\Ɋ&2ppܲNEp`.;zyim]Ykv=|gYյ/U'~]X=n!nn4Mw̠5%` uIolgBJWW㫫w|$vlCcMzQ(,G9RK ujfot`dtmlkUKq.|i2׃i2g{{aoB<- /_^Dx1iK)COlH)``ps#WejY$# ޸[ @4-ZA)1zxmI*r!)9;Gf٫W̠^t]-Q=Z~?$nڌݤ׮6oVZ_om2[vǰZ8>8??_rBYsOu7oFVsc}m4A7Obt:2Q uF;h[kךAsc[͠-;F +vՕaSyO7XXtE(A?> { IVs“׮ G͓s|Se?+ߒjb3 p'_ #¯{L7(x &$ 7O(Bbo鯲qI3e{w{Y^n>bzY:CkVfq_9P/|k =`U64cMw72Nu` UXkN>B 4E5|D|q7f#Ef TP2T`5xY%z˘|u\M]j43n*9/ɛ3_Onusz[ut\߼u.~_VZj~NP]7T=R~t<٢q|rj~/M&|ڠ6U ecG\g+@ ϏIh`QkeЂ׃HuLNp58C*2Hk.+j0YG#jMnf:T*f$j}f'7s'ݢj7}j y'SLy\S77Zw3slx6v&V}ff]5#hww9߿_+?3}gO}7X7G;͓ߙ=>wPou6_oo/~wݿ働sw;OϾO5f_;ޟwSp_;b/G80[oӿyͿ7jm H@o*/ÿſD૿rtx͏4'3_ϿFw;?'s/7_?GOaɍ{m̽72 d?e~9|eG2gw~ߘ?so~~?/jA??V~߀rݿ_W~ſgA/z?ۘ}766omnߺB_i;*?o-~l7Vk歭[Jhv~d-jn_@ p085 0g78 $ m0tf N@DDȌ $Ӹ2 {A1AAvʂi4 h< 1NN8_X7$ݓzqNT 5~VN<èߏsE$|fO;Ģՙ|%o9mf0 i]8+D L/٠1%ah:1q(l2n63aP*AogLCmeʍ`!6 V zPI9utpM'ʞFфh#`K;8B qyË`F蜆siMв"2'&b%:<1[@'v>$DusD뺏|Wlj3D%?18(_%7 GUӟWBD)Jr*qI-{|0| eת[ kLΘ3Sfcʞv,"׆63]h)B =e̴ToС34/}PB3΃=yރγ_=ڽ%oą ABiC{x<gޝIB5\mO<"򖚻Û&Z Q?ƈ^(lK]*2A 12"±wx*ޙ]W*!bEV<7 ÎEWr<[md2;QpZ ,)ɍ Q;|2 ]7ȴ n\lʼnܘto3 ׯҒ[{jg™tOͥ4t,@ĭ-7,M&75*Wll$"07}j3\V ^{ +n_9uu5H;Q a# |dXdèu#K @ 03F8"uf {O [5{`R4lP.NU͡3 9%(Hάp_1 ȅq`[a 2=WQ4b8dܣ@Ba:#Pvu@Fi]7:zYtaMf=ѭ ) <Iסwq4'$]e,EU"O>0S16< @-Q(qT-Bo ub_-WDqPw1儙gWm2˽òw$GV̒cִpҝ67F)O )K%p".)y1ٱ Xe: .ALw Ȳ{3i %qP DdoױY{*GhCaWNVl%hn4[7#:I ?5\06S\Ⴥ#s[p|4 SHԠ K NkǶU.ZQu}?ӊ?­}iَdDZ:JDТh%AH#NqE1:@͎(C-(P1^I. GJYT"HH L[@}V *p(%c[>6±yi o (l{jErƸe e?+ŰS;p<<Rҍ)AÁ Ƨs߿+Y`K2 J[O-~~hk[E XEZ٧T~x -(]:͢ [Ҭr5PBHy@6T\j/ }FGjxB E i'Q@&&`ltSrՒ %ߖ]7L9Ḧ!x7)GD:Wn&ߍoMψ֋m/ Z=Oe0^-) " 4}{|m2Uf=nsz=辮κyF3?w.EzNl**d*ǹv~]| Gt`;ifq@1 ţ8pGxRijx`+q/A)(6M =A؏$ CN ?A]g\H`KRZs0%ZRCu"cgߣfdj"j)رN%5Xeu,!KuY-SG&5$+ +,/mECt&DZ:7*Qmɞv 6ʐ Pʟ %v[7<|h<)ڄ!;Y.nD&~#ce^ED%cONzYaq5G?{;zoӽ_;1'ȣ"&8<%’5I͖p[A8R dai~J/7_;̎س>^?0q'[Kb:go ME斢i$7 m)}a΢ԔԬz!f[}2Hyٙ&Y;btfd' ;Eɼtv j3Hw2~ݺf \nr*+ Y)8`7-X--bsawJ.(^uqڷ kW {H MK Hb"9>9G' I )Lil|rJ<ܑak,6Е?"rb#?[ ?h:]X뜄%&s3^8kg7rmqiqWdf䍑׵>(lCp 8sXJIr6!$LımʎF,LZX];7oXx-r=5q ʬk12s2$NKvF*uGLʚaS=Me/>< Wŧ[izqŧ5zd=R.mH,Pk먥efBhB 3wEn.ꢽ\tK:\+F Gs;nhEd\x]]&zCraÂx%]]gǼ)892bٳOŠzmۀb*#,[']tg=u&s,!@*4lI2.K6|^)~,1]LgGIZ?m&^Rz)ehU;P~G1/N/Q494lnyXCtKzڪv?{{yhqgv؍#+?E'hNP#(xa KH:-*ImGmI)bf\c'j9#M9(.?x3@@^ HyF 87͍]~eAoEǤjZ{ `on&@:!H1찳rR~HNJiH@AA~yb6;_ӕ/qӶVH6+: [>!y Z;[Ӂlndem^G1# pޞ2/tʴC=a OB Dz&&N@i5dW`7\F^ə̞vQ/ s3(2eP*z;\k55ko[iZҗ"G[be,.S֥X g<e1[c|Y %PD+P(Ewv=wӐy b6V *P IPGG?/( y3âށvEo'1Ȋ ̘C#ڄ4[y3ˆ+Q ._QG^F2Iq~u/q %(G!$1jɰmA6'FP{r/0fLR=շ4 CI?rMc = VuIOUh/V|Q9<i$ pہ2eOW.LD;SZ{<,zm6"g2x{ܞUOKE* ֐GOsт6v!"^p^dH{??Adnʗ(/ԓqϗ^ifc*-w,U1iXghEwuXnᶎ%uc20yieaQXA=+K5U9_ oiNfDNY~= u5<׫ًP".^,-^/1y౸kvH/N2?|4rLor3{QSB=`!֭u7 $5r-a]䧎e >B^#0UФo//u*3 s鐰]pu!.Ƭѱ˨JXks:-MtO€(FdL2$f4qHہ4q-{sACt7 9Wz󲖑Fg:ӰhGa#>@)Cʕbz~,'Gmx&]/.^[l!19.K=1p%5A>7W&VpZF tA _øxaSW^\e.̹6_Uuݧjkg`o~>wiɝ|xp"]LxB{TA`X5aUʲ dxp<+tӺsճ sz Ah#Bġ١L H'n"R><{,,tQË@т[ Qadd>c6xز4%gA.¼&ɌP`dO:C͏fT"cp>;D.m]fKu[8we Vr_ZBW-8hzj 7/ƆY[ @M"GXQ>xIm ѝ!Q4Zp+6aZv8f(&ʑ{gt<<(a@=dp,14Oq #ݢw}wo`mǟ#7gcd%cAwM-|PH4~ :u`e0еyEU힙,ᬾy6n8DCaMWʩM)!L&PMe >jg>j{o*&ӚT>X\YkɑPEnH{HEX1  ^_ÿXnk;uXhY܀.}i=N|N /ڰP}8"Ug.^T&"i_'5QQf3ߞxo۸Ov j8jnT5j0eֶiɞАi_g߯## `f{sz{&࿷??~Iƭes?O $wSi**x&n $P4^X0OGEx$2_berMy+hS!y5OG 'U[q 97/%T */xZ|&V>3%j3Yt&f$/fǥ]n }11[G]XL2e|=;g&}i3=]@3?Eͪ9޼qCS͢}nޠKQi0ciWCMzӲήmbPOEFE6E-5ũM[&'8Ϻ0{m?poN~nx0 kςFskfQ)_ȇPTy!R B^,*K }9[ )/9oww>YWf`W%7W: L- jP/o6Z8nQjY^2 2e0gK,ij9&u'Z??o|Zm?Zo{ б:Nóq/d|E&, AN&}P҄Ynn $ ,9\J*ʈyUcX;6G h2_#`&' O.KgS*"&5e IIXcj}bg= eS?PͰUu.^AMz`i>v;=!'hUFďݺChA{_uƪuAߥiF Fm I57jBh\.8SQް{$dw8Z?H$ ֌FھdQtEk>%Ufv%V̮Br"m"n&WwA5sN;ȊϲVQDYpgA{ IUT($1e:wAԗXvjς qBPaxB`3EvJ" /;ٔ*̦jNTd7;sqO*Zj0f뻔i1cL:}ZrЍFN_r=L4I+2ߪH' P΃ENuW1ߎVLvi\aRJK x;VsK`G/b^V3 vd4Οbn.#Q!|[hoكmF%ȄN[̐^c%!! #0ChA0l c#\S̈J 8a_WY2Zpo?߂{O&M0 v< ~`<}B-tOCytu.7;{;OST~_/Gv/Ŋjm.17%:3X'F~ yUJiYC!<5w}(u~ ֋ǐ -hǐOI^2 $tpԃJ 7 }ݦSq_YT+(V :@] /* etN[ E2t8˔&Qy:b2`31 aFQ@F.Dv-}$U`g?/x_VQ\e'i £_\^ӔCͱ'ir.:{e:P*!{/O)sژ/-ژnloj6@=.~vQ hGgk[AϷ7JJ[*A$9XC3-c)f  -~6\\+RJA9dz=ʖ%) Sm.x(ѹx+GǠs6h2 bw8ө+%NG. V c܏ϵ/.ie!Xk0n,#ArPYJOÕL&& jR;**mXi21ZmgzH}Z Sk)!ޞF=?_/ yADO -4JhZ/Nd"K i"w+x\G6!e ";KrĐ9a{Qv־N0(4,f8ILs:NГyX=DYZJӴM6i~S|ף 䖥îRGDSV}Sz1<(L*6YL?uC}]GO{ژCo57ܼwSGZo- ]k֚Akc" zWBJf_>Ypz.Hya_W޹(άl2 I@:@=VH--Ƽ\(5sGP9""`;N0Ǣwi_5027 @=X_XZe<BBf:XAϊXiM=2.,֖)OcQ4lT FEkU*_% ⍃d1E7AssGǿT̝;/ |#KGE 13ț8y򁙹2>z'32F*IS8鬿8"9:dX uFTYӬea{ODHY*U62^pZОS62/4 }nDx[zЪƋVUz-F萔̠t/f ڜZ]xG$qPq0n;}FˁvN0c:xŠ1[̠5!\o#-oK&b~ZB7p7 6dѢs0ӈYe<_O[sscon6ߟwϿ=yk&dqhZpvT+P.z̆o*˷~.u"ZST11s+vEKI_xV $HhŜ]? NsܣO+ mm1ơ줇2^/@ZL){~f\Yw;9~0"g _$qB^kGH)A`*$3bpKQJ<ӓhcl7o5[~“ _Bgnz 3$}1O txi\ᛕQ8j!<`6Kx!q1Z {x@G-6c鎡pGE<ǵk'2یK0ͬ7})kSpͨ=az\߄g@CO8IQGQ@M0\iBɣۙj"bqVzP1N@Y{Au>)a03W19udH>CcY+S85PYOn*c}Y)0$%rڲ+4N/ ]l7 Z|Wφ/2)<{ `@A6\WKh H2sGaT莔SDr cA^/_GxP|jM~Vb$EHn+я |X*^^EQeNT9 P#`*3Zsv V<nRJpyT)q`ĒtYN3s%3e-\۪nĜYw_O }Q!;|Pe 3\]ft|Ux(40$^т3Ls4<.$ehA!w2Bʬ';Ͽzg`Ag`u6+r*LWԈU#oݷd=\juY`ZqO¹72  Qg݀4[$<Dp .*{Y*R.V@}jdljH,H:]a.n S$0Xn2` ;3[,nP24{OX;xrA -v HXj4~WpoP? {Ua3Nq]4\eum>,2f#(st L7 osӁ@ӻ: .N|a:txtT`ó$Tk~ܝtKr/T($/7Y ?u4Uҵ?nuO.s$ř=Vw9 ( `|2 !F̌aZ)hそU"1'б Ļi$w־W^g-8)`L%&v/cqؓ75ҟf1!JZKe%Hχ 0?a:H&]>ۼlփ֭g͍Mwynmg'oڤoZL2cncHeH[X6}6I`5wEgc iItb~8B39ېh]GqtPCfvAN)Iy#Ԯ[A9׈uU-(!8Hz=8#m`X#OS%1f*`d8h4sh'J-*ΓQN*Ɂ"F08 4yP"/DDL%L#%C~ZɓK|FNlA:4tOA B]zH <_F7=we̞yUȘ_ kMc#3d2I+I6K_-O{zs "T(=KS%!,0Kq!:Pe&!KC4sZ{a Z+lً̖)1)r GЪ2*BFVd=uzEErϲSpdARm',맜/ VT ih" s9SE)O$o•G&/ʽݟJrŰ2̈́f}[tEZJoV#ϸc̍[ zҽxFI;l4#zVTr/ v(9se$ϙp\$3P11z.vSCPU)4"^1Kshh'R:ȕγxI{`ך!>P49ƏAo~#Y:g*ڋC\#"RT#`6*~&)rD7(Rd`3HÈ?{qg..XߐǸq\kRT+LSǂNsG }SLuۣ$viEnW1l|o T/%;k]K,#H8$5W8 \Ee%/г?Rf9> ݟkE=8gS ,&R%w [&C+՗Vd^JE6v 3~h<;!@@g`4f-D54V-v02b5dm!Mʍ۪~<3Azw -^}rǕDaIĿU2 ZdvϏ}ұ(հ{%kB.' ՝|}!1틴Mq%+܌{:bbICt `%%f"?:d}gUSzQ𴹪Z acgxp%~q-ׁyw$' 8SJs'h6P?~LND{f{5ߨDXM,fFͿy[0eG+^`n։FgC@R7CKh^/4B*Mw%o>k=Evr`n ]);5,(g5l{G19aNW',cSF/CFeɭg6ˬGE](Ild i*}x8?J+~Q5sQlNHuN-X>Vm $'An0F8|>LPW!e) d?Pf!"1(v'eH _:nuAIuvO PZGwG%4/.#Ǽ-$(z}+y$o9 KC;kvy`(RX3U1*K Ɣ/C @82,u$Bef,pbWdXK}uf 5@T!`&l߄E 8=ctR('aL F@ݟ#g9"֬]~ayuK c}lOmp$\ӝe6:z өub%?:{=Pȝzs2F{g;P-B S$ On>?' ԓ~g"yP8S ` )&N(-ly˨*)l5TlpD(;Z=C$_Ma`= {hB:7P.~ qt>W D$|H8+rC)$h|3iemxG|ȑI_J7W`N~1au"鎤H`&IOyf\/JȝlO ps~_ ˿.gU|lSz]CHy7N#|Z_{ ?;-0OyV?-&62yOBӉ*6"60̜wOFKzX̫e~Qз]+:Qy)x0BV0#d@2s|/J<Η{8T=pov_qђm f^سGQ74FA e81`bzQT$ZV D Qʅ0A"w_"F(E)Y)"$r P&нӆV4]9QC}jo/;|KL& 2 M:)OQl#SR@悱E1B]`{v]/=af7i) 9IfS6@7զϙ#"Y~X9T(y|YHÏ vY%4uFpE%:9'ب\e]+A+ ӠuY9`Lr$7ۜmUowS,ԍqu3`AA륡ЈEnݡr-ڒf}MbDx}N:Su fKPyw^MG\au!Wol,ɶeTwVଋf-Ie+@&U+YZO||Xxs5*? 6Kdz%{.āŃyqL$r)KBbS}O t.kAID-YKS}Uj1UZL[u:>7!UyDfEMnwzgducflOfǖp.8m{f7PW$o0!}K"%OZj/"LIzJ3˵a˄ C 9{k7Aΰ'vBY{Q] 6(F53 o2E?u53f"^K6u9/+T,f=:΅sԬ .9SĐZBu]B;qh ohg -S!N~s}(o:}YZF9Kžt<3ZM%6*sP۴KQW4E}BmX Jibd M1œ8mQ{hX=$mB42y`3|@vߘt ~2yB sl%2M]M^舲T `0#`0e OMǀn/k@-2cKF`5_ pl^V&P|)ap}ۢfWG,SgA=| FcuV4ןѣR~/dFퟅmPD ? b ç3#T]Ba֦6P6gFm_+W'n_rN1N$h5 &bNXSn{`p:JW0З~ur~Qw w%}rcur,Ӿ~>MɅtiR"HY<'֋eLW`:2op<߲ynnRf֭fk}}wSY-|6[*ӹ$tM.I,ד(s| ԲTؤ|3]Y/rs3((͎gpL*.n*rٜE?^~^nn/K6Lr.tޠBШ ,@P[Ymߗ<~f[)¿-.g_'K'd .֮ wIWl>6?/: f[-so^|rr~nzuc9!GQ 6G*  "b?Cqfϭ%{É`-VƼiJrPΧ $-Q؈INR,m6mUU xřDV2Fru1m '?tpP!{Cf'j @):$;s5O"*#3J~9pNRW4–\a `8QH6=1\^rn(5;UUl5:w*J4TAuZ{xuR_ڣd2I?@X^nm(E(ꁱUX! T` :\l1*iEz̹,o^|F-.К[*3s{3 jyugr!/5.饧 l{茶ʟ_<4Rm wsuV=.~|Ovёh7Z~l579gE`8_49Cgߓo񠃎Y};nYRrތ;dE} Xq ArX+ (.gz*  aAs ڼtUs@a۳Mf or)TyPj\Hm. {! |qV*:EyI* PÕdfꑬt**usgEPRolVm>Z/ g}xb#S*RnEyz'ٻ.|}bwGc׹HrpDȸ(s6]B"›MN?A-_fP414L A/ݝy7E[x/ 3{;߼_$5R//2JHw)CR8_PS;ˡ{YD9W a2\-RK)+pE[6q<@efBoQJF. D,6<9SZls(JV'3 uOoA-ˎxL/i\N9|Ovg9˘˟Jd:\HN8"vyD5Aҫ^t/VGdZt+J@zzCM`Ҩ^ge(k!%gWWy*ܣ `ks*rbf-3MSέB]>xV^UN_kEJdxX 챌DeNk֖3:TY|a7Y򭮙e) H8y;d}=41ON1=V_ 2ltcG RRKi?<,X;ah8G1*|)i8xP np~=Ԟ'j-#gr ɑ"yssNtnJOnv*aJf!H *W6MET`W, !ɘ$ LFCC^ig,j(tBז@Mmєu7yzX곭]E<}9 :ST"zȸh*[rb {ɡ{R}e鲨hAy{icFKOkL& ySf`ݤBVwlTɐn4ePM,nRUQw\fzpԮxPr2(?ԖZ?U+J S*L[ O];Xe8IE%&IXj_j2v.@M![`UY:pS>/x%/F̜CwW^eЏNw͘{g^DZB>n&UNm_?fksf8W=x];? QN@%62NSLG3&q{!gј;biaxd}G2a54fF#Qq>Ka1e; hqa.WhK0^8[xӋ"d٪4+z0(X󼾻&S|&[ҵi"p-B[0*ĄT˽! *7zNz-;f6n&>m! .G:qYc(eBYP]PQ iD`9㾤Ls`ǩn+4sC]? %1T'v* 搄Cf Bpox,4ra@Q{(i9t,wHtg$).{ͪȠ"ⱑ4}o{/{}z7wJyh\L}8$J7~p2h)R^4h53fmIW6q5#w[Xާܺ^$V}6jӯ:Э^,mDpIoMH0R7ONTc mm7'w\T:16.ExrhS,, Cvn~wSdz1{[ަv%B X8U?X-_ָ)dxY-ע>!CcQ, N 'I~zmHg1l*/CwӽpaYyʃzN S9#Zbf#\JgW9cB+mG'tjV$^ed mGm"GC9ț,'A)ϴW2{),?͜GUrJ L^Y _FqZ17Dw!VY܈Ȝ P#3+)i~Cm Uɸ qLj& )Ši4LsmSzkecN?f`şzI5HgK.:h|bP,WP O4&iu΋kQf]5TBe;nA#C\dYyж*":u5\@YCi2Sl/`FMXĹ QPp\H1914[3%b,__K/.&a/#Xwi:vʄVK}u XX $I& )gz"a~`,\k,(dt]`6$ CNÂL~\+;̼&-|MN_"øyx|=!4y;.~]+lFeZ2;אst" l#؝DFQOe,$juF`ڀ;(93{Gl#IaПg& V珌 VLb(5,=^ZMGAxJ1f*pr]R TP=|9š3]Y:s|ƓDTM#>D芍DF !CAiҗ1䑃 )}]!DR#yqs8\>10s)4Wx~B0Z{o̞][1:jŞӉua@N1&1JWgfBOYMPX9\ޒ!e< 㶰Jv]@؄+r98D,+J8>u[ilݕ6eאt2`yh=Ic[y8%R^hm'G/16o4uz v$"EV=4g*{'5 {bduotsyhdȾ@U+9_݋ʉ|惐:p+}ϽEzG Η+LXOZf*$ ce7cNOF:%{2?IJCh4<,Yq)̭27;sa^I'̭.cXEهٕX4Nx%-OT,ߌngIZ.[mr VIr0pUt@ *Lw<)HM{Y̢LDWzdP K#`̔^e:P a?dBx)ȰOfW&nڂe/+,xNF.FϑYE0l#0J&Ⓑ29nՊYELJʠ^Y_WrDpj6ew(IcXs7 UдB"R/%4=$(0/Wd̢`rt'h z+jq‚*Me^jKHjh8P[:"A{awA$7P>Z(z|K~Pe.ưB$] c>K#?$>IE`呐ǞOx#X  o,E ]Z1GCnv{5 J66ֳWm ̄gcs[ ]rw'j~́;VL@3 ī ']' zyEmpPj%ְ{15*5(읅n:\|v`SxJ /lK( C- b`Z9{ly 838|p " ,ЖhI ЎDuI.pHjJCFT!@Ò\ϭ.oq}}f ̐h]q39ZB,ט=OLqZOx|C+n@IC ba+z[s]n)N/5"oMHQXNBUm O҇H+F[12h`Ĝa adx$kՉ0b|rҁ4\u9/яQⱈz9^YBp: ` gyEP #gޖĖ1?&J{ Jf^|Rlϋ|yxKw{~k#|/ܼx)`D$.,d/vt\!hlA9=:G .w*(/!E疆kO۔6f"Sz~TV芊;"?-34㫺*rR^:3%qV5.`DqZڅD`ڴ+Lr-$'BڷE=ɷNGk| ?ژׂ4Ş5,,i*)&l,<j2ۣ0N9}!@?Ѵ;p"Κ\wej^'ogśo)-\ :t;[}* '̚[nU\jU`𚋸Fv~UJ9>]Q6,\3ӫ[SERf"herF}pgG}m!!m2TaC&s$0F~H1% L'!q'~:iahE2%&!HIF.Gu,5g*g`keؑFΚY6" h-;Yv @<:wY`,J?di,{]fON&E \/ {C ]`>C(]4C,LikųD_Efo+L3^B U-k~%of .|'1?` 3*̆7JQ=V|J\=+OΒnEA!e.B860d0g42C F2%ȞhT>(SWiI/[u[E'i~lPhK4"U~b)ZI]Towˢ4<]aۋ.bp\mr\R1\:&?UyU.GRhE>C=(L( }.?>7si >M=/N5^[6\e@΀XrKlrYfP{Λ_ I|{7u%S<) @Jpt$<}7@ .]3-Qp@xϽ=l Y2ywM(pū'KKh^7?rtKF;?Cus]rbz6m4͛of޾yVus}q5Y?S Wshim-XmEFךfjnݼ<8I`(Xȗ1:PF`6OaG֬#JqXi' $,F%-z^DŽT):i5}4Gj&zovXr$5ےlP{W8 V<{oAs@24ڮ95_C,s?]E&F `c^M Gh[+vb~rN)r S7AmbV~' B1p)^j'z-Tud&Ox25@'򆯸}Ħ!"oJNV5Mg,B0'o Ǟq 7s]iS&rRʸ%؉^ng3aB<}g{;|vls(;i(K3`RmI: .ߒ$o:/#u%_b|dz1GɛŌ)~@5 Fszkcv8̐@/#K8Q"/$UN1yqgH䔻@f(46GKP7ir:rMYse*W |f7}jv`k<]1ZL Ɋc6tGV@$a@ mcՒw;`чd +Tgzfw.9}a ; O M,M&h|NΤ쵀/t!L|H;N໬Csp5{g`q{şe'SgH9i.z>pat\I&7p-qjDKXį9`s`c̆ڐNrBy >T"]8mN<Ddt*WV`Zbz!z[x3폾wW? d֌a ɸG녇TE{mAx<v6g6$L3I*d- ?4\#/.\@c-F{ ׼W7嶅8tYl46M 4޹Y =nϵGy= (=cI|>61"k2UI*\.?ys<{8b8f0뜾s-‰z޴ב';{r|{yRH~թ]O[ɃFF_z'|֍ ʖX, - _ji|gOW׌?tQ8ƺ} BjAޫ/09{/QʌydirCQRm98_L%W s(9_;שϠ2jָoj@U9הImm;&S +pZ1g;rlEn;zmS04>/ I!LQQ0$.0t6HC+)p >Mny]GZUwVQkδ[*}LI+ٜl5BP4부OQȧi*0JKH z]|qrHa:҇EWzklƏ._|cvx,Dl{hK;hc?I kvtlTr-zR{ZK3/&#dQtp1٘-bY_(fN!YxdQ(\RUd`q"9'F{o:.7+xd/`ALE(|8JNHyd~@k&+v7|1ɜ}Uf%URi{H{N[J'I1&O5qJ\YW|O9K4Ch%kHrwi^ Pݕ,ʄ»HtT3't1zU/:B&F"+Hv1rV]VJ:f=< *LtZ C'[|6>KRihbR'PL0s'VxQ ,HӫoNjqIenxX͇ʐ2צqxzZ?C9Cf 1 !^fwv~3O(G;0)*7ƙ`RbViʢ3Nے=`~RҪAASaUg],Js:̭q2r^pN};QS8&^Q365)e[.úEEKĺgS85bP PUٖm.)9qir8"GC曳Ƴ'_n`yYhA{Ow'Tiuĵn#mn;{W6EIymúpV÷6:fܱU~A Q,9h][ ͧ Ɉ&n/QK>K u3LpJ5KO2Uc k qR3'/]YJq/ɑ35Z=>BK8|XX{CGJ:UvTKZ_Q@u1*b OVՋ r I%+(&(;$`&h q^0?Jj $~qDPX 1A~_nzz1q6 =Ɏ#"WQ$EtGocGXZ l_xBU~dL=]f}w{X-ēeЭefY>Q DaT@-~,\Sݯ};~>NV·Md p?ƝmJwCTnLκ|>)E6)Y>Q /o]5g.֘Po,L`pUׅyand7W!z[`hj ,`KovrR8T6jƌ7eNяN=#\fsB{PGu0X*]B}jʣ uD(dT2vMOìsQtʱ.e!}f{%CSO5K7<6zKK+9/(%Uz'5 ZlEDgi.T[@ 4S D͔` JP@NX]= (2َHSCgLaw\4+~ S2BLixh? Z4~h!89 A2PPc#sXa7`y3zMH 3QF %H?T#gMGcFq=L4Ps!XQ;ϦB+C+ C3A 1X^!'b1kX[9nDm"CJeaϥC=ZS D>]Y+1s7"~uHtx) V{}:@E{1̟c@nkVS{s}r[Z *?]JTʘ*Yh!KoIVU\pTn!dro*;OG ғ$ǝIzx[URVE>$4'Y2wN֖v>yj;)Ot%ܰT7uŭ<d].iau~&:9ؿʵt*xnUB=/z>6!c>[ A6oO@'z^]Sm_expTuZKׯaPƼe3 p\&Y>br\6ĵH:u@: U{72n81ZUmmաr&UAp_^IrV h ĘoYNm,mP<׊s-H<8ȵga~2wD<%;7h1ٝ_LMd}Vv*5Ard#2xeTMP5n xG}$RFfo7}2ao= pb΀rv~f1OQ,%Jiz2,aTeheL_\UZ47j} h͔0J`="³v=Êi&̶qoI`KA.*"Kq]5[x ծUn"})t#|%z,0E:rtdg2.V~eUnmEX!'@^t$J5? DIΊ?(-QP Q:̱|6J@j(9胊 0,1IY K1sj`%g v?Fr*J#(Xqpd1yc'm1/K#8!5PHCp=_5' { b_g"Y[//@s`G~æcy6xSkum!jW~8|4*e|tiP9TwA9t|ZsBil(X2E%u[!FnZQ.H~jl﵉$5A, -e9[%Y^[glknrUf/)5v7pAY~5)*wAGfU)PԹ)eB"4QVu1BIL`57\?&<pݘRT6>2dgY6Y1 㣴:Av%sR5 ̇v`2J@QWme<)[N[ռ1Rޔt}1  3[-*:|PwU3!+Q7>]HPSQ-; Yv-8ga^L6MUSQ^r vґW -Q" ΅V{AQ|,Ah D^'(tc(J YU'tfřC}<(LI X4b䢉хBtJ] Us]ͼ+9*o<>2"!7iG0sgi_vE=0mT<4]$xWfo2gBC[p̌ >I98TyC*YJrjXTԇ.r>* jHi $ZbOސ>JFQ;͓>*|]wo޶ooJ*y[V98Z&&-OP(@cVrȑ^q;ZLG9%7~v`ַLp`ojoDx%D_zeQby79x%0o`IO=Gpzn/#R~*m 3sxV2+G=Z/K2QIDd>ڊA%)*NJ(t2S 8^d e͈rc @zPS D2<IE0sܲڌ ybD9esؑƏTc0 j?&I"xL̘vtLv I W(6IR)b/ V,w#luwft_6B#kf6 w4}{.Vl%Ca^bd,[_fvѺ hl19d'y,=zTxN@5?Yn@N[t5~ gdGqv%ڐ̞6rG=Mc 69^~xs ",s!suUŦy]LҮWNq"VfPdf:wN2 J {]NO;89,tp|~­$oLۃȾڒ7V"׮$DjQY-Ře8ODV; +̲lDJzI'GG dv7Txm<`4fpX{(xx/ǧE@6 2=_M툱gzca=t>F9Vӯ@~ Ƣ?IO }I3í$e(b}sTLMB?+3Jlle bjNGs@#P'2H[(Ԙ1CM.NzOt d!1׉,?IUy@YrࢥD@ mN<,dB_BR-CM2?I`;Qj+6\D3d|9,dHsLd$@%(] Miux +mNGbe;*hQ`e ,v`Wwo\\lIT6AKx [(97t1 qg\2#޻* Z`g99ؼXmTx>!y'pb#G%CI(RpɒV%)[%!DUr~G]t 4ll*d2t?P-~XI@Zt&i5o7s_>esSs0N"áI_\A;nAeC,[uR-U9.)UΙ(/܏{gjCo4-3+=fS2UM MylRLhEC B9\ nkfr5x =e 5)DptVu yǫ @[E5k~vw{mnmivu?ݕ~,Σ/0Au喽6}0tɱaQmvot{fwws':͎cƧmfyG>\CJrƇʞ >ڍǘg,4Hc)&ʽ ;n>55Fhh^}ǏrJ8vNvmeP`aY12X{ݝ=fl?σg[q; ~}ݾxzb|c`~&PD+7EyjX-* w) @#nVsqWsWe W_'- lSXT?9w}yq/V"P*P^qL@:,4:y" .3M\(T>y$|:r b!U? yUF kdzNX./cw9nC\Zc|>>1.t:FاG0D]mw&UWah2@f2z%ה_m5]OlSql^^pFvU}u}£嚍dk6KhToBp%Ju!!UvBE ˁbpfCeĝMZ]#! h| RœI2z5#^8!hf_),ULzء8] a $=+h}3]Oya+S ~OߎC[t ̠L=zԪsL+4$}LP䁏{]\RX#Q({WO=;H=.bHWIcj鳶Ukц.#Ԡ{XI5㒷p'Z-ïG (R4Dz .c0=HC4w&IG Q(AxӲbe6cjvhx( zgߦhN; syVX :k/>1G #ny%ǩѷb>ȵ\T!E/p07r@hс d0Su52#%.IJpkYko;$UMiflE )PVu|F z< |z}yNqN7Hù{])b''{o$BD?ulhW3ŧ]&}AfW&oKw谷|Lj-IϘA9z2\yk*c|#,/g=IGƳg{_rknރ{5pŏd !}Ķϱ @H,#C.+ R l J[5+gH) L8 6ml(R X\shAbcdz1x9 ÄMRm:"e#0~MaKEF3CPRp! b`$Zf8x怈IO]k:ci I[H&J;K!-dٌBociF`Fr dž!e+`%Ct;T Vڮ`e;C)ǐt>.ZOfr $U@G .O.ITpzm\c!neZf3ZX=|/bĿ M. t=p8E Yu"$ ¢jc 1PYWSf y[E99W TIfr5s כ*4 uhJ4|)GK8c{]$ ̲ &ڲaNzClܬ9y_ X1J"w0&tJAn.ԟc7c pۗ$ *Y|DFB26R3lK^\w92?z`^HP;$Ļ \F<t .DЕѿ1RLȯ bS`aF!ԆuqK.Xji=CXXc ]h6pi7Q G(F{W2Rf_v @xĸ/w\=ـkR4l~P/Rjf{՘˵Rj_`ּD**ԂA隂(ƙ'M6g~eSP= ,{!ʹ8~`Ta]@ TFU;2Z3ٮ0Ŵ jBj>A`E#NF'Yz܋Z5P}wh>dy@nz&,((;Jv32.Կk@|Qt ,PG8X~kV +;_C>vY_/65p.D4SK27JKbN Q ԋ Un6<{aNǡ9B}oKv3T: mY^ϘQf"Q4UDs9r˄Pf@!^xɣ@m,y=& !S 1bq SZ m3"Ro CZaH0 Xv1iN/rhsVm?`9GiǪ5 kd)znHI(p[=PzDyWWW5ُ_VMͩ8Ma|[w BaJoxk{w>&h5ކ7@.Qaې ls[F&o-ſc%cv"W+B Nu8]##M,aE 句C =RNdN^<7 gedXʱ[QB@ 'ᣄEkk_]U`fQal&YFjmV!8Y$/!wr(ٙ;tH')P hI`z,9;<bURs! 7oRQ.)=VtKxK_~.q埭M˂K| 8( pO\H4% .$竖G8ׁ1kMD@ }\A(+/0i^_3ǔ8T2!:I+7m/r =h ~ƧIӦq\2\jox0B?H|ߨVV[)/25ӫ(.PY:+zS׼Y3,QfO-W܅Wzu\ٟ!~^ 7SP"qU-f.8yD $n-ִtEG y&/3_a*tvt0BzIW2r.*/쿌~({kʗJ8')<=n3&ne<$tETPvaruZĶz׼22= KwbQLjMaf86yF*2|O6k=BS_ZGf+)#U )Lp9Ri|*N 5T-oȼ#SbFZ/nCȞ85ZzM_?=f-%-^<]ƥbSN,VFhXo#xU|s0D8<<*ؽGFlV*l|Xa6aF *[U sD}dzwi+n$z;dE> %3gGd˕b_[w&q"*)-`-oT>IxPYYǹ4_3瘮N=H񰃹vF;Gyr抅S\UNf@Y+wpJ;jb|@IY ͗9I h]NRjvI֕ȣZ&zbULWU;0Kp.܄إ{?Qݐ(,(3Dٍȋ{@(?N6EZI|)sezHyyWOcՄ)e8 L1235'Y$PʉQOt*,Y 1z[(?>aƙ†޿v & a1'Qnٽ y%'1z*s< 9hWWSh ,0ؓ=XEgW[:g9EŅ](`#~)q~@vgxJ9'/+0ZW=< J7e:8Q/܇ȅˀ,護F+"𜡳0\VÊY(Ac/ bxeӎGc-f?LpB#!cΉ^}ۻN7J  m2xl,gtʦaG˿bdTpHg7NA'e_[6A\';;O tvTL˻ˆFU ƨO9;̐99|Ol)nDg{dKp'f'5ݛ8u O-gBpK7e&Kj\|ۧ5Of᧗17so]?S oY~+৅؟!OUp?? O4h#F dݼo[ݝ{;[w{㉧_Nz!DU{. 82:=M\_ 2&K$Aց)HqW/AB;z^rCo*C 4mT}WFj4_cW@l8JEI߈?1 y,2T1- u0bNΌ tI%QdqƦ 9-t03*dzC#8n$\" }aDCj߰L0/|`< `,W8AS@rlA`ED9.O8]vl}_}, 5P2v 硖UJi#D/QNcDIl0sx2Dys&jx EGnJKԟU [B^ y  vnKU$5@%hio)X4fuIM#v$)Ө 2b"}uٸM,εi7֦zyWQ8TԀ]](@r7z=I;eIg@fd c Nhyۘ9uǽ}GS9K+3`p` C)T?(c4:k;V"1ʹU"qIR2~-e~m,w6?kg vcw` YU s$i ˂tމV4\hqD-g+wԴ}W*[%(}Xj~`x[WSt .ix9(b2߶_{zz€i7m ;+l(H3I2r1ta# hpJNqRߝ p>NGf}) kH?qb_Z13$h}Yłg2B+·+LO昇Q[Ds<|̫!@M* kckɨSvŷvL{ڎ{w{UBRS+1i"{[}H}WF7GD- qq{:SOk?&LV'RX+_ w҈n^*XU&v+ ɺ+q9Z -;C9\g+%3 L͢o}Wv샊_&3k;T|Y gkB:f]ןqFɗOnZ?"E=>,؂󊊋D>.S'խakb__+XɄbِ_YqG?ݺ2mb!وF'&] aފ:֡R.`=~~ugʪW><)m[ośiw.igO?OK!Gʍfj܂jFeA}I\C :`qck^Vam.6Sj?\#= Uc7 8;ծC.6HLaJg:,V8Q3z5meuoiDk N] P> @ ^"ѡ0:Zb48A y7n,M^Vlv#no1߄'_P/8E+8=Aƒhb.)Sfc e:[r L `D9&IL~lN" s (mF@WpޡHԭ.ab~cu2){RWc 1; "H˜ I|ZK''d&m @ ag32؟$AeCp83LDG6.jP ӘsK SLC =x%r@)%^$>RPUčڄz;_CJtS.\OpN9E98Fڸ(OLL}?NӤI7*}LB,OOI<"RYMHBD\0@t4A.j.}91b%];s6Gri5CdYrtOSUsZܑ7ZSy 6%[^,F@&Zjb,W1v\p$TI-_C~uї5ЯJV]b3 ,XP.T4 I&[=ix3Lrm'0]r[K8xB^}Rn]5HjB >F@UZrXqn h͘m̳8 *,cv$Nh_n{n^KaK>čzD=-roKO7ƪ-dXWSì?򎵠v_kAt[ĬzP\Uزn|+zm.jѨ m`X!p]cWwG0c\ox(~+_WzZK#oաxQN/Zg9l gg 肺 ^ )+y60sJpqEڥU#7{]v1kf]WSmՓBT:%J:zD6zʥ˔"-o6GV_Ǩu4]H 銉-oZ!JXJ.$Qd:kS6!JJ3g KBw[ՄV=t)kÚ+FY2`DLrA C6,lX Adk*0_XBg<|Hw@488oDt!DkB ɨi|4M)lǮ2 Їa(/H@7{Kֲ+oiQ67wL̹]cS0ji$]a,WAOֽѢ&ilƋ>1<{Lqڿ> +LXk _XDU Z"~KhlX@@Nf"3*SWme(sgHt՞rT*A-kp[l+: }:lUžVAAۜ;(BaIETK6WXV^;,^"ʬ's%4O%z\!{@|Y&U1 8* +Zy3r#b^Bp\/PVR!x~, bDD\%`د"8BRR0‹Z2T6pV+)8g+ –ퟲGp's! JOILZ 1I%.K p0PR-LrI#])îOnO BP+14*!-䍊Tv;āņo8;S'9Pc?z "#v*1HShViN?Qy@ZryDj" /0:`9P"&މ8b8ٱVz.n:(:/@uI}QY?-r9 []H.+y<~CR7^m"B20зNYLRj4߱ <}ʹ(7]'gm@*#Jܚj1>4K}Ԭfoݍh$Šk1\F 9cep_`Wf h:^zK3,e\Rw,p[WE>2KA(r*pOZpxn>f8u%+x*fX7ƌclCI0~\i(бem;޶9*9~96OWZ`T/xmzjbu 诂kXB'&;>{P)sE"/W!~FuÉk鮌U<_cyx`"pZ6긔oz钾uH֬oᛜ d BWYWҊx0B5 a-x&|) d3ɹ8ɳ1#KYBDK':Gy_Ru9qekZRvCZy Y0J⼏& ЁT_iol+" #)Gr 3) 3@mDUg;mO?|~tx2 QЍ:?HYiZ+߂7 8yr(fc Θ++ӎ-wYM:B@o09jϢU: Aњ?DxkA|`YwEg'@YKd8d|CV1h /it zPS+[I1+2Y#!0dM'=ɥMhb5eLk5XK]ߎ4b*[:W۔Uۣ'E ׫0WrSϝ/˝EzM#>-H݂ -*;o,UYHdlnz~ZswVA*>)~^9 ፐT3{FҔЬ $[ \(bGo/ބK"*?DxFz͈'0 \"<tYןJ>V0C{APc#*l R4ѵn[p:}KKl響\zx(Jbr_[GR |bdpFK'xp ~6=siR]wr-= kױmXZ#2,%+TSXf'G6;vD{̩(1+ٚηi!%;Ȥ/vL|R=BJ|i(% /@|C(9m32οrUdz]/];47ߡtI\%=')dę\A^TDpQ?"#;:70c }C.vCdѱ!4hnƲ%Og:FA.{& 5] c?ctouPay,yR<)YR $1Ɍ# K4sŀ@Ft)aޭN0B/ Vn ϐZ 5E ;,@RR窙1Z{[T8#jP(t,WZyŢ1?z>uEqm,[M߽fOsl|$r R^oc^~;YA9el7A<%/u|uQrbmYLճ> Z?YGofeݭ-los{{=ƿhμjrhsN% O4 n{wܾ?'o;9`;woy!ֈ Ҿk &z OGYQuQ5Kr`6N/BsMlZO@px~-/{MGL9pqn:mAYlcxetPppyeok?0gW,mAo0 vRJ# <ڛuNO˂]w) ZkKH8?m6&$~Z\ ͵CH;L~uGߗGw=kU`+kF0.`^<)hIąTME RT Ή.4 =2GZI(+| K$ 4 c>AײPHQ!H!CmUմ(UYan5.ݓ-ɟ9k@+hC^#*0(y&܈V0=M0r<N5#҉N]o6d9%(adyW D u[iZݱ#5uqAnfj4yaFY3FGꂺo-U^9K"wY~r_Z#new&wm)JG.w6G]s䤆W ccv?m$·cPkf<)E|uJO?7t,ە7OY~=Na أ 0#NȴpW~]nm陽fp?޴##pInow[ ?wvcl~ *85RРCuc\*:W1cΎ Et.+!/x峘] 0f6'Yf#73Gw z~M'٩}$SA"}0 xBLiF8(+y[jN+1tC\PrBljJ$`--D7?J1 <`]8#Fhxю|acph噎E*>;z^e-^۫h1󿆓EVLf-ax=eR͈Wb^;p$ 焥wyźNaٵG%( .L}X=bSvm/4 wiy/>RtQc<*_?}*^A^+ +T;I.> N0ugҶ7 #KJ`skIL I \.BqtOX!KoVc-[E%̭X[v4sh&>zwyW~soI ]-iZ&eJ ?ś^Dpx1i,*Bm-LQ\ìLI:\S™՗ 9t0B@1 OU>]DNxdrHl+,`\;Y'bdSx"o ϩv!S:s]TO 0#xRYv'#d4$3Ʌ}A$U +laY,!bar1Ԑg{OG*aY|fB˧L_P1-u{fǥ_' $ kYN|B`4=O&$ t^2ff8(4N^6#爤q7 pPCPtJ·\N<G('_v %zsFFp.2b>1A' 3hlfa@cKPu. wj[)HS_ΈbTdzf̐G!Q֘'E6ta ЕҦ썧  F hT }^/A tH=! ෆ"}5*?Xd +sUuߧlh $z?\#lلCs¿SFq2= 2E|^ת=*>@Bp4mj`-q迓<4vdyw6ݨ%lY@sgvQM-ETY-_zͻ}ը,ͦdV%bn.^c@Uiw}8d/EhGm`c !ȮZ3ǪhZ"gJk}=LV{5[eYS>~WQwKg]LF ih*ᤢ,z4ӓηhޙXLS<'>D'^χh!N:'W/ឪ{ItW@N#-"FW໫r4.1\I]?2nS;NIZi298RiXgR5@ Y\' )8CZ \ wkN i.s2&]8; yL@=:L jkXe{sOF 6&21rsp ׉2`: cWI ?i^iGb~ /yZAWmuVoy<5CEL ? +>:΂cU^HC:1<BcOX}V(2N4#Ő7K.tA[ R_qhopezpPZ߼Gl(@rzHLHo wFXx_ke4ߛ Ag'fAKG6,5z%H&7eŶJ :uy jԚKm`Sf`Yf j`JrЯ稤Uh>sG.xYe'8E$>_n&<. 틹cD(3Kl8P)ȘFSBѫgtҏOWvT#I%J@k&SdiQFJ˲i^Z*$WIf7 2XK_^/Hloҏj۲J{c7$(Wtvhn$̹PO| Ϫ7VkMIS2-˴{uJ!g1iM NQbw,Yzb= '%%ް7*5S* 1 C}~~RzRXRJn@T"/A"9ýstpHMy6w*ϴK 4 SU+5+^NS䰎¡_T-K8asm Riɔ}u'y?sjZ1p7qqgըUG-VS\ޅZK)Ù&M]ru::kހ@.5}6̧)߀ j,\b!\ϢsI:$7cdզ5\K'~c#E!ȴ##A@iJ*9p/z{ lgMx򂻞TrRU NPE|yZ|V@{M΍EHl?cS_Yx\M'*RНd)dJg"PzCg ׌~S ) M'J;pJ'DN^ ^;IahYSѰ63pVWKs1ˡ;{w-@2RUϸ]:Z,D^W/y┤tjQ=R5߰ R ]3,"]AžuM--9΀'mĝQhGs$mwM}D_}!I_dcbTd漲l+&qBrIkFCS[ؒK=K i,rZP | f}JbTOgM't, q Ј^L'eh6Z6(6Rϕ#X7pU|;];^^uFԄ*oK:eJ+t4e D9,O a/ʠ +}ij)>}B` tXlU/#j$7e[ZSy$ ZikԪ9H\IkPIץTQ:5`‰Kcw%H))ju1oڕwy̞Lw3¤9erE _L :D+!X+!I`qDHERE6i:-06osbb 퀷^:L=#DiLp0>e8 bs^?ݽwoov{,?Ao?=MVwW a/4Fj;}~cZf-x Ra̞˪mx3_MԔ4fcF iBI1kRPI!A ӨOOA E_˯GQ3vw*A7.fewirҲTk*U$ i\@;Cd%鶉ӯ+:_V`n8A ș{~eC06- 7 ~^S4*+8浰DNKG2gUL u#W j Q=޶<5{]Xniu*04kXzgY6z!#a.H41Ghݤ&.F?=AcTa%B_NҙāFk[ \A;r})Dr5.Xj ^gmH7W\VH3_@f%4evP" D'\N }gVP"qW䋄U^T1ElqXC &xqT#Y?EEAXO%/QϮjS[Q}Ė7+2|)"\\Z?bope,r,piݾB(CBR?d #˦b) wU}ʩ %yx >_AWc8ձ#NUZ޳Xv]ATל5z?xB%3W磦 'f+F=\Cu3xdCUCYQh){PQǟz\4^qz>Sk1ysiq@y%+Ekb$6ݭ{sogkk[{?OӎfgӦ@;[߇<jN:SE%EDYc n6dl-lܦvL-y p/ /55Q7b.ʨ29v~! G䮟qۀI͋%}cݺSG\)/̹0]%q %3 0v}+h3K;ŸBޒš f|pJ0k>TDM&rC$9-q:w~n$IEvw MQkU{^erk@|Ea۳gv|? LěШp C,P?s5 cm4 V-rA/:gVlr;KJY6 AS[b o?KYםHg TzOY?ew7۹g77%ھnv~/n%6ozu9~'3w>mn}!fL ݫ =ON'td Gp'vG*O-D15?x)fHtF*a8Y@aFg# !&tgHVFrqibօ;oD06GMԠ-pQ܇/ɬBMLr߲P C0)%cBu A2F >ͳY"H'2rU/3CJ}%U+/t7Hh`6Y dݚx|S)>]B߄>Dɷ9iw=%o/_{ԈjRU?w4.\~ٿ(Waǀ[EY S$G>0GٌQ 9|z]dy = 9i3Mrz!>}NhqVIxM("-sCZCNZ4jy]` l z=P3gUti]\c:IsFo{c;GZ&Gnif#@jhs -nuZF×*7ݷH[ MbKߣI;5O^VC {KQ:pXWR53B xWhH 6GtB*h]owdB33xԤ:'ÖYMrufn"OssXm71U5uRG T9Exq%0˭yMCo9AC4:LD$id9J[xwuɵuDuƯ$'0Qr.4GCpdh52A>v|Op˩BkrUM/f^yH9m򹗯"ij [8:&i6/^>UX͸FGtc#wRW8Ѓތ,*U;D=ϲazrK쀀Y0an-*~(WJq.ȏR32a[\Alزn}Vfnl$atU >4']g{eu FlU nؠ+1ܤDGE.}Uό"Gd2爦!$b Ic.<\8h>x̬s2;ˆe+6 4ƕ0B#6`e! U u(-gG#G`&S*)ՠ\ 2IIf, %Q  &~ծrj7Jb\iƟ?Kxzm,nzJn?V.);vCLL %nU"F$2=ae϶<=y L;N?|no't>=[(0鼿u~t(XNFT3g]P(czws}s[Uuns/kT-j͂kͭv6{߯ǚZߺ_wa?7 qڹW6VjU~!Fhqoh4vbR"x~I9'!ڦ s]AP+N=;Y:8s}smL yV d5v5 zT)rrlm H:k!0q]*mm^J݅><Op D\'eL9:paǢ924,GqAi&J&Q;Qg?lG](N Cbv^p Zy 5G1CMϣ4)d@ s)<$Dc茎 9z-Y U1פ`cNjDI;4~JKl`=CHV3Erxҳ<>9I6M/]OqW0D7[lZ3~+i4w0#>EGPIkTl"3v9B$ŚP,WųZ\#'q#GI)t0PA1$$oÐʆdfF[s_S3(7gʏi^oKYFcbFsqAe;f4?&7TTY &w$Na]G JwB+ɥ }ٻlb#' rabۍfFAn%~{}Kjn1WI=u-,}#Mf`gU|38NGabu/W>VNsMul6|}I0n[Q[R%tW# m+u;ng\ˁudQᣠ${S0\J2ϸl~bZH7|i5_4\>MnAyabD ƒx@!p>IC[J | \@hzHP\nXt'.\A2sP;ˎB'G17.҈}9lWۻr=cʪ|g>u񗒲 d9F~=zT o{߭UE٣OZ9FT?Xf%)[US7HbtR,] Ӆ4 3 (s/UY>r9RPVA6/ċ+^ѡ N9F;2$@@Zdds=F]@ ?3&uMG}6wzqwk}oY~&-`'dAs_1jrYPq/HUMNynk;7ւR6Y'l7ڛG_B[6,1c:v `v_! :evDBGڥ H1Q3 <}>v`.\U oŷNWC'9WWU.XCrxG F/9(S1~ } aƀ&D3ButCs5EoQ Xs>z7'XGn7PxuPP{O秴'?CUxÛ"Bx篅u >\iYK(n%䘖 3f b[?>E\Ź%c:h6̅w(PGR-唘귦pp6|jpmO 6ZF^KQJVi@RpG)#mOZ -Els sCQk"ev:G3Gػˋ#c4k$my]_vJE%r(CG5eH5W=*1U7*Zӄ6dQ'L35YAz|;,r1^p\Ï4n^a{5v}] C=cz m6)tݺ;/-rԂ~{^0*bJ03_'*+,RFzsf9X'Y~w>c )qP Jɚ])٩>E+8Pd/!}ίwZXj+OL:_XyxJX -;򤏼J̩TI|ݏnD]K_v,[ݻ3 zw;mzwn½K?͡ d&75g?8u +~M6iMB=9ۤc =8&F,zjeWW1ػp @/0}'%ܹ)ݫT8œas9j\ԓ g77,GV.2oc^uA%54@k?NlO)H9MS{e"q>~ʿ`0My~{Y[PMV[i!oRP9s(5塠βpbk[] ^rpM%Lf\|.LxE%O@*b&t˄$?؊߹e/N*LgL=v{Xݒ_=*}k{+X-k>X/k_<;ڷ~0ѫ#01rG91w B͝aۯ/pFE'Z`]z ۹ۮcG}gMR,$0+Nd@7/P e(yɧb`z.Dk؜?yWC) ki o+K}=ᰛ`-Ӽj 0ft Zɞ nQ `q<\^Dfd4Q:mүx\,H jT23mbŒeh'% EWnݎݟy\OeQBxo&Dv[X-29'e}2^ ܪedIkw8\_]Rߡ@x^ #j-Gͩ—C>Ztиޖs  jqČuiͥ,0b]e-œ!iM SsE3`!\/zFl1iV{ȡd VkGX%PmB&y~pHknuuπ3_j1"5ԱAS)&)szP#L+.@ePR`aH}s.Z^yRPQ 95iQ+=s3OĝZ &wƉAO.H M4CO7[U{VwYQoڸAY5kpKj8+ MY.UhWFg2j9Fj-L;y2C씴3PNs8[oh_hdz̾WN"v**Ƅ1.xsKzO(Ynj; S@=4tj%\1ͭ/1b%Z | 0t \B24,Tq Ц (H#)`>z#Nu} u|P1kT ݺ|MR4Z+IFNt$7TQ@9̀_{-jQ5& !wJם PYk4eD7CH3eCL f+`悳4'6.@xgz6D׈gd0G5a:m?Bsx,ɧDԦFؔyR>/>i'}IqtI\zq"KHvD n6Ebװ}0)e{>e&â 2r4G%)SOCO%ҐJ[؀ej@|7, >Ou=ܠkGWmv9|N\eߥ9)T8eǍ=`f܋]Vn]|ӥz>s5]!~dve*0CnCC) R ВCi䩚W~gĕn)A$VN.9rOfWI4t}9:_X@0$œA2%f5jcK$0 w_$eWk.b2 8S'8WKy#{S(-S#7&m_X8 _s$ho q".7n mc+.2/Q_ym*Oۍ MtE'F*F%!9XjHV<9Id"adm )2K4<S$rbl&b4kDE VIa5\^V<&MNp\AWB-v`%8')ı/D,`QO;Mwkᗏ!$lfw^SW ]pSd&T3ř4Am{ rӏ->~7A|dR!͒ ` #N͡ZxwJ{)A6?3ڋ1V̦Uz' N#~{m7MAulZtx14+@#$_`:`,8%!~5Jz4F&(h&{qd\).ұjrgO./Zy cHřF8|UȒn?31dt 0@Hf%k w wVp8K4rxN <5aH$)@ԸfdF328.8C@0g!ISrtb2 e>D+&uiM4J"ŸD/%Ri3G$9ʀO_%1¦p )hQ"Y}_[|B܊hmdqz=_ vfKd@Ss:|>$ѰS8:˓k%Hu> FHԖxivA@*x!tL}BZAgQEšUqpwP(j"L:腰72gLvA'=YIb`~Gv%hQvN(NEE`CDc'EbΎHTo~P"sJ#2iNOrL aB1Nm;N UK`Ů f 2#+oO/QL%0p6W7=z0JN# =&hln_:ȶVMElV4+* 8ֱП`gmGKv vryx'Y|w%7vw2 q%- E[ȉh 縯(=eoNC`E*"EWD |Vk۔%w0Z}>Aiyt.g?#?;,w ڱ'*٥:#vCI<щOฺmVƃ 2%]R@P`FaBrҹ':رeʦxtͦT^ҳ5U|i+>\d*\_,>U2 _$kڔN̂tPY6QYf&^1s(tZѫ/|E/L17D!t 4gk:KG̃CUW,la^$ &4QkMf7A"׆kaRKPB !E:}12źNG4_pv% _H!HXk~T_3Z9q*k>n4ZL %+d5qμ^tVCLᄲ&י_~{ϬlXQְa Hs>U92nWܘB`'U*I؈-,/oQW<]n`ِ_i/{VH(^T62ÁBu(خul'\J HZxWUB4d b#8=F5=W䝚oB Y#|/.k2|IPJ?B2n A>~GJsxypŋHȼsC3^?=?}xih߸]WU7W|)Gj4^|ћ1?inbB <-| @+F_cRZ!b*:9?,@X@=d>"O7J YDx%,F*-amSZΉA8@."v WҎzБ: @}#XUZ+`t'lxQNzɩӨԍh4=aڊ/Ƣ7PJ Ы^':& /ԋ{(J31oI4[ =a%}ɘA&)OnqQI?qzp |;e ٶ]2eSߜXEw7W^lE ܥ!- βp(S"8tV|ڴk)?[PepKOoM7bk)&jeޕ#VO%Ђjzolz))TSgW|+0x7w{@cba)}Qyr^lգ@Ě},D"Px5x80v7'će(Mymoղ#!4 ֐T?`^t'մBR=· qR ђ!p1""!250mQ:Rr-(W   O~!6\Ť1`ێ)Vʖ٘-h{J]ݼn8dPe:to £xgpQ~Eq5I,0 f ZjyH 6d$BkMrYΓiWE>ä4m !1JB9/!ڂL'~]F~+hJNzqB( _;wG<:9TX4_M<"ʃ)PH&I[tv|> Wj;3r?ě>q(_O7"$tm iozw~'.Ӄ""Ʋu2tb+ fM~FVŏָx£.[е{hMdWVpEJAOp,< iMX&U{GL8ڃBİ+JӮB]塜huͲ.ЭJ;+`P`Қ|mew "{Ʌ_56-Gog>Vͽ>V&'`WvBv9:~?ڠA*;[#; / =J*M̔RՖ#ų Vu,% Qftm/# R& YR;u*=ܐJAY6{ !9;֨f _QttQgUS*'`b(r^#lѰ"6K؜d7Ԝ@cN#Jz0,oVl6l +)dm6,VM j4z~h€4O.]@Wk[O^SqGO>2Z=?4_س7_=ؔMB5X)V5MO nuFOGg?qo "la#f-Ne,`W-jŸ a]T:ͯz2ڌK/҉ߵuH3[pWWjJj*ޛ3[ l^ ЬYGe{ʫV\+V _*QvrFf_>f>mZC|xPvKH AiR 2/UUYz)Q;ncB(̡um~^:M`Uy\h#հCdZ7iϠy+t=q(1p Y*-s>U׈f=0q4aӐݯFJ \hۣHGS3WZ AnԊqq |Hp=u"3 lW HLzGo /Ltã\`5\@k=V=u#~Ȩ,{C66 oT,$ ,VH="2o/WGgG>Yzr)Jݪ%`tS& "p$þ~9Z#0kYfWU%oaJM "xaH(_G KS) Ebѳr$Y[o~U+ ;ً}Vژ6yy 8TmC"mzSʋ2 ;CcpQ_ӕEc~$SD+!ɐ#It}BQs$yo4]_yunNn*z_׬me=* l,'P$d==xsuԨWj"#:E&6,Ix Yl03)JiONC#$MS9xtMD4oG( Т:;UAS6nd~e`w4HgZbˈdDy]44;TbyJwhB'&d F?nqSUDn HH%O! OE!4X."#yAgy3.ůʡX2H3MȸZƙyCiAo~.xv &<;[SD9ĖK`KW3b>cUYc5{m*+%?X4,!~.g p]D↤2($:!L$DgEQHK&p<8a$> 9aWd ">yA#6mkL 8Яyǭ5oVkv(ޟi'%.&=K#,FXnw{uvnnnoww'V#GmFv535Zk1 Ĉ@T ڢ9]g' G{(wpl:j.[bBo:Eud2Wt>˦{)F[<{) Hڸ6`ޮ/^o۹D7ڋfu@~)΂ۻ{|'?CGAFwr?lww7U< |<(="20Iy?Jqݨu;Rcf#3C٤RT˲c+z1%,;l7t`~cNN[74Jⓥy͟cw>,[wn3\_ ̟)oor#vw{? ͽ#v׬nKaNBF:A>$qŦPos]|TG=G51[@]GmX 54|p>lGC=`KC1B+ 0 X\BE*d+Y=ɘ/Sg>/rx2y +mÓcw6HzOyLGE_hl6u~t>>y`zD/`;&~`=>EO{kqq(Ge3 J˵DŽ w{wtm3kQsð;,ACX9勶IZYE@Zyvc3(bꏹ)n!6Δu[ ExiM㢸0{gQ2^]o9U*DWVwЗAxYvX'{s%#fGOL)8 KɿuP ׿I4OuL,ێ!O`>_11XnOnDDmgmmnw1߽{w>ǏzQvͨޭ7ѯGϞʷLG땶pLIc {|jeDF љ$ s^`*:`wMlލ:huN"wmƁs3Ѣ \9; 8DzUsN}mK > SXnĴ7视C:N ?vQeνowGi=_ocG(7,~* x5SJO4lU:WY91J-F_' ^nTm8w1G#ʭmV:  YGiU9B5,)UlmuP]"݈pޣ{,?enYy@͝p֞,wor46Ms#{;]w7}??~&~0wM4v7wRA&Ne6z ,*IØ=x],;N"qޯtL=YA*kX]&u! cxR8W04f!Y, mp-;b&Qt!8i(mյ76 OAߞ5J;x|uо &$rrQ!z9bix%< NfPпW>m>2{S -h]HA-I yD tD7[fI@{15Mg+A'cڊv#-O -U{O~7=lE\vhod=l>fLɷ; ;v3Up4B$?B"]˗_uh'<O>6y'8rso[7S+?4#7IJ^Zn^'1 >lE[pYaNTd,圚DEqO A ͠tDiz/$-cFBy ^`4RFİwoVb7})I<0 O<1tCP^5U*J. l"VBa06HWB_F,\yWXTe)[q;O2SZHb-\FȿZฆo"na^FUgJjP= G?E g\qw}n6J/ZkP<%c3񄧵 q2(#Nm rH#z}$Fs6oͿ| [xzeM |,>hOTTQ/zȅkTLZ9ܻql$9<\FEb#D{^hc@.LEh\L(F0vC@>X }烧D/~z X+baʪu5Fѿח{OWZW{r\Oԩ/ Պ̊tif5KR(ByZ$9=7wAC*\JN ء{9l\5L3<*WyZ%kL7U<ߵ&$I'R2OSğӄOEwJ,!Dec)Z/Pְ}v<̆Id>6jV[9,q'{T!!eH)Sb?esc Z]|TYj5u*}bVQiA6K,$$a-hH>z[.5$_p \21 OŸKO ;@g-1@cU„&Exu-Ы94Yԙxdu#a΃O{nM2 0|(Ap*r1껉cHrjߔp2LFKH.*;J v|9Z#Q@!SV]on=[?hdAh/Us J \?d3iu$B(Q%.`A`0D=j;QF,R6Z$E  fI$(/58 7f<}YhB>DKdH ~ ͊T6pYn@cst.6Hnd}vbWg</;5E|$T}ِG`q+ד_㜒^ght6bcNE;Ary(a~K-XVҤ#Rqi٦Po \mKVwɦPLsm;L3sOPᣳlt=}l+!}<ʹ|,r.4N" ě\ YmrͤaA[윀Bek V< X˧6^7;-7KkVRyyS"7*QiaXGG/Y}Yt_b9&vE=P]Y#m>H6_zgfPNKfJ-*(ʼ[41h|Y툢Ic`gc_}%]Ł~2򡒳LG.m@)wl|'N֦>Q|z'u$CcOO'PyGϧ $JL!:Qbah#|ЉY!W6 .Jf_3JZ$6iϑ> t HG _ 4Udq-)N.g[0JcViu6v4uŧ/҄4y}lwxp]ĵ$fG= ghQr>:x~l^iG_+iq^fvnǶy{fmEGP7S"?|r4{eTji4D[rc`3c22j=r3/$,0uZӍXhd6nqՍLe79DJtAҗkMjO0| ׬۬+w>'kM#cd:He7֍o/?QoKGQ0@u[k"=7Xl.<'h C#̔ҍ|}6Ɨ8p6 F3L̙ҠNTH')\I f+eABs aqf G}a v`4CdղB pRO'vJ[m1h+!Oj( u;3TDW]7[7]5 #!oوr ;) \xv (S+~:J] bцh[l}un_<)${=y]%!К^nT,viŻtʛ |)WU/ܛǩY#i4UTfJe9;3\.+,҃H1`֏&͌YI췋n30YO`͌Kاڵ|ܐ .];H(ˌ+Zg|hk n֢'דf9nizJi$}<ݎ(R[M-4~"CWwG5${S$` 'YƂ~*7lħsЍp3O#5bSD}ʤ Q#A'{y9 fn|,MVNfd}cTS._,1^QDS#CXqQW۽_q0R k jL45 hW*c6;O6'RghH8vy<`'?#۝з\\y _,7+(*$Alηi{EUb*1ه1\9LԜ% 4%:N:ўFt={CѨVh̖8OF5;2\ '#i-p4[4jޠ8TVjqB{w&b4NCyЇY뗣dr:;׽ d>"^{cCP|qӄuБ6V%WLw-.T}%{vd1;QEoөg+5&my> 5"gM^Ҿ?Q%̋E[i >K9㉇7W9;Z!Ǔ8 ΖCJ~m6 11c;P=Տ?ثOO,{ ʔ\g7i2IreL1PuFh Khz̀ttZ,*FLv:J鬳v swEZcOOW=ʬ!zFIbNE\FՈzf( J\jId+5C ٻJʦ{'EnᓍR,GM.n,.!i`a]FadR( nYxBgrӇfGwMBmxreȓ^?qrМB %~l5ASav ʃL+Dso AbX0m^L( \D#S(ꨨQƧI6xDBS 6@OS>CULeW (\ sE2T!0 _0DsF!q:a%j_gEOw 'stǧnW$c3|ﷷ,l\YjJX~@ëkFxA?M#F*$Cdi%M(l3NA<;xF>2_(]PڛsF_Β:U] nUP,xY &XD'p,\cWٿ>>6 μ{$#VNUeٺĺvPM-6ǨV`uſ+G.pzEqdgו51촵&߳@ȹ;xrܭ!Nӈς Vؤ1I/^4I?arb:(1?ʙ},)w8w@O$l6@wz+Md_itQZاU, Frl5ɞA>%ҵa^_a~X|6A&}xlHEcmlRf8a5SxCћ hŮ?#ӌ.!{ iڦsm'8"]tOMV'R/+QpPv tH@[N{,t5-Y"\jJNŐ `&eNz>b JjL^X7^u"v@k^BDζz+ u‹ "ھVvC>=p0ʿ<\ڄ\lu{OG{.ļ\ָXI!7)%ԖS ݔR~7Wrd'KGޠHW XB cs Ϙ [maYШN_!~Bi]Sp>V Ǻ9)WoGyDt!@zm/KJtE#*lQ}++<&Oς3! th`h#S^vW  O(\̵<[<Әu{ʹȅV3WH*`zm:_]'{TR"rO/f/tн cKmՀ&Ui x|dCKi?ts|)Ny-HG}e6RLm\%~,y*_ 1!"qba}*Pؕf=0>Th 4ݴ?J9, UW5>c#@G 9K8|i>"tHOcȠpLFhRtѺ{FTGw(/t$'󬯛^r@nuG- gɗؓW?Q<̲#P5C(0޸AYW$ Dp:#aaK ;cϢ=L%Aö{Eƴw=`},d_2NCӌ I4=F?B/w2,JQ^\zZ>/`_V[3k>Ӊ?mY`o_ƿy:ً\T+1'L+l f*=+{,c=W >pwǞ6]rOS#w /t$DY0kݶ0 SB1V 6#( <OwuI>oB.hҥ '+TL-]6w{= W~I4Y5-aC,7BRh/n{ ː=RxN `pGɝKT.5Bo>At@@mmeJn0ng TKHf{q uVP#_-bsr8)YW裦>4Q5ɐp : {yqgyA4ǥJsA+VzUyZ9!:]T 캊$/|'Qz R qTk*2JJ!$Rt&*.y<\Q<ȳA#(:" `{I JV1c>3)Lb|>=SR|Ϫ1`Y#僓L*KMпHN f('猙!)/k8I&*mQ0ԓ %\yI ]Q?*w,q! _py}d_0 Gw<*9Q҄%3)!}Z_ʨ|a$Վ= D ~\WP2a dwYVvi_y.lOwi( @nhtRMag 8 HA Y8Mk贂j 4PQyѬVT=NŽ]Vb+t_,=,Qz#?b8X5x5!ԙBL3F"D$cGVܓT7!\%  ]H`8{{)a }s=;Y7`RxFtH+JM9wF56kTTXO>LR|:rh IQ:yk׬,~|ڊ`L! 6. q<0=JFy PV%D8;Ou7{:_ Nl>eSynΧ<R5W"0%TE "SIȋ$l:]mjy$ދ>ބלA8n mM*F7r9[v6[f4["`V[/S{d=j0Z=<'htGJsy Np^('uM'5zzMZ7T(]Bv^# [;SOFy0l⭤e1,9j얟MM#Lz4kj5!ϟy`5o@ޛ6G/[b=u󧯟FJiE>m,tMS5@67?F' ft٪닦vKf^fˈeemn2=uB BljZo[mܞ,bX q(Z|ȓ|5Zy\Xo!]~jc GLz> >!נx'ܣ˶jߣ;vo{Md {Wz5QjO '',*ы jՑѵXCB=I*w2ND۴R;T j.b#rk2 1׸PƅbXǕMT-epEs,"dyGZ,CL.[Nb`/U9 a1Oѫ)LѼ"DCY:ĩv5`9E $s8/>}kz|tCx #?_vY~nAN{('<PjSq-fA(iqCٰҍaΩ ԹJϼɍCwL*cZ ><߯k'+ 7C[̻f'Oܩ8E0׿&J2f%Ody^660E3ZHL8Pl>I>eGM5Y]%+V2 jıɯS,dao1=j+st>W[.Fu%8S( ;[*Oޫ]ʍ\ hw'vrx?\P<<8%H`koleP`pcEl"F(bKm$p^)= 3wf]iz6ŋyPDb ׳S^M V'(4Ȧ x8{NHQE-ﯴ:<ԭ6<(aɶoxڽ dOڔ^O$+)9r,F^l]-!Bfa`0wJNniX<8A t2SFغ[CFym> b`,B7rȍi>~[?GO<6NNN4҈FeK%ݴa#Rm.U@]ԕx&O*x~n(Q{ }YupacHBJ4l j)|2 B@C9z%BqԈwn KҞJX[PukͧUu(7KXc}oczC\vvK!9dQdω}U}yI芼/GDM3Rj-3X֧Z՞GQzN}+D8&Σs^q+#LmE~u/{˽p>Uq,~'nwv֕p)RR["mJƔ)GNSaNS#/<~~? MǺ&%gnrv}fn,Yg**Wc(Ã\U&#U5ep-#iYpiq.sw4V+r ^m]ԫΘX4uảlB%mV$V} a@]Vz`;h8V Dnm=ѽ_, NAa# 4D ǶU ^ɺ܊ Pٲ x&J5]il&go]t9C9'hEFHn'aKo"d9yzEHFad:xpfN3g @J8 P !OBi.#k+/e'ߍkjdF^(a*AaO\ @2NL,o( )UW1>"콾rea.!W|ӆ EP泥+Q[!O%s$(G|>H#*0BrË y35l&$`ϛ,rO;F-}kx ɕ=jn"# EUVJ(ާLM&rH)3S= tKQ0JW\džj=`zw!Ι;J ;GT[鎸ӫw՝lB,XԜ9r_4]P<8x^hHs:NX>fy"w! !4U: Ųҝw)[7RoȂ6꡹ zۊ~.֬$3*.n3?OVdYɌzfE%d źͺx[ Se@sCdb62^ sA)f/D#l8?}b4!%rӎ[3㛏Kٕ(tѭ{i^[['_8_~vq Yf΍D @cΎX05+XHk-EX5o*a7h֎"}iVOmi<|Rdnjp:~%wD^91 ;=6(L08 H3i!3e"8kΐf>zM^H8!PƸlZ&Rn+APRRJCCܨQΐ+9)bq)ءTb}hPVjDJ+s9O׵wf΃ey^r``%:ZѕaikԩJWAu7=*o;$Z#=.  8=%pMCԴ͵)iGʜʱou]'Hҵ<=M'j. 5ξږr! q8ijTE?(e<)uA*27+1F,;T>pL %A6&RL$0~_aK.~\C4^b ΰJ^4mf*{&;Wt2O|R~s_8c+cz.Y?C쒈' 4oeUa~,^S ͝_پ`1cY(4:cGDc{۟t!%>}g{-{tF֍ێoH|C7ķ5crbMFf5hHf'#SR['$"+Xr<@88'< pqI3/e6@_ɒ^tp?zW}9`.z-z}mb?{OF=rzQoL(x 4vpa5$fß鬳:֞ r vb"\fi-f=ȓBq\\V oc٥lǦ#W{5[?a)Y}ZEO0s)7NE!YA|wnonIqY sс>C4oo{_w ծ|$or 5if%MxGą-&6o(w8+Q~)1P:ܿ.Q7!|2\@11)£6;DDBWN vEx)(l;y?% ՜n$f(u;-sbFײ",-Vk|CU#$q__IgIZ W=\EE$X2.+\[s-O^ &cd."_ 2ӹW&hO$Op3eP0x6O k1S +MXl4`MŠ\H@S"~A,at5*%VfRAh9vfijD룝!8K]j&YpbJ?6e 7bdtçSqQOn^Р팲bI]nof&4kGd;(ڀ½G9sTuy.Z`cC\Rl7 F`˴CUGlNsrg- fĊKwu[ *U"2܍7CaXYf0HBÃ(g Ch.i3wxa -Q)_9W9` WGL@!v1S6k4;s`Pv&9Y=p X|#n?tʛ).ߋ9*zԂhC ڿAxvP(8W?5Vs #V.xdl`!};q )H&(W dLjZM~@A8Lw6Mq䯦s=W@_U%oU#IWjE2ߔP'Hqi$VęJ.@2Ou,vҍa#<3ȳ7K΀5 r-liС WUbZ#p`٢XD#>pd;OEZIiNkJo cQpjYe<|/}o[%TVGMkH)aW;*7k+ jih$<֚r t; hG fn0u# dN7 J0xE 8Y[1(s\ #Gպsg[KNȅcms\%oY LMbM1@XP/Evٸu}dIJoty/pȨ;.ٜK@Χ6JfM]0̥&1` K @qEi2q*`6<1͔N+ 2%<98  F)Rq< PӤR[!f46 ] ..sZYŰPhwjb/Z2ouJM*BlVQYM\ܠ剰qUF`X- 1wAњ [S%z}٥:q)e%_y+j R֫AWf|;-2P: C:Lʹ05鼍S)56R^ؠij _M:4ĬFnqk*/k$~<. Bv*4|Y "4F}bEzb]KءRB@8`x2HP1|<9~@9ðMr'g!^kMt*;buR՜"MPi~HnQF~HdeQpI,{GX7+ #r5K1}W>7P$hXqN낙&2h8(GcBN SU%轩p0 kk 20в &Nߋj8f\RX,d#ƴbZ\E]!qFƣlzmG%g<@.¯A\M 84>u@RB/ΤoP֊I* w[(A0r`MWcR76~Ni@T(d~ bMӎЗ&:;6h#rGHt1@d¸I < ɤ NAa j}@^T!aJ1rd>vDS(n&83(U QHBj!MO X;S碧>w F) V?/^=>y|w(go ݁üG}%0н:GP/}I෢ ;:@ t! t )rWdk+?hU%-FK>*!{LGh@ڕ4h۾ͷ2-m"̈́:;& _rF.GRB[,C5P@tx@wxEٸ1~OFbO~xIp!ޚV fCYcVd$ ,G2jdTs>x#Np~U]<]+#7e`)h?[{ ,6%c9ĺ@/'R&7a. 847ⱺu5kC%۸tT5MYm @f\ʵ,KN3Vk[#G3v-+27i!BkQe.P*{fˌsj1@ܘSkNnm'TZ4jѥ-pm}Mnn$$i^rD u{+&; Tڡ^xm-_My;-7=/S6nfhL[ J?pv(1L X1Bύ FWI6Ў!|D>et?~~_uwpǖ-k|9 zw"[,p4K{3d3j-ٽߦ탇߿Zol ?:o@ۇB.^sj*yZm,It D a*HxWeqGVyRh땹d]͂(O%YZY+Lw.~paޑ9>$dv`|f^NB"7xe.˜6)1% 럃=s\S?P t0`JRH+ptE1g?ύjF6Р0Fl8e>@k0b'S Ni.hIDEXC84(%[dX. fpl8J爢'4Y篟>UO2C~lоo"ؕH$Л!,|Gd8S\&%6:K[F[ti&֞@'$ߵd6UVnA-  xĎ mSocp`_Hײ*6T҉Yx局$7g {\q(#g7e`%VA& 4zUF ߡ?OteXJ7l8q<$m+N>IҰS,4 &1Gc/gMñcC鬉NP0 pDK ^Z&kvVBf[?[fL EEtzIqr2M% Q, w;=xwW*-u63##nܘq0ЕFGM?L!yRܖjfŹ6sAF8\օŃ'EѕJ*:-:Oy^ຍ #mq !)'2h9s}DOq#Xֆm>@}d-j!  }NNzr*ŹUKYu 띷/(r|g3HDS g8ʟqT"2Fم A,02`]mGĞ u90h$vTù `\&^M0[@bM$e2sm_!Zn#J6A[@00rm C,{r@Cq[s=P욽%7KG3=:q(!0ږYD#᧣@@] E1aEK_>Nf=c(ugt mC4FItI8NaDN#S9W Fڇm`o?{{}sg׻~Co?Cm.//;*o-~2> eoݝN`vog{g?vw0 N>,E;d>W1XB2\`pQ8I }4Mw? t8BvcRTiQ9av n={)V|bʙ9a<,W_?XfByV+M|l!+-#͹Y&ŽBvI XKhC ۈç ZM9tw|s1ejyxUyjsD*]4m L O x ;/nd4#z1%XgVt,ϥi} zxn8&jIh88nv.0ژDr~>y_%X=ܭ2rfϫ+>uU ?JfAqD0'QΏ! ('02Meպ UDEakjiJ o٪VJ&։ /ӫ,Tt Mu 4$6[1uܩ%GS>g*3&,cR~! m?Y IklZ!H{J{_!ȧ@M&}*w4H+UVe]e[-T낊Z=6le=SPT] YXK&O1͢ϋiV4rn?ES[!q ~@C|AR-/^9]}Q"A%q|OzjђyRa@ h$Z%ª,T4LY̳1dUK[cAX+9=)4Gw1+4(`-`$UPJ$u0ɒmT3woț0ˋN+zdE1moqvwt׊wɜeDŽ@&w-"tÈ {c6z]#}%=bn .x2ZM(l;.X |1X$uzɩ.$@m#3o['xȋ Fas8!S9x7*5^E!n~t$eXfG'lHF0Nq6ɍՉcv=ɵ*5rR.٘1-3?f^iS#0U HQ­'&D}X @"+9D/LA4;2;H8t~RqTЖ%/`JWʚ6;qƵ%+iHP+ K)\kىL .dEOQs6&٥wF !>)9\e-w\*3"Tag)_TwKbmItKCF#J̏C?r$R#TVEȊA}P=PSq %z64{0LCp:D vK\Q\p7On,.# 'k(ѮH[B8@Q@.D]D~Ðm][:Ȋ+-Ea9^ x.ؖ/nZ^_??KQ!1ED݇0IVs$}fn6d l1Pc~ESǣ`MBnR!a!5"՜Cm*BE1@f¦C[ir!=}0 + ՗rxy8^nPЭ{ py}Q ׿q2+L)4S(Q;a¹ρ5" TA5%|uwKp- Jڼj= 1#oWi'$pƤU)22Tdͷ$U/:ch+R\o.D G^y=xPQ{t!HO)OYíXο]*:z[8oJғObLc p.LKA]fJZFi#L1f1Ich|)Z)[0 A#C"S'[E`"=XS4v'ۡ!8ZKP =I[nv|gQ8k@8:M ŕWecj$T> u|.`b!lbENZ ɐJc7^dA"a~ '>JWtfK>Blw{mU(N9i`5Ġx6*Ymʨ UVr0Ş=\ IiD. M(R#8\$ΘJƗ# Dgp{8YKs@uNuO۴g}b;39G ? xW%o00fuÂ93Mg;I)=eH«VLжUQLzWLOF5M(Ak+a|@ \3lŶj=N|{۰͋`j) H.o(G!-|B c C>4nOܸ\j8^1l_ct(D}_=Pٷx@=xӥEVdqR_lx6h_P6Zyaŷzd#8i!þf6Dfz~(dd0YG]n1fӇvHr8BC[yF^(fjFӶe$U̿[XOZa8'~z6x@gpv|Zn~`Ewy[dr:v]$݅bn_ihtr/(&Yl+4dY; M!ED={Cݜ҈[1`.EHV5i*cQPekN,WbH&]Qt ,Ĩ\-=3>♤ lsP2?U}ny#ajAףimKwTcKNIgL7g*Y"7NĈ!dqaTUՓetdž vx&ƷAnA Bc$p@8Tj߃ԾmxD!Vu @/?ץ2/^s#l3e[ '$5gxI};Ax.4*ssy So0X\[ 8CQx/mExC -F!k(I4Ze˄3}GzS-f ȬQaڭlf ]%6;:S>ЮM{e߬Eޛ]%\P8gfbM1 .CB5EBnZ?ކHIze M0<GD!~D4{aaWi` .ـ"ٰ] 2x ͷ[$8did^sƇfvBZ7b\j?ڹ[¶~mm3=˂TRqy1c|uaBWLPnѧi4+h׋tr Fmvz V/G`(6HEC[<Ƭ3n:HV)bt/,n] ?Y50J~#LrTL.fĜYª~R7jvhhd,1}Mؗ,t}Xơ$72|i9̒}zASh g54v]E6{ 0ݣ{Eƫc#ؙ>B I(2we 1+S؀f&1&~coT B?¾3J;Kmaaa@c+ߖ}%y|o2Сgtsau3RByfdj*ۗZQ{81*O|f{~ghiɳ犴(^!-Z'Xjb:L7W̭]]WghGԜ.?AHtDj<)ah8L !Ҟ O2waS3cDBw'XVy79 1q}}ӜMA #CK>@6?WQzj<=yt@6x^< q}4%~v0cQR:r̲6hk?E]deO&zM) c9gbBCYf--#:([$lR rdhLߋţŔf񑩔(π`[`ZcwROxUۍ v>_!@gHJp dMሑQW{N~/5VubmU3lj2#Ż/\.$G^S,.y 6N#@?:J뫨r ABtB_?:jZRc+8 )K+ 7w=Ur^u/z 7Uga>vIHͭ,',(4)Ywm(a9Y9GxSq \az"cة&p w*rʃ yp>n uu;{dE"5Zݕ@#D[u8ƕ+>nQ6 [5dNWJ&Iu'Cam̛eǪUпeD \e,pS2AoPWh4 9)+/5&dݷ” TںYyTu =);<~/^˃4 *oz.wgʼ~M !Gz'=-1k\P{\gkt׮+ϲR 1)sXwZ2ɇe e~4D%ڭPn$t͎G"8mFGm8!Y4^+fᥲpZ]iENZׂ& >q:ocEٺ%%r'@ DʓF \" *"jE9կ;Gp,5_?$UT/bW!gm!L#wje6&ٌe9pxMЭ.CxdXSͰhVtd\Ύ!ۋ(_HI,މa[ךR Q%d 1PV]㑵 #ir_ u, х"59P󫫙ь/\b+Q, i̖7Gݟbs)^ֈ=#'T}㧃gO?y/N1FDrfm0d)0fRA{gv |@u:**Ij?Qi%]ygBYs[`RxlN#*6r1*U"^$G⏙;v{Y 7nXVrҹx akV㧯ONnoVÏ"'SRt- tWЇh r`w{6T8.,?FSQQJ8~=iS`gɒe(&;M{0h߇V`Hq$v||+ Y<=O<ڜ:Ż>V8MYLD 1G]TD5͕}+\); OY.8P Zi)ONyi!Ni8{ Qiqx .^dZV'jM+t ֓,BԪ5g^JFDX552@lN@Qȉ}(vTћwS˹b$+KPo(߮"į"MzѾfFт жcp愌γ%e)}Cy680RF@h}b)7%KZQȨ]{nSteq[gV@Qc^Dの#0EYo̢qJнv ~[8P?oߨJoV5m0ec_,+])wu`Vր~ /ׂө5xh_ z:Z[yie]wV2jGb%c;4y Vrj@"Il>չ_`* }oT0Mgt<7z6q>BѤM x{Tbs2,R)zØ<ǧ[T(RH-LVxḩ.GGx~ad,ԅAUga2r8*D˂C^sVR Xk w// 2Ifv万@)dmRlHO1Ƞ~4Oȴ!iMM|pX0KCY0UNޣ)\nlH DV @ŵ4 .Uu{nsR_'veM'(11?љRـLޓ e`+CaQKy$W\xR[5k<28e/b=s΢cEaM(8]Ǻfȡ7V{yP9CWZ[.pWmĠE$o ʁӌfZҙ?V+os ø/a/`=P 'gwjvj|8Y2֎,09/‚Qǡr7R;R#b9.+xO ?¡ehh.ƻ_k29b=33LW-ZuiZnrNؕÙ3f2)c&+vv;:\1rғ*R-Ҽ.öLTOj,xi#̑(iAͺ[YqJn\0ĉm2Lv/$B{"MH7=4eD&BG*p BYD9m:$,i2GTX`5 u+[䬊l`k6y&-_Nd~ƀ@PJd_n;{70'ЁuaPcP|}ڶ*,oٝ-I n-Σ_[3 jd`|(h*FUheŞO K Jl]YҜ#.2[pxBm/C#'=q54 FlL&o3p<_pK237U l\CbT,Ƹ53nrGWbއB!V0WǧW=ں&eVa^te[R N:=!phV|oyM8(_> '*OkȔf%&FdX2 yސVoA`Tt`dFOċs]o蟫8L ttw$ZPپ K;2{'B'ΩS+We|yQw tH 15Jf h Wd5'W8,,L9vnkoMFQ-h[a_Y/WUn8O@P * 1WJQ ˅(P- i-`Eޛa[\=6|6}Ǜ\X-`p65&f?jJB{̧%+!9cr&c4-K5(Bt4ž[:6h ^X,KIi8^Ё˸VQ?bL8spTc;,vXKJhQBdJey( 1GY`HgGBX# x׼:3 j_ L%;t1lx ZAވ-%-:6wpV-D,i77hc$ʝpeWm iS:(}䫾#~ָQ+`( Me9 XX )X:)MV,:4V[%ᰂXO^]7 ѧ'u"`= zi2H%ߎ~QBdm&e8Y6;PEFrBU!f!M l 8r Zhd@Y'FPcT${- l[\w&mEv2619A2 HVTӅNgUJ-l&7>0Y%K?d˫0#U*R}opN$cC SvAiP>63@ު4 &)k/T8FdF :B.w[xidAؠ<0v݃^OR$;R;{nn!Ksn1:b<{9@gN*|Λzl\kI]cOώ^bo/^C;Bߞ#& TXC\EKq+s Xz6+,89oa QY,[ 0"Z600ܕm$#/(?&f]^ Q"͡ɞK҃ط'qO!<,fQz<Ҍt] "8Bl]fy-W,|u+ P9iE*ņ*P/I=e|tUPt, @^a.ܱL$ P[N ԧs0}l s8b[:TPքyspk{ M(HFDQ >1AG[y2mm2\(cz)K>h5k}•y5VUD ^zmj <ŤU"\wKTq5fsU<[z9s\ޥb'|ta tm2W-bj ioA&žZz(`K{+䍏Jwؐ^*Zn(gĒאEF>̉>A}̗%tF74 u R<@; +Ó2, 6`oKi󳪚r5ޫJ-kn4[Ih n yHD; R U%1 Pe<| ^~S [:G6 TҪ6|Kz{LWˠڶmK7.DiDLw2d`FO'-(ZȷkOۮ򘉮rkA7AAĆCCgecʥU83Cd5E!jѿ]<;g9gi(!fӵd9\TG!~ !m MwFsٿ@2%y!cT\8隊\ Y:_x "f!fY^R4A֪4KM+e1b)K t[Tm~}/yLu&@t'g:q i4SQ00%o(ƥAɧKM#nI]$c?E䆫F߱{ieHi57O.^7QrNgHp8ڕp݈om27 ׂPck"sb_[ -3HԔh^Mn9: mI MʟF-r2~cW@$oU2v˗鈖qA-EA,J,82_$rJJ:GA HP_%nW[̜Գ!oI8 ZB4VQFBLVK,>(>הM@\zyi5<&Tyel;r,v}A"[:u\bポmtMtg׀Y3#x]~[eo {to7}y+^5Qw;3)* {RKٯEٸ4| ,0|oVq; @r''6֖: Rhxh1Yk,X MBX LEkSleY(&C*Yi߳|_-3sXRHE~LOb?T׶l&cNS!loJ:M6@vNlT7?xB}uD3Xx`J{KRTqc檒&g_¶ j'ՕhVzߡ(h,lMƸAWLt7H(L'eҧCt;g()/Á>5A^o"ft{* W'"A*P3WVIBO:Xx0[Cuj9ʬ֖Lb_c$SGEʍH{M޺4LbDR *0'F @ZPUo UT܁(x\!z߼v9eIVVܨ{RA7xl VJep}|ք GI` TA*6bZv]9WJcx*]/Zxcf-^- ر'VTc!*k+JHjQe..y!8#=M[lr!-ĝ  "8Ǒ9]Xl[ސ ʭpQzV`Mİ,X;CT⽁(_Il.PRLHTwvFH$[ Q%S~=%뀭r|2b4FX,[T48c/U&b_uC_qt\Fhր[== |/brȳPY(qir tK\ȁ95S_)U: r}/d| '6lx^(GDɺ\TUB^shT4S &vKݻBa1hn|b򳗼'n׺?E,g! f A v I0p$[ʞ*{ EQZj(h&0>K,[O֩`>ǧ ٳ6i)F|_LK\Fd[w/UUId}ݣp ^('Bz4z_= Y/3mo5>lld<N ..l^ҧZvmNB>dF*X9x0 0q&W;OJasV ~"C>QHkTN@@fy~0qӭ?#%9Z9e=uGϽuB,0<F1@޸CY? f$|Ҡ&c*so`3Xe@eGh0nVo{uJ)@$7/'w۟!K6fN[r:!*m>X]0Jɣ{?p8!Gq@KoE2+xy>S 'ozCMZL@'p!|Q{{詹9 D}qӷeP^VeՋ\IRr)PBǠFkFU1:s8--Z.#K$L*=u|& VHݜE泅͢ +F&L0KzʵdU LI Ԩ ABX$ ;o~!*u t@C%5QҪ Rlpi41o'@+Qlm>Q.` `(-! C.~ATnKTȝcsZSuwcEU[tI\?vRW\5JϳV.\l"N243X xj jI!~’aޯBXy2%YUb) ЈT$tv̯;Xthm[FM-Yb^bHK0mJGU6<#.Wp(ʋ"f`"?ŰbSs"r7%7&O~"GyVWn)#uk3g)Iq~JDq[hIa|U–"ۖ`Yқy+2S6dFĤB_=Yq0 7Ua;*9@SƿR,(ٹ1w Ft^r8ަ&y:1 BkXB(gٹ !t]u?}0)ԪCB&%sKENᖴ"3"#BMS!ǍCQYtmp0ÿ濻;<3]Th0)e?w̿2MM>v?7hs39 rNBK؀" w7 \2B TǬx+M9i?/bC/: j4g+4VR`r zE*6L#0ø2섎$:M{)0͵~T|IJAY/P6^-Q8upTܜ0, 5j3P\ #ʜ s4Q{^ٛSTȄ_Qe'݄\Jj>Y1Bz9 ! ! m$ڂdNA-K?d‰ߘl}z lS!7١hܙQUV/#8fmBm4~~t~P0҆ ׂTہZgon=Ggy|'Fg{Gۏ^pLr{9i0oP{ ٫N*Ws*Ax9n<{X@޽{TvaǀL™suI۷G`8,hA[fQ8>2|z#aMT Ks̒A5}E?ax pVϣhLbN99v5f(|rbV޴<ئ̚l\ Wqq7px/?lFNS|'Yh3jmm l0HI~paF1,"FlHyаCjnLGI=w<]E];^poZW7Ew4= K>|D$'[<7@N>ypSrstTGfu|-|g]E_S}&E jOUdXYM]0ܥdZ>J(\XɠMSex@f2ۖ e١_)翔tb/jdW4X+, v-Qyda:Ő)  ̧X*TЗo ^gBba A 8fLy#IGA< Cҙf'׃%!z35-9مӃ)͢eWCTA,V!FJaAF:6r]d~? b0>,W!DdPRAi+.810 ef6~N jP–QvЙx\V MP8,ٓ'2<-E'Ǐ,m@pHC 7.t̿bij$Xn8i]SĢĥfl| ~񴖍Cx Gx&hFtq<20+ 7Y-WM hC7E,bEʺupG M7M&̿va6< 9A?# )nq1Ƶ~lllZXzCqse afʓ3ߣ6o{4M2dv4{o2A_GF"j܈Re2; A@bG= z~43iݎ T"*1ccEXa 1_&|*ÖgL)K9q5,J7Nzz ּ .\_m^~" yAJ{hfx>%P2E#!ar8= :bGLkh~AʲSl\4eEE<#!Fv :چ|+'.ˠPtЛϠi7I M&݀0] Mu/ <߹Cbj}=!: ]zY!em7޽^'=%cF|aW 5%Qَeɀz0刢+cW11RcoLp9RNݝ6=,= } "v.y87T9C,waB4. #դ&:HpCL0{GOc}>ObFu`D}&g# LW9L&{% T.‘<>{@Ni3X OJ^Yj\U-yj)B=%(LB )ڱ,KNC'V4FSBuPژCkEmIOp'iu<\!%HGM/p]٥eΒ4*!RFH4#\Yxk c݋ s-)DݻAQ#ԪNWQF4Oui%a Kª{y\qÇCo ;KE*\6 GCi'E514|ggxOw=͎i2 a Jxf ^`N *n{O0ZAC,ȽwiH=LH=%|3|C}&07Ƚ!rƌkP/{{QT8'559=+B`5= 1}֪}XfIwdwQhQ)娉]wհ ^7&K'' ;?Mn)-Lw@DKÖmu*X;&?)qm0AP(`6_R.5MgY@4fh{25 ;zZ.gϫMqe 3;[S eͼv"]yj;Ud N my*/:bǽ}$YV{ƋEy<=bNC0\M~psy}/I>,zl3bgWlVJY f.8Ic sɢG2"Ѕ)wՈrk'D KMWt\ZqF`{ۘ]- ?k 6l"|&S-:wݨѳ9q#EtXP50dxRs QQ7G8V00h~n<2[ZukH`k RTjVFnnǧSSqO5 ?ϼ빳3OmYc L}~+W\ rb˅1z+/Ox.I6z_]d.A>|_a?w;] M@s,HI0d!sԪO"FDT*+=[̍q⒢إ0)J_]tɳۻ/pUͤxӤ+bZgN\+8aDj0s Z_Zñ >s."`{%ESw=b=BI-H^s|)Pʙ}2/w ZC_"7an᜿VͯJt=zQ ڹ$Co.bw(!*BlE Wܰ{H+в$-[謣Hף%._9~vm>rh'FFܩUE|k)}Li`ǔUf(3KA/,J+tNV?#Z`xh%o^+mf8rٻZ/#q2줗\HXC'7i!.a fE501uT[Kmlu9nmIl5 ~o6)m # KͶ|71op@VӺ/l~8jY^gTBmnAncǒmdC ijZnٽHeg(Mhu9K>[aC-*/Ym.9YMB{3i!ILiFyŋ ׄ1}̓w9=Ywot5B{4[-lUB'˩ Ai2A6iޟ0]fKL*lu)4 !v`qx6䦓G!BD6<\\$ɒT(<`ua [=V#\)&A/x.Sهysoc INe].d~PKu. F4E XY9zj[8 Ȑ aC u.fCvÙ$E qo5vG;Zˠ)`IKf9s38!)@:ƢG 5oa0/a4EgI_Z$CYu 8"a`kprVP;5 ؟[kJtg3om>far`4'"h- .vʿx6-Rn @2?L$Jcez숏~Pv7nOYC$m2{m3vFV7reeXid(Ζ/߲Y ?p}~A/w3]8&apoi=GTe|N¿.Ϲ 2t?4D)ٷaWO^<5[OM7\z> ܥz@-cFJeʙ v:;Ak2d y@4nʠl;vw~$~WH<}Rgo`wȟinw{ ý<4(NAhpAi}p/6!+luc/9d r'6_?}j/x]=$LH*mjаWo X=g㐪yYa>b2e71t;N*T拥=[8ˢ%}+!nv|0h:M.)t0ojXyO23 H  OPVS"gH}Z pd$GkjG`QYS(yvI^_':Cw'vU6D:̣l)@Eۦ }[[[m#R[bH%sS[]rzI] Zӓ'W0^;^< :cӏ'N0-&6mǼ!-[e:#Ge?.>'ϟ s.-JMsh~g]ui 3ҰQd"|iwy|3do~XhFυ[l`':@ BQm{M MpVߚ\W q4pՂbAS(Eڃq//9e^Lc$ُPtau8MզKqL͜¶ 4[cny֒{O4YîMZ1i P1 b}0+*OϞ/vN48CBk|0߷吣$ 4 lBA 3  gۄou},&x4iL طoNLx Cyq|/7MkRV3jL6"@&b>(]5yӷ҃5nPi,YAyW%wP\߄2Qσ~`Lh6F:'o2niC4Cٶ`Ʈm0D";g'浹Ā0@8`>Fe2Ijt]`r5D:zMLDx`dyJѿbHn2Da~pnm\[93aW:EpGf;KeȦYdfc>b3rH10U]CdYp^Z =^ pBڐfp?̸co|jip>&pڵ֠dɇ8-"̢'˄+6jjETd3 Zh qb:DyTQb<ڃ4`91]U(ǀb=\O&2[`Ӗ㧧'hdfhف&<ǝ#Lm8kkSTeH &PqWSZ%64-c/)Zz[܃`:(k0yp vpXYi?Ԑ{=8 D.Akp[T̆ũ9 e|,)ߗ  ӄU×jAi}Jo^Q'yaWrլ4@ - h@cgBlX)DgN2 *1$j!D7>p]$"< Ma>GbV&<6t<#p1s5bm 3{ݕ_?x7]dxT E_E`[D4SgЅfԈb< ~92ŚRwԡ|V.͈ Bzyras\(l)%I;.~ʾyQm؂`dzGψU #8 c oGN86R,o,}ƍ=BE+6|8ؙ2(n܁T!/%RzXk~荚8PCaSmFuM不V+}ރQesL 8]Ka\-J!չ%NliS f+Ą. Kt@' ]uK|1 ^{aHO޼-B=C2xrY"ֲ{y(vi5(//xubywO>9ϓ :c*%Z#pGdHM5<,c X(ۈ-,R߸Pqz=˒Q뭺`gRyC) SOCtǖax#[A%dw8@O 9o+4Lu%/Ǘd`YΑKe"2)b'AYCP, kGWHdmZB^{Eyt:f[1{0E-- xS-0ra]PS]O~$,lj/amHAƁ ވ"ar.+Jڣ WA"/h_Q %4ZVm&lU- |CŦFTf{C|3iq./0]/v!)lҕGWDasLU{a  &_=61/nV+3nJ˽hhKPv0ic."|V6s4{,0$ =u]Sp|䮟[S2z73~6g7M M9 pLǮieW:j@ЁrF -lmyŰXJn xNC=*E8ƒ} f:/a qV4C7d{Da-}'7d6ܼEF#cf`Kmn9C)(4٠?O Ma"1 j3>~!$FZ_U.a/ڝu U@ZVsҠ- 'Kfȿ"L)t8LZ%mO-}P} Z^EqCz>UXP9 JLW# ٶik`,i}J,P?e> ^%}o?yjW'f?yuū,ii#@ŖCc axIs !(qZU$RӨ5 4 ߁ICL˜}C M?K Q IB+Ҁ$ɻ=9=;:yщu4\ܓ`ػ+43Y29?7C]'ؐb,W"}b}3Xy rE&6KP K#0Ut7b2B<@z DOk7.vy*<+e;dQ׭h'iZ~^ڪdMjWT®vbz.Kn).UZ°#%~޹!|uz @Uf,1aK .!_J ̐U҆"NHtFJb+U@f0\o, PAKk2tS]-ХiB[BxredEϘp(/ukq4gzGmԜgՙBg١Z҈)s[BBӟ%4h)*P]) PnWQ,i3ih Q yez*X zH#*P ] '*63V6:L3/ +1 LjAA,!V]a%z#D $u~%tXwfWMJ 4ҍ)SL8zuh݆BȏHe5HK7&B!jPf] ܁t9$]BumȊ_2LK̃SqʋFwuM>X= Fr2`-Xa\ٸqUg > T:_/%29 ]9|k=!d@e_;5V6yh ܍Cv]= iȳP<P9ʂ=rn=լrVzV;xPzbc9#r:p&dzgKХ5OA" ^0nY4m&m[tȦX,0DM&Zp{=c g?,p)ͭROFŠ5OP1xg Bu`9092#  $E| |48cV4tGW` <3.闗]lJ]w[/}̤iȢ/%h[2%(OۉAP]t֧@ax]EHeh Iи%԰!ۡ0&,Adi{8%jo&Lޏލd95aY!x҉j $sؗsR=t@r{ :Qeb-Vr(u3@fVb(j YNo` 'Fr 1<0,"*{0{$- t$(Nr,(ڌ|  к~^j6HC3CbK/[qHخ%:is]kwT[0C_x(Yy0~몾k6QځnEXshs`5wq ƙĄ*wBWX +}̭9F{rg wY=/lT33g!) 6 q}[.M\hqX AwPz oi%$%{~{XǰS٢VS[Y1 |Cq3'K7fRqRH Nj`h.:ږQŊx%ԅ_:]ߝkG}tk? Ksn!VNAȢmCzeJΧ:@[#%wXnd-vFkDrۖ'1З43NCEA[" J1_kr͖l-1/ 5m[SE%Q?iSRq{ cYm퐫pMnX(@oAkĆE`8I*y`W%/@^/S{C.[Ϳ.h= b.6fH=؆RG[*# yȍWW#{arn8\^,s2Avt]ƅ7BrZrmx8/q ]GocS|.[/{:*U/ P-$…aW +pZpo/o%Uճ]c,?jg ,L)1YB@}p!y~\.ʺ>9]i=[ G%fpmݵˣk Bas_sJ4oOK*ijzOKBr߁\0&* vVj붰4ym}L4ҔUa\2f(e${ݵ R~ϒbT)xtR^|![:+ͩ-*u[.JU^&%: Ia+9v\yc4S9eQWo] 9oEnM٭Լ֚ȹJͼ }^\jEZ4X*7ouKДvG6œDqo$$4YqDH9#<,wCFB%o%KIX*pv}DۻLw4[ e J\AdNgҴp Q B,n*hdUW#T Jܽ)AihrND^->ȸeC|z5 Wd`iTQN[V;Eݍ9i..v;×%{2 NxxgO<: A⫮U|TT+Cr5ZByqկVѸKb%WJxU`%ɗ ZjADz9& #qZ)疑Y/%]Ǎ18UUўju"%e%M4ѩ.̳l00(ciJcm k&^\ 6s|d eL$@ūGK 2L\DnsTnhq<8 Ȍeޫuen09]ݼjx r%=yz茼tu߿z "8Qٽ'BG xU߱##0uT: ^0-NF>Ji8N3 :kXܔ2_W鴞B4$hkIFcbE>>2[meQ gkz$CTUt1|mYlZoGAnУgoVXPR c׹ldB"H#O#aRdjǒqq}Øj[cHHC62$EfC|Fc#n\?KGIX'hŻW$08XUo00%^xKӰaH0>xi#HgaloB\Gۅ tshTVrV(jNmbcD/iǎM"~Sc]y/Ŧ&Aa'vhyn Tb5Q-Ggqe"q#1WmU( Ă(؀n\,*49M% `FRr`n:w\[F@{732wI#ds2dK]艢̓)DL|Re^C oi,LQe0C3Ave/$6}o%9WB8rpW\wk v9նE|!Ndc}±*>"2Bg֮JIuLA ?]yDh~iI,k>p.sim>tף65 QLK2B]Wjor]It}}b 0Zk^d3t|oJR" yLfOΗx8&IhC]pٗI:Fc _ L#qtD#[ь#YY桙zMh#alCP#ߺ40.d䙬-w[ܖP%H<z5X@Oan{Ԓ>GGVwĦos]XH_;6%t_ݢ>`IxN7Ɣ*_=ň+ (.UЀ 8re\!Xp%2?h|f{@2o+B]XCx4X;?RrڈsTw+}zިn:+_Qۭ@6?8_ MB5RqI1`690HIJ.ƎoI"#ʝ`aRbx5չუ~ WLA4Yc' x-K@y42:p~5 *(|#]'4"& {|+wv?1dZ1>mcpoM"m $K 4N,[(nDS_ϲd{yIqo\{{ G@pa%[q+E{pjZO '>hVqǧl};}}Ht * bYz~KTPS^2'}KB bCT{W91vw:(2HHJW9kB+}lskVxDp %P0 d6BE- =b&υ@aZrސ 88eOxܢA-p6W&EmٞK4ÚDZ.VPoj0pKW?-xnH~U^Q|=0&Kb*D FiWc`O"@M8y<[x֮`n OYЌ">,pcS!3 ͬtĤ-Tox"(,,X(cCəs:=E}x-R!5Z*%Z33gΘ}1vbS v6A7K3JPs6;IkEFKV2{Egd(G[nE)8,3% Y%Pԝhy <iL^@}WqlB1VTeOY[&*~x`PǏO/_#P0฀ SB6E:i77TOX9JKS3#̐ 0 k*0Ę.LGB˰Vv$Y|Y968K֎CK|o %h,г(u&,2> i2 =-`;*>*ݒrg[64|sd@؉9A.U.Sy1ߢ<(hUL?)%+ː#<RpɰjjϛnYT_LT6wa6'8Y/8 ?׾*&Ŷ񪋚MLq-mA,*;EvY<"1{7Pb1K1z"LOx:JO0̏΀RHh~3;5$c@S&;FH>Q`90#nePx&a e j@HAiBxMFC\ha8|4qZA0!1ٳ=6Qa5J:{Lu sxBmaL*;C* |fCόѫ4%h-;>;XqCV񱏫+E6by]tjIKF|o[5a)ߍДza\xۯSP5dvxu0cHZ_ 6h#Yz[H`~!pCZێxeLŠyvbnϳC4Of.@0'V_$g`ۯ@WEhFˁ xUs1@8 vn;kiw=hge>\,גm禆>AYͲx1߃-]6IBU?MQva4'!|>dʄQ0e{"}n1ѕ'zяuU.oyo8]MALa{UuRDK} eIny RҁnsAem0g1=:mdf"oƷPY2TȌĔͿwfqaopzs=?n~'wt荻*(͜ơnG~KCkYsZ9x=e#z҅T]rMѧq++?x1s@1O4{ywO)ȴcb  Rv{Pޔe Klhc|mxFԹ@Y <ȺE'Me<̍'&(ܣbLk=nrkӅ Af! 1Y CT:-k`4JRGD\f^ݘiXcw@7*cKU ź3';0 >ܳwT|ҘHapٺx G`nFi/ ӡhs]-sV挶$U҃MI L# Ƒ~GhYW&mO }U_S18&jǜUDv 'Xv KОdf i vkxF:kg`5ķ) ԩ R Ѭ\0}8#^^Ah\c78X1hMg|NlGdj*Ip曋}oq0,lΏls> p{{qΣ6 a[.TBoB𶩞-VB]n"kR_慙"ЊĄk#j귿r`G:ހJ뉩W2OM? TJ:>I 9M-QVMUYT)MoczuiBƄ_:- ߠ,cy:EdC|GcM n`CgZ xc3^8lHэɿC}gEʰ#qJf;$gPFbKzw`Ԏ#wd 0>mQg]AA pԧe bl:5oKZ.QGHXvvߡ섂xB%h!X>V&yƈ1t!D)s2 G9q+687 #m|u \{ldAL `Ȭѷ|`s@-Odž1n椳vc{d-6Eb (5{Պ GрT (W# <9`1;cR@j@(a;l xE G b6(di1Hr(Ҏ;Zx@5F`1Ó=󦦩Ҫ'Yu|Hlw[ d rɾUfElD<~Hrv6k(^: @[q#yqHa}Ҏ1,D=z،J?OR. 'ҽ)e4 xv1~q4=tid)m7x6 ̐1 ')uVG|Hy.]Eu0[\Ish#Kڨ8ZeƒDK f岴hE ]aj^ rT}K*Dpm5u 8L ~uEmWߝx\cb0`g ctxu߯:yvlpՋW*1+"gg#aRb@BZ&O9YHYWAvz{{>,Mcp!>'g8oʔICeH~ {@C$f F):L+sSlZ_+:yZk=&ЧBݦ8xԯ3 8$#Xe"`#|/|YŅN7ײꎖ 16kY$gG 3;Zٴ=>1Q?={eDئ~DA\p4Չť3,胡sfuMGʾթ7N VL<̚{T#mSl:KC?V./oE Y8f Eeuq*}/~ Nώ^6 fcPBuVMHӞdŇiXh8 iK3#)/p䏼 8ΑjN Ә3ftDbyGlFV%cWpɨ΀/8"yɣ3:6[fbH(_̓'5Cg|]3tifAݺfIm"!i7JuȖ.: h}PNMAB#MEayPHtgf)+]ǕŠC햣ZO\A<ەJxlIkjа㺦Q&ecCS ∔W㑟ܚF0dGձ<B C8΁I)-4c$hnL,plZ14&F8&,嘞 z!2D5'<PdGA$;3lfAsMLfML3}mU` Uk0) b0>C95jG{gI%8s- Ab?T v M!P`)XQ +f,PGX_UT1;ȃjNgQyL:;XQ"ie`!EIMh^zUA[N&wKXn'~e,.ĐEK+#1,Z|eFֽ1@{s$noI?E$cK`&R!% OfH)9(_4b)l̑!t[R%,eCgnk^ˉ[ T.~I d7i(+M%*S/1\u>12XbLÐ.!b(6F )? F.ʶ  *)΄DfN[IB̈vZ>[CO*X?j/78*,uI[o쓷GF4pC7B6 ao#`dס[&ǜRX=+q|Cס:Va|̛fMSo߰}wC]#Ќ.HLW2+',,j+'Ͽtd6Jy_X"yWWϟ< ]RhS\J9#ѿaG,8֊ 8 @ M Gii䲕miNla*Ix a4/ug@讥]l|`p;=x! :cdK獶%]AÆcbCahwUb6,8]t5oVb΋p o#'Kg(%0*gu w1#10FU7И9UDM\X.qZ*(~Ip^-v嫤5/Uצ[u<*YE+ Udqi77a7rKB[ֲ~6XGmoh]6|gBj#N(}5ꫡjHԲ/nmwBalwD֣D47(n$/c2q hVҵn |iM:#$b?(l&q-i:]rѯm|e5UEřj%uDZ@K}QŇjT#;J|؁B#vb"H UyjU;TpGpU67dBl5b!ep`3]\+`Iz\S53cf [Qu]W~ݥK7$ϴHs/G9fq n>z>~'wwwޝ=.//;Uֽ?>۫\}ow}_'@>wwa{۽Aopwo|Xoq`W&Ԑ[,s<0 #tu'`;NjEdp &XN60DypW Mquz;}|5n-[jq &-X$7;vw%?<}w7'No@?/[ڷ l<ݝnvo?;ܹgAp C} &/. T 熳[lA"I .颵f_܆hx sj3#>B] #)Pk!CUf:Hi@L]5xyhA@E-..Σa nFLvMc?\F``q󒝅sS). H6u9;00  0i:'uD;fhhʳ†SXA5Q#'6>viAH.t8"N`u1ɥ>*} E.k,o5%6_H[[{2LO6yx`9Dj9fqy֏RQA xmCI1|Jp%ԜW6FR5 #&T,а' n,Ba8Tp M s.q|s 9x6=]=G`%^ ̣eq.|a1IЇsSK|m`(zrW$w`& m|6C/x,0onup*ˣQCrqچHVm>启8t79A(،o{inImOra9*y 7HWkG xye>eC P,DHx08xyfm6,|.|`@Nm;G&˧Q4Gɔ\7^=#B9'Ѧ1`]k/1ƀ(jf;]f2`!%9p)PMYӵ/B1;/Ƒ:ïO-YWd6ZoC<'&yu#`& 5o-MOYad<Ruԫd蠓-15\L9ǘO/@;Kb(ɟFיwf/CL'.!vYr}<X9^Y*s-b@9(~s KHG2O4 >x 8F[D cNt8A4;z8a\hj><@AÒ#}WizqVg⎱62 cl?hu©8 T2H! o/<6`#_a A)>>1փE o4yPrYi1OѴ`j_//qQU 1Rc =`a[HcNU<Յ)4?输[^Sf Pb?[& 2!op9~TNQ2PlTQhhi~tBÜ1YG q =n-d6"p3m:f'ꖵYnx3f8`" SQMObǚD,3?'\ r.e_/h>;-z;?_x+@} =&x 2%}Kttr` ыZ Y? sP%遉GT*982LvȴF| $?03ZFr ֽpPsi4E)߰ˋI(\VAF;_ 6'P3` D`&_-)@ً"[]#N$f vir octVClAZώ_U,pckV(s~V0Y| X|A17oZp^79FkNr7R\0ȶ؀yk+.K}Q>95ey]C ^ 0CK6j.%2 3V䮥(]r d9E}99'em{0K LA9sw'CJy^4Rfy_/gQ.> wKU?}t@OcOggz;wЃg_ˎL[0{;k, !sh؇h;<וxE_bGq`ʙ|ls` FeHxیѪ8Ee 9VaԐ"`z6:a?i(+2ϗ،^IO9[ k5o-EM]tr3&]qllp_ì6Iۅr^&4n J>ScV!s8l ڣp@aH e W2LMA>)?`dy^bc>F.8Zq^]Qt$'oAe*jTof=񊁲cU#sg%3{5AB|,*d.+9>T&4\&F_ߓ`lݲ0*b  Pp?u`r7O'K)AM%kXĵRPj&(D7doԮiK_=S+Q\fȞ3͒=#x5߭`Zㄷb8[Z\H0[՟2sDȈnTvbST1\)9*I.cJ[%mb0یWA~l  5bD[%CA8b]I=Q*MeM<[T6.B*xMeOpceCG"Sƫ)<H@2YFRz Ҏ<+)pehnIPYPYq/ݮt2tMK•6e.0/1gѿ>`)76Ò-Ѝ3rn瑳,=\r e9p k(pn٣JatB9F?XJdfS`6Z@iímU,O$+gw(B:6忀5wrZT݂G@5d (ՍYLY@d1LRK."LV݉8d]"R|-32J(RE$SQ3@f3)\uP6F;*_=x >KIapN$\ir@,Dġ;086-$e%AJpLƒ譑"20Xq[ N؅ˈW3!"h,,| jF%%vy4cFـ4dAIM8rQIg~' @"Ie `#:H,#iP#q asm 8U&=Q,B#xna\? Z}l.J:W/6ZWCn\(N .Arg#BKZK2EZjD1{)V7M#DƴX+!ā hm=aa; DRz 6$vxg^c@rUd#:Wౡ Vp|{DUL%Nl"2mhHʀ2p\*NhF  vj(?u`]aƨ-|5H[^}jth!gc#i206x*P|Kq.LG#)ւpxx rd6/*\$f*i7WS|^ 2:a~_)_\v m㍔ɏ~8fLyt 2-6ڞg !K7%ݔr8,x,E#vza@z:=/nC}x06<>6i&`i8>>K[Z͗N~Pwa>6d׶v4=~?hcy ^F؄MN.? gHbz};{]|; >>]]jmH FP!#4BDH݄wpf]RH=S=SH] ikBՆ͐;qdCixk! {8q1VF\}1z#وF6VFc,3Ї٘Ș7\hO{45Ȣ=z?Tmhdш""MzUnlCO.Abrpl2v|2.cB> G5 ܏rOFb>#ۣ{C 9*ßy 1wgAD2'+d]~y>{y\]zϟw\k;ß!sOƶ v}z85gdxף׻9v"*s2Dw :#y0r0vi|{c.7a =CVe2v)!=e3$LRa?O}:j2zNl adBdg3у!?':&;h>G 9nDf W"Klyw'Nc:p `~}|wT`bܽ>o'tbOp&M~ȅC*r0 0C*10#DQgc~ .G> 8"B0idw&q="gfږ?%4_%wh}}w<; ;XA4w žФnZ1ucBpS;v-ϝ>{nbޑMcgܣ̋/Dv*vu.Jl)=v,}}GgϞx{ʹ&ݑϨÿD$hi}ߞ4;Xy!1iSY2 wC O$0AxoDĀas;5-ݞ]9ý:ƌX7Vf)bA٨b ,T%Q;cbFil!ypW?{EGW/pDޮ< |2w M̧;{w.3Ι/ doMlŝHxDD|pq$x | A b@›ܓ0ls?{'.|F IDܓOv2{ϨU+Lhs_`T< fp31a|sy_E)KQ|ucSpL:x nhŎU@<Va)g.hnݳ,9OE֭fD`k'K>am"? 6bO{kF5 >1N"VJ"Zȏ Q^X7_ohD%!cLll&;aiKmY%gO罭s.罒SjUu$?3UV:] 8M0dͼ!t\X1ސQٷnQBlʃZ츘?^;QaP*ÎVfқQ4ANDcwA@BC,-:X01G,Ls=g*GXMv#JuY>!_:/& Wx"K4SݻVhiln7_ʌt˾2[}(y>=l\9Þ2rZ߆"f)7 3A\-! @QW nL(Axj[W}]HLJG?w]W)NDJdt]Ӗ}3 5`.5M*utnZڗNtn˪4aj_ѝb |>6[Xj-ޖ^| ɂKx$JM e]1 B'$N[XTWPB|ip:腰2C1=>2xbhM>@LFAQCl*ǀuy:Ђ=_ ꜝ){Mg,֌^7J>P'h{9X;RgK%='b__ӣȊ=I&<ijr !H  (Nsb,@!{lʀ~K&3԰sl",!_aexk.fVYG@~4'GϞ}/מ'+`#òDZ+a*i1]Kaws:u'wwC߹Iö9 (_xj- <)*TuZ.]10  $"#tByfJZ(1f+px`xţr>~u4âkTosHr;wͬ,zl(CP?YzT`Ɠ~̧'3C:Y ~2W,m F>"WδN4iYf'E@X;j5Rۂ@55ne.K?;tDQ]l6#Y3JrR /܏^8AtXYjX`p1N)_()-Hd+E66ֺ#4 ʟ|h:am?1~/![ J)\R &4B?~օ$`20Y^UfyOPۂcfBϙY"Hg? ["Ŕj!4g:RxXY^Sò1{ZOlU%mǜ1JvJ֚ , C-:AAp57aY,oܪ)zIz:̰!ɓA$^eaqvIx#"=Rt;iw1_gKx0o_algq} ӓf \p34bV5nYN9k/f+D/?zI}<A캳M h0+.N/sx jP* ic:Ӗ<>"qaaLEzm$9(m+թ.pTf%%s@R9(+{eS2|^0|x{C8%(TjY?1 ;gU̓ϥ=xze"Xg{dsS[w]M;w{`が¿Ha!#h4)1=$kJ3? yȿl|KY9GXd ElH@X/r>| xSt jn1M_\;~h{ 05s./ˣbsg QZ;og~d<} J /'W Y҄WUMdS눶e<<svy0`ɄO`$ͭff6< V Гʔ٠1윛{# 4cy'iFu헭{}#f9[zg,i;x=Pu(o}E2cJr$RFK!Q%NדXggV \~&(7!_N+G> HoD`@r䖡CVS BM RD;LO|_9i:\S`* ~2[S4ǰ-u%)^Q0FHØZ5 XǀFtȶ(Ʀ:gRǛO5X8$x%$X\ltgSO Ds罖Jl"=+'> K6L.Lw!­u$g6/Y*5yH[>)$Npv 1`mz7xLPO~\g %'e$nu %Mj❾4szd w>3Z.rtN^9IS '*cW61;i; ^u1Պf8y9XOi6+VUZ`]Eلj]`gҵ*߰1x1o`ZKˁZ|л֩}qx~ I !GB`Aڹn(yZ` 5aMfDT)g$?6ZgFf]w9I7|\H?F#6B8tnjol}zc('Se?E_ m˴M={^V7?~~ߦ /Λ ;Q K/G)1Bܧ% MOvfڴf|# TǒDِdp+hڮZlmWww]n+n}{u_݋Zp7ymV:;}z 0pG@OpSP^/rcҚU%j6F@zFFԑh 8@NxxX8ԃ^A皥O<(rهD/io;?6#w~×Ϟ <{ˣׯDIQ7˾wkxGĴc?_ͅ*' TKD;LZtPխΓk`b;ۅZ_F &녹H ӃUty@,t;: V(}EAzҁp8ga9tQAQ*K;%ts#I1^Jk̿"av)5%U nDb O/L~ré9+ux60ȀUtK1/wN]^"1F ԡv-ø]ׯGcJ)R 2:ly 텟ۍL \͕qaLSBp2 "s @nU`"X]˒dq,8vG9n'اߔΎ] &żwh%'74֊ :f^-\ c`h LM+BxW7T'1DK:O߁ZlgdOb-fQ^K\uX,@7KOr®R瓺\L$Sz{.AH"m͠H4Ԍ+ --:DJ$$5Ҭ;1՛[oܕ&7 _ڛ/gl?lm|m90Cыwݠ )q_@r{h2`ޖINi# j뱶&&]_&pt?.%j%$O??x&)^9h_s %#֌!0<v8Q4U'9@·bt&*x@/"kaC@DߓO^q/7ƝLvm$ǒYu W,, Ӱ:ٓc|:L3utp5Mnfc)z{H(M'x?/AsONdq!sDs(&mP*L|w)[LuH*F_BeS"IY2]VyzS|?A8/lji#}s!&КSYk.)E> z6U8,4l%dcs;oѿk|^ϐ!zK_h[GdM;Xn#mT{oICEA7(KÔlVdA4h1#R0hғ4d!L]8?Z;*犷< Х ̇ 3mq0Xz.$7'߶ىA.-|<Ɠ$ R%ZPQnCmt{*߄E"y>=wLMIkW7?m3ԶPŗ_਀ɥoH-b+xvaaG($cbjHhȓ+$ܩ=PLC\\ +U7_[e.zH.)i؋*''fx'<;ъmwO᳠N;юǞlֶx4Lr$RVr :].CP bxFKJe$4OB2?Tn'[L6ANUX+!ܪ: ß/2c gZyqV|ؑdjv|5o+;N{1pړX/"%̪cu}7wK< ޕ6gTa][W3NI;pέu4wU^pT翸yqa. olP'K2ʪj9*娋id X_k|{L0ߎI'G ,ejdž|Uo8ᶃ;0oy5еlewe74ןKOl΋b0zF't6d 'IB竂[G )#36ըO@֟*VB~5diH/֔#:X1!椸h"RaD|wDw D^4 !sg/lX]Ϥk/mWv^vKW>3Wʐp)$cf$4v-U ¨fu{j=*jbC=v oj8 #P(Gte:uHíIh #&3y.Ba24\'aMuK0kŀE5ÊjuT /jg<: kzqt-Xsgk8zkohj.B䢀{jT־[b-YZͮތEEb~Ω Hc,sL[ f6:11 x6|kד&= fx":V^h ,J,SMԟr'[@P 6q4A/m<{n'mtXy^h\Xl1Y{V~DW"'%&`K,.J)|asmp-Œ%@l+qI@aBÔ 8e70gD/c @2.DqOYSJL3:z4)Γt{AyQ8j@6oE!aeYJg섒!5E3GJH6Ah߀EnxJ=Y:@BTtt0Q11MaP_GZSI Av,CtdX7ՌTR6# }1~8Da@-}Aa>u a#QDRT&i  dYͻe>j0#2^KI{\0[vw"@TۼzXrkzڡ1X{NsNA]MMQv"'LnFHc i}iu<Qj[j?Z.= ֙ͫ4b`ߖF#3kL00ov:˃yL˲fN} >KYBc'2QM]֮eGy*;//BQnGP6Jg\Tӛt*ӞŧRRD]EX+gur0L\jЈ}? BN`}Q؛ څL ÉNy t֒3k+ [/FYV5"ߚCVӬeHAF a(f_zwla:;!ҕmmt!A_KCz'~zytCt^9Tg^[6w뫯Ay+@&M-kQh >I{AW'jcX ݠlt'ut6/ Osf9$f*񕘨#TXmpw U8R`6瑑W/^.D%^~~ͨ,+ euoeBq!f[Y5 MvbkZk VK6]u (b5h7Չlh {%RG[dв4ݲe.iTp/=enhoHpǜCLbx֚xJw(1DWj ' k@|qUJYz| PcQ{vw(D[9'+̸[lzjRQj@.{S+yݬ Z>=2Y5fUkX:}R`L-O,D|@/ϊŘm,%7N^ x: "AK.CЈ-z\;c\GjȂ/0՛ <ub~Zf<]'lL汮 !yZCŹ`za 2L_a&_+@id]V k$y8 ."2Tb[*j9n V鄶\h!A#G=Y6;WW%%[BȉLk_?O 5!E`MyBlP /XUF4Ql{kUG^f#: 5$}x+7MyN\גHt!:ɳH43V~LlEIM2_ v~)1 p|D-KRI#[$GjP41 :=?s֎|书&!pꑷ.NXcFK{@dtܝMIB/Y٢7K*vDۈ镜 ]6'TmlxߑDkghM=on /`&Z@"S#qDܳ3a/&kK Ef<.oۡgeբoO2r6idO1,G;S7*9ґQp oS9)}O+kYA,L U& !LEbN&1)xdԧ{>N7/B|o^{-GFjkka=v2oPHQ "/K.D0\6ҊHtz'ttWF 8e \QZ+bm(ȱg*.dE?<}2x.8}ױBwW ݊g6U נ*tY7%(SO!尤ʵllϹ%0)eR }*:N*@~]yQ1lGz~UfD[?$ji_Z|/叙s@Z T2M 3tp0_r.jVu>kۜ1b ݓ(SA o0B0UőM)`hŌ4\7w0rZz`#*җ$h:iub6 ;[=YM-qboNw_̤ jV94" %.9RwG8!-sqv[r=%xɠl 1$âo{=˜ǝCq~'[DEI`0pR84={[ǃ=nO(WDFvNaA шF,> "R̴\B˜&Țx兡 QȷE0[6RRR^LM²?LxK=jn x)rܟ 2@Z~&'g xL͵^m꼹EE29SNUOVRkԜ[7~Fgie 6=\~ U֠FU-^fy%+,XIA^z4iNȵh4aCKs,م9{nS;5r{fK"p ]weۖ`NIzd]O:i6yњOW3Ha[dg*@8k>@&|*c>LJy@Lm~PlMC]a>-ͯSZ_!fŚ" t@g* F q6<6Y k95 q&`UAe]da+XKSu2Da8HMDS<ě%s0b|츰gϽ/w~.^|W(=}Z-6&N̍o\v{cI~e$l?|)&fh'2ױz/uihV}A"rjh[wہec?Q#F5$`c'iZIn= )Ed)v #>6#.] EN4zx/ j8/d.D&o)C{c ,ְkP宮uwE"Lz>`ǦuB \C51"fjLŶo6ժˀRe5B?3{3gמkGm}9{[w8|om%w;~rZS{fB(  s~IeѦu=]1U32sy`<}wp$1F`;,ް)2dĦtya* :ɦ/ʷ6w 7gss~%8},g^e!D&%]`Nc#[("`Q(;O[ww?^= !`}X3 0!|S)#aUz1^#z澵YUiW o#ev}Oȯ^ Yo !1&L@+eVA(:Y +P:tMZ[ Wc߿i7龡+_P%f-KUwz߻s[~~w[dz`{;c*$\a]lZTsu`œ J#,V1NdE'Tƫx-)eqG8L/^sb2`[gܹ[:/{dT .(+|js8OYSM٨+εhh4 #5s-b1)IF_A>(D[c'yQ%>&5w{Ĺ>әB~߫ ,{AUc݄]Mpo9y6醻0 (ya~2W#51d @'؍Q̒a"?/PX " AZu$-bslJ@l5in?jl>ͥT鞐a+0<Uo(_iA *>B{hP(lҗGF)C }jZpk@0w㌲(ɼ{0q4]@:5;näX6Ut9nƵ9#=G~iD' ;&)*qS&yB`+ qNMԂQcHs3[s&QieBHNff +įe+VL#Ƈ':t1ɿbc*=̊`9cGfKE,F.ճS{}uh`5bXbS'[(m~60g{6oN}ݻwO'G/n::' ?[7ܻ#7£E}[[I띭{<@:Ph%* szS#fr |9z ]>Hqn HW&Ee>~dq _[NͲ'%?!#:e3̄95ӛ @y`+LӺe鈲URLߚP4#2@Bgeqhbc%੓t)?pWlSE4b1cyբW<Yc\8[ @Q׈/#zX2pv)R{PVɺF.VM=aIErCKFƽ$]#EV =LJwd$-\rN^;8Pu3 w/4 `z ,E H J5CÈDע i,:d`evu#p-^e`1l\iM!sZ/T8SSP*1W[R=UܮKfgz/ ;-*`oT{ouN)UbWI>N3"7-eDfcZ%_'" |W OG*2< 6 "@佂9ZJ T!Oy7K~@U`$%ȳѦ*-9%D,8(m~%g~Cތ"LY L7JfFR9u{L6U^):?)י`P3B!!p$,yY%8la,MNbث@|ǃQ?1z DCHi]>|>7Z}Um ªx_IۛE%Z%h0f /G~ пfj0qN:dfSL,\v$~ &MML]Wq>%|d NBZ[/? ݙNPn-'iamUx-6##6?3kZ$>.Ms:d:H;#.p`C:TSe.!9KvA{K,m+K˺o  ;@0JH0$WLiSyxfS6_6\ՇkWGqe|6/>Yɫ?fX}؈}RW꫈MGcvoWٯ3q$Eg^#kt$`ekEAxP4O^qPB_]yLI=xBbWjmwfb[&5^+$G4g1e‡I' Rʋ/<<K1ul 8/¯xE(Gm"g@$AxgRN|5v8-3jA$9S bEe%X߶TX7xt˃'/_飤G'3^r4K'*onFv۠R1;2R1['%:oi-FYm2@A bAl}q2MǗRp d=p . H[9K7 >N_~ #U#Qnw_k@]f?7V/_InpГ>Ml֟z{~]b mgw@[f hl.r13|!RVT3'M;G`H2Yp/ç/Pڽ^ÍS#> C B5cV)/bkV7u #|Gql^!kug{{[<9 X߻u;[wbG%׷gݯm"}ҾC #ʋEĿYLS?+}߻_c?z۽jz%{; v1Z(?W&QW_}Ԝ$[rKpڬ8g[Rn8?$CKzn/t]YRAj3|B#;1.hzJ ZV;~px,(YMyV\"ц8kP,:Ѻ~tt]V+]䶒eg+Sa]T!7yz\eMHSUdJػ<Ҽ~h:,UŚFM3!L't$D."YHDH#A|ۯ7_=:]eYUҎ 6v_}??"cPboxa. kvF&]l&6jNM3ƸW҄ ܠX ol{`03:@=BH U}VOrTHN-KhF`PoIB ~7sDay%[X'YcoqɮܑM^ ~A?_SGce҉hD4t`ҁL-q07'L煅 (WI#9T(`ѷ Z>O WFNP xvl֛beT<( P֢\!;'?Q m0WpL"tHD,ɣ.X૜.ks@1kH+Im>ihQˊZ? o؁q/n0 IB:aFY# Xsy̰vVP&&L h6*W(%tod' @$I{̥V\NA(! !bHp_SH1dls2?ke+-1;uj1`p$;d:2Y)4저^8.&j95y6ӣM2yN#x-`Z7^/`G˕qΤ 0dnL֥#\wg2gJ(͆ZX')X{ a1Iظ2{ѐ`W\e1iIbSt;*!nk\ 2UZ>ϲO~VܽEwDt׭^M{ _Jzw[$8Xe|Otl}\1 N{nRDwځo6phX z1Aie̠@KAijȎZ>+Q:oc*9m8~2?'d7/LH>Pfz1i:휀5 \i$ޚeB'aS$_MdfJZQN D$9x X%+h&(];Cފ %TSOm%TUCtT=DY/fUQDmh&輘nb`1`x@$BL[|]ft%E"gbYΖq؍ 1ndg!>~=_iچh܊6)C:h063iyNG| On[ԡd u 0{OEl)V e2eO' 쫼n p+g@f<7XT0f[hvT#l&eP.E3?Τd‹ee 2޵݄U9?q 0'ҕ'q $H\Wm6luǑkTV[!ɷOɬ]s=MsCY1A|lȝFh0;M(Jw;YyHDeE)4Ym\EƬYH |h}s߀ξ|*Z: ;K)2ϊbЛ ՋfKb#9RccŷoPXA⏎X00䎤~I^z|*'(`ҥb$32 AE =+&}%E6 *a#{`3xֱVħI8ŘA, I KǃN*kR`^Fc"] 08:Im/hD9_IeJhq&wi܁n,80̰TjPGx'CuY+W+|;:!KNTI$KNIAx5Hq;Q*, O'MKuΗObÃWGG/ܱ_ɽb}'uj'K+T³Z3?^<}) @wΟnGՋn\gZՓDpa5wbowQ 1K{i#Ǹd#qtzrHq3_6(LW kVaXk8fT ƭefT |)yGzԳddbR#tRiϞ3\qGӉ̨~ޮJ&9sD/ @P$2hs;'a#(Ԋf[8#\𜼄T@H`?ɕ@wI}q^P(hH)̸p$[vTd낲Pǀ<%y=߼< vi[rp%du@U*g#hy*PbfzKLW`VrKx@t "\n.m2$Kffc<1KXQi2|n8Eödz4# bEgUq垿!&_q6W;3SR:LrJ pd/OMVmJG+':UpqF q=kE®!dUp\Tr>-+4Z8N{y[OLw'UsF;m,3rjM-k}^ _.|&Q p/4qx%= ci3s =Y|[y85E;)ꆕ?Zo7|ܪWVeU?،йvr碪R6faۺBpUz+uW_E m(%7bBeoep5_-nWky哷c]\;d?H;+<͗_x=A'I͆ì놟LFȰ raE$hD((axwV N(习&$6V\N reWCdbJ/2akku_ W;owW]4aI`Xha߆o~oi珟>|6]omݿo#oGz;wC::$$aN%uvaǠ .XÓ XMg Gl~iEPaՀz4;7h)Y?#:4bb\9CD !9\|)+-&_2-aΡ$؜u{n5zeo=J M )q# x(PgY0* }Ļoln|5RbnTGC ^ZJ+wA n+bc2n1A05@r<VYЯ3,|MZyd2ǂ~-cgci0_T (pL\9FU, Ra?8\?@eruemI6W`Q9L)d?vmКmrqakfU ĄGv;dc2S{d7fr.)I?e,dC:A[>t!4"RCBu<$jl&61YՒxɬX<>xًGGY=T٦\桔 W^ɷ]h_mQE8{53ψէX=f_e|pcRh,)~XrÊ7@z;Og8q'>f}/W+1-Mv2OsMۛ+Y[Օ]6 dʋd KW-l)l y'͚sq1bS)Xz6*_~<_qĮN'#Wa8JJLG J!E~<S<ŶNĽYSttF*lS,GV0V$* hԒ9\C6S97B45Kg怋2ytxut:.뙖V n.$nA3׺|,{:9(~$/g/n*j]F2έb u1\8:V /K0-3tGT 7MpFn"ЊJPly5'_ 3xVznSKpSމ!z.Ct1 H:د#"`^S ~ =xa rBӀGFS i.˦^pQXLذ2[=}e4)Է`9jh.bƬ'/q*39!hrA&NwT72B=<[(Ì^!/Lx&81s.fE/b A&1,~OXFԙѧ%XLU9D P+L k:[  SHXUC΀htBZ3T+TPƁ!Q@DBz?XM<\ oK`7?=jt $(4$_tqu& B:|x.{P=5IS{AEpdcSPIAʚHv 'ɤsoUMDzS`o~ mY+엖Rl W*uPٌٙOJCޙŐ)۪a[@L, :)|bn !AT,8;; /]ՔI0#,}ѽK$f KJW0GdۋhʲS3ҹ;=ы"pxEP{DK>k0L`?f@Wij'⎯jQAbfV2|*m~=Pdҵ&(^ `!9+KPY# Q1|45Y3<3x6 ..ȥ6hb4#T(P᪯ m2/!.%%WwBisv] IέWz7"'! A$ TQTCOI9C #6wӢEP?C-vDd'-J K|x!c|wURX2sbN7+s#szrsSЂ+\4LVRRNg:3`oW#` ӭT{b KBQÞ:5= _ w޺U#5lǓ*#'LGA..a "BP!Tntؾڑ̷gY$*ѡ÷A;|ͣ"+#gn9/)!W5 ItgTTF gQ雬S)E2_[l4TO`FH_# ɁtQ)Q[1lPn#@™cXB/= ${ ہE`a#xKܡV0dl14k1pS.Fn^3 U~ʼnCz"bPY(iH3=W7XGE:bv6g~Z6l>snbf/DS4[(嗖B?gN߫CNzeЎu0cO@A9+ 9/--XS) ?ik@c7G]nâ{Jڪ\EaX`GGq#sy#Ѭt*K9Qk]J>.Ek1EO-O63'KVŔwP Q\pdQoRH'mq;,>j)N霂CےxA$7U[}G}L6FI.F}Nhi:q|,Aw3t>Yy1ǨwD3g)hS_EU\S!ce wsU},f~oPYp@~17 /&pOv'u^a(>'a9)c5"Ϙ•=OLlV@VK:*Tq? p 21ݷ| d'=(X KfZ`3΢o@HYp5hO(䤂I8J|;Z6\ &fD E*%e ZlD g]wL_p z aTS)Ҡw7lL^nŘlL^lH\r#_ C>!b6 o䤈NFY6Fmjhm6aUAHaa܉d9*+ ۏv}Ͷ^gnNO>fEiKvQя-k/ōāǠxج]Ғ-&| `4Z7v~ Cԩ}5+_cjӡРy%:Uo$ zJM|46z.+a&R£DFD6B91X vl,0$IT@hh`硎&;sY+ۊ$O݅:S̳w|o.$B16?4d47@[7$]?tK,XO63PO;^>USsՠعO^lM@ U"l+ѓ+z&o˕Ǹ=J{ݦa)gTD(c?o=Y:ljFVUC xV+w&0YF^zʖzԄ",)0E&Fm8-y g^H䡌 gZSoF Dqi_ջyEБ#x_F"{׵q(5T|ǃ/^vD@G!~1PNo]FXK'n ;jO:0jES\h1v͕ċH AΤlHȋ,&~? v#'=Lmydz6LyܛzNU9i'' fF9״δcJ殕&GB2Nxz桯|S?#zzpKqi")fqy4O2 Xx_#Tό0X,Yz6y}bdrX9Hl`87b̰n5|pvF!6)G!E){qR[_8j'>Gu߹ǫ6-fsKa~)μcli]e["oCgRX_2q'N 1:Oo+(R+}trG HF2ѹ,KKHJ[ˢ|<̲q3N)`+̳`^mrS9 ĺr."G#:>L/B*qx{x4WkHtJ߬L96a(;:DQj|;MG(b`9sn]CCR^O;_)o@Eh_a ũ^qȩrG)/'NMcQ>?5 {Az>cy<.ͭK,^1fkx"\uzA>bKJ+貉ZHS<%4 Tr9`JRXSj؞5!~%h7`$PH$`.\1i#`q% ^$,6bPVFj)t{o䏕~|3oTAf mt [0/nk;N{r@(?Z9!5 HD}6*U^ŝkzQ[+ }Ȩf$RQH] gl\m0 w%7S=%=r?q8n&dಹFպqߣH;|nC|JOd쪪 - A!u“+<_"et "B0Қ1 "7UjDAL_-H5d((UŠxKfuFT"آH {)2(!{G4XY"o%mo$2OZ(cc FV" "വl{.<7.7^n,#7 qav$J֚t4y*f8_ Ca9-m'h0px/{oPYvMU2" !/5.%x]BdWcPsz[` $D^R&gp]+[љ(y,\|U΍Dcř6>`sӛ4ѳ,$I7dK=e?/k|gdd?v7y:u@Hn{DȞn+2 -|MbF YV[ڐ -񸘡K{E05 E β%"+IIK@N$'кyWq;΄\(琢VO KxO=tf[J#UFP @x\0$%}7s(Y&?87͙j8ւeT_&W97iƟql`4|f3}gh;o7[7UkΌPAPFhP!)ŕVzAfV/3Hӈ@ /nkzT۠aᶓչ,Ҁܞ[2E\9Ee3u ܤ{cnJ͛ilV;j cQ[}t*Le2rfu.Q'/Q ;'AQyJ2&?lf3Z:}IW֏Ǣ MЕsf\l*h!M KtV3'dAR@5Ë  NtZAyx02L.qѮ1*;L˚ |.)Dɖ:+)@?g>] T^ Pi};liNtV 8˜V#1pȈ>;xr-Tϻ dSi8^5)Ѹ KIzԩ> a[ŏ;'!>[Cq޲VV;lr=|36w. 7€,{;[=(^aF 7ed8,O7Y:6r.48̝2:5g3ӱ9KXp&6DP4XM TMzZpËԦS|>8G zB8  %. @`\\xJ@Z"C8#-@Ͽ24!*0-dC b@64#$32 \&*;xf^!| WQmWekRʚН^t&+)nrOYq{}+kaxi]w 2a+`fG)9‘1J2 B\S,c\ׯNB 87v;낷-{(u>n) 7D0S? r/r\Mc4^_NK0,ʳte<`oؓ|)W]^tg&gy|Qk:!}FvEQjtQmQ"Olhg~ݽ{ݻ[پsz[[wz>h#? @L?]\\ g+7mﭧdw'n?oI;8M>L[ yGgr\Alp$t]4OsS3]s(' ,9D~kęD],s<$~y hB&$Y'=NO<:Ea4}(^4R-4nFP3@y$ sR3A6z<;V#E7Y0]O>?&7M|ٿ%ʹ+1O?r7˭ޗ[}]zLh1Fș/OՀ;\nB?u:jk !cVʃл% %A.E>ЙwhV_LE؋%?gx7uQ?Ri{~Qx.k fmc0Fv;9Lr0Zi|uE^p|aa\ntkJ`=S.RE ;aMIr8vŠEvl=y]†,=!4xQ~hu(kxDq 8vhn[B(>+I3gCfy>!'u,9`0'8BK:γlZap7c3sȌwQo$ 3dv w0uAOY\BoQPcz1Wpc7aG}@ø-߁{AY3rzCNJFaPT Tr;Чo6iB%:5 mFՠOX65:}",-f{̞qmszbn>6]⚉A$es@i֨6GϞ}/?\\2XbUȥʓJ%Vsh1 N蚖M3x' 0JoU˻iI0L1OKQbI9. b|P aS` :*)'HeJs3_fz[, L̜rKp GtKxctg,:Xē*B҈lf*Fi|DK|$e`x@' JTq@DQm`SRX\USʔs R<jϝac1RTb=PTA"r6dGN2*wAзF':K-|6/åi o݃djZp|ژNU_|#e}1b>3("Wkڀ!bYOЮ} X*'^WbVGXJ\];j -3 KKqm/z,0$G w?+&mJ݊@Q1iYJ3 `+!&0:ڳ35\*f)Q!ΨP ^%"V8JŐ/ &SɦMvnSW)1+!L1sNT;QumAmA3L^j% fg|{ux$T$ ѷWoRnx:&[߳٬poXV*Vc9қz/wXfe,uH.kEA,6qs/ ySuID,P56/pFD7M*1!q&?f#,r@fs];RAa+k ZF8 @f"^jv=l)bw; Ś4H`d[L<\Cણ$>g- Ix)|{uQ}t:rSYC9/7QN \a4ХDsލ$O1#_6#f22zVQ;::㇈_5l98pbyaMNYCzS fp|MHx`y/YύJ@U 8(!v9N]jc"P,=^mߚsNżP6N˳B\Pb+'|.`^ +HQܺ-46q* #"QB'GpgWb[`̋3,zA]lNR+Q{!VJ܃݂9 dUDj ~ݔx+ qO?M+{wn+?{w-~v8%/޽WIo{g;px؈o<|g|V q̛:|!Ku?ܸ޽O.<-ྯ{ r.>HyW^aW38 i'(_,ReF8Mg#cG2?OJŽ` "h6n|>⏉ʪAUP6iBo-R ͞5jc+4BYb:A\318ȳ L~EJɥ`ɒSC&ʺ/jUuCk6)WP"U^zRFѰUTu?qpy6z:̯=d٦Ԍh5o/o`SJó905ܾ]޾lse"qS> l_X>%}Pdqe1r3-rqFa%l7/PiJV-&\7oѭ[v3t;賅ny'WhOMf^$vd@ eomzAE%b#ͩ8myPИw lahD6)*yq"HFЖYc]V ÛH/ ʋ-|xY>q$CކFڱq*(pţB(yHv"/- zB`l:mڽk@r{8ӡ.W Njʾ]TѦe!vу e$kP8p /1%Ppl+T?}L:βW֮~esC< |HǾ;ʀ̬XLɗ 5E1l.s_?a{(!e ?Q`L9u kjejRaSxM?.G_]/B$AirF ً1u!EB[#X\L/^H6xF[J.l mM]ү ,k[!zB}DȂa!{~Gfu4H 0!WN&~3KHohaKK!o&E5Ő/`1>tq\o[]}.*33]r`GanYfpʠ/m] 3B-R PAoPtJ ٖϻ@Ge4k6c[s٦dYvK^oTMl}DwE=WtZۃvHԼىi(q%jr%6j(9+:&V4HL˗5Mc"we|rtuWBw3Ne藵aDYR.=40+Ϥ4ݬ2'9_>dC@ns[ޯ+7Aqz*"0˶@ؙ/.?pXk\]V:m"|~Ma %N_kvF6ܝl \̸7#&5@ ØEl>j<}Y.N\qD Ix݋a. %pk;Fz[+r۰G\6X%C~9mU0}PJחtWXjg ׊ 6aE"1u-57s (+`L 771-s~^죉V#k.G2c۰6%a1Pe%΃Y:a6Sh*PBIa7AKg 8n䔠ISm8p+O0J'?~xQms]X;{ݹcm[?XO<7{{;wp?kܗŠ37 geYV+k:*!yQb#h+7Tj$6@i1q"]I骠6l }xڷ1)I$~" y2YDZwο4Rjr'y=^dn OwmN+:nSLZ2ґwY7/znd|էfEy._q~n43-on%ن`eBɲb:^5N #A J:X_UR}g}մЋe@`H=#wJ#ڛiO)#"VMPasu!k'7'끻fWM.Sf FM`VRdAn1VeY=fZj`$|brX%} /AoϷzo!?qw@߿NzwwwpV|\&# ]͇*[`9,dt r-U%l->gMbS\7-Y}So7ު㺰:8.l~Dv=zB(A>:tF?fθ,1,Yaʁcլe*/_B$)S 9: #Azw7g.ݘ`iPވ*? T)W1Sװ;Qp w+fG݉zƁ™Y<Ƥ'Y6"9OX~jj)"5Txi1y V?[YwXq\Ll&G-ق=ȿ 0WWי}+; VMe3B C,e6P"̮.{G;8oEGz֘%ƈ-r8eGP[<7BD6k1\&?A>; 9|iݧ]%bPIC*5xzg72Gָls_S(@ٓpٗl_k%\yb]r'lmL9,X6 SU'kʓbT8xKa H(=uXBJia C%<éWAA'6ԡb9v ]巹Y}>6 ŚQvkG] mC4kݱ.J9吪 R?] z 7I"CZ5:+g@3"h%%SSGx8.3_O1i\5UNP( 1(%=SK8c쯊ׁ/fI;q%\ l9B7$7[Cgs=zҺm,g6דZ,%6@lA60M3ԬE5J9- mK{!6Hk0SnBGi mqc$?=_=}tnćt7pLy.wמyBK@z>*eYί\|`*t[ XEdjazEnU2)Q:1 1+[dr9;B\dUǥ,$RkA~*k_z^lW[<+-iBZew?~C֬[mZܢ>3PXYdH=Tdh`\\tf@-T]"R75L*d8E(N=唛"7D!0 dI|FC63& ˣr:v~ZLqXE1b|ތte1~'$w:NIuNu';;w{7L 'LuVL|qγ nHrL( 1Kh5,90PLMX77OV;qt,Ų9Tڦڍ bizpxQI`W?E?([wzw{׼:hCV߹de5:Dql\#䠖}D~P9V-I:PSU<JfS8l5KSV\Ԍ;'iE9G٦4ZGJ̢n/Β#-n-q' !@hI'hԒƏLt6lhJ~m8U(mi:"0%beԁ3:աi͐t^Rˋ aY1siNnS ;ݶ~J&O'^[g7Xq‘?O秸X4LJ٭; |=9Z3 37ڎQ Xzf*rJK'y\!!]ȈylbG ivzvY)(+9C09ަJ9PɆ\L\NȏU?O,Ѩl<]X$c ~G-Qy\huf1.vlb~s*yF HGD&xJOH-cvD+z=='3ǧ9PKMC]eT1\PVmeLD*H+Y{M51u[~ 4w 6#x܁,S@edBuBe»{ ]\%y\bbsնvOH޶_8T5}~Vgn2̸mFgtuW$<#9I+/J@{^):6wX. ] b+Al_vКkj\K8)0DSh0nZ,?qTMmN#7u+kW>)<3?fHCS餘\i{zTp Ș8lQNsqubؕnZW1DL7ny6cNgU3GUH@`^G0DvƜH~&e*{-E! V5K28@л&Jq5xç \AJEjz@G-weԟM*dn4:,S@ : 0NC@4#C%#_MT"hA.FtIs5wnߓrI_ѡYas8ѻ'

iNYSoVݎ5^Cc^,ݪ6Uܡ^G䚐+Κ.rMxkᆁqKnZSSS"vWQov*P+蝖kBvomx+]_Mk8kw`OA=Φo?^ZNBSj{{1_E2F]6o;c"|!м{9א.GJ2^^ Idk]O&Z@p2IuÒtͥ~+U7"I/=o^EzJ[CaIt(w{R|l+wUg7 qrmJz;۽^7sc|MAN / GpH{=׃WO_<1{ɽ7vExoIX; Fokck[U߸7 P-p44ca_5n[2G8=-~֟ m߻S~ުן ;+Ei=Z?`2W#PG A!]l]I\O)^$l4Ϗ^$SS/Ikng㴜~c}։;ۤ5O=I?_td<;xrNjϗuxNήׇ!6S]R?j0PUJ'' ;Kܔ5KA߭/ R L|j&Ņ6P@Na+PM<0ѿ쌅@ fhF1وTpKl¡iY1k=j~wWl­.Uvt*9^"_ǯ^4ᩀ̿1ڰxem =ec 0,m 5h;@SiKi7[L"c Yf 9`;HKb~|TW*BXH,?|UeP3!އO^apxra^)-Y5`  *Zk9kBQr䋎++Nuj[ˋs<}$d9R=}L.+y(2 ۡ%ѥGo,UϲVGFRM;SiqI,#hqIJlhIiƟcnt+^rʹ!KOwmWqKF\?H^tR3ΥʝFG"L^R })dJF'QEt#_~GM p9+ft۩^VFb}Ydyr?tBigE9aםWzv,%t!{%}b= s`O"v]ۀ:4ŤO'OkN#>[/WRBÔgf1p \BWכfZzൊ/91<u\AI^tM*6V_-sgoU'_Vw7_';[wl/ه:1OdgOv.!&*$SLN%l0[;`Q7G6LC-JBi.p92> `OE"O=H ܰ|J)K%fU]q_fh&V#x ^g[Qjri*-#?ި!;A]>FpYg>lH o]XߖUbs ac$y-66'@ԑ`j1D$],cj)$ȡC^U\9@>p_Ѵ+z ^y)͂fn؀y$CFm 8oFdhrR&lc>\⛦ 1J?fM21v_лC#4f1h4<~i7 ~{+;/;,G !LN v]F׊ qhX@ꭴEBYrRs/L6<l/_[[ty^36;\v &N<,dLCrgv H&X]wu&jFU I'$IIpM4L>~H,9A8W?Z:K߷Gp]ܢ[S@o$xk R-v1CDvsn Iٵ^EGy;nfQ,:}.Cٿ5\8DX*ܸ Y ' Agb]TJsO |t fNf5mᵋ3|4 BXg̻G[y{xĕg}[7yAcnc+fFwj;$ü0SXyPJpQ,18!T 8Ăgr|/(wDjч7E&*"3T r+ɐE@niX}Ȇ<|s+"#xN44A[vʵa n} ShlDӫF"\$P`h& PM37Inaż+YIdEܬUnzͭHGr@=,? 3+ yBTO; BQEз˪JY\%}ӷos,g3Li /#IlĿ^rC˕T7P4Ɔ*ݥCJ>Wes=2 9@9K$UӨ%C!P VO~9-&#𴿅A'wmԱ3ty%6\I5"y3É^;6K3:K=q_|:t 2R*0#.ߡ!V3Ʀ3H7S fS'ā0dGpVz)& &Rzgde"NŠ O&*!1HN=5>[^w nf5 ɱЬxo #vc`h. #dBֈ(͙وEbGd _i >+|狻Kʎa~s.!-9s%Y. rlTY. 3ۡθCؓmյ8@LSʦp$9a 9\:Fbf$Eܡ:K!>c`\<e^Bo# s\(k ZGjjED7hkFYvTNjqLA,OeAN"bhJh#j0z٫:bѩ[uObv<9'i~HӪUBO IAAghMB 3k;+k!~ؓt͒Caן`g!Gj-z0l. ?3}= X1ݬZ'.87rH^. @!]ɬ<{xk$1q!0=Xx1ß!RfN}hw1kxwY[i!T0/A1^k/E,/ݖ#ek襸ӵJu ]2h_xȯ1J+\yG鰙yihk\S~i﫞H1W1N[B)y_C-EK/TtXmƢNOjjnW`̸qR4R9hstQ q110)|2 Yt:r*KW~rծUN\-VҊ{1Ёޱe6 *3 Uu3W9ifM:qIڎamF Z Dh02F3 ?U9(VR7~BQ( Jʢjd@ JsK׫F("x؊T0*BL..=_rȭZo#J}`Vj1tW8eZ;O&˾Cbw?6:4=4zUDb+u):Ҧ%3tT^ۥK#2_7S*ozq>Kw>[i)k~w@-;wt 4_ #*/]dO  &ʄqm䣼.P tF^x1NіC g< U54vx\`'x/a ';s\ !S }2bZ X E.<_KS[Zż'!°)y'4iYYzl=yx톲S#LnG L3[Ab+B`qݹfke[n1- 6IvQaF.V\\"k$ibN E+L`5틿ږ0U[ً㳶%:|qS GZuZ@.<#F)@?jաouED-˛[#}e{bDfgLE.LL%Y [}Вy<X \0.fvݡd㧇Ys*wK9xOؚ3za+ӣeT@ ˸5:Cxyg5ꓴ.`5pqgxZ /oA*/9\kǪnUvDLüa _̍<hCTIy(PJ#A&IsX^a  j@qMv౤onMNP+so8"|Ad!u(gfp[ή;^G,g."fԜE \c6vfE3r,ǜѣҶ)((Q:{4lGaId[Z&ϒyzR~CWz$D՜BN 4•%L8ƃG`Mlltgseؔ&*fCL&C|[<Qh`aH<v2·X`LtxoL^-r0-mSkya)ܒ9ڴWeDta?!:2#xe뭏_FےN+kP}њ>vǍe%#=ݷ85FY;>GW"oFdb+)}+ p.tR7LDm}r5 2ĭb15XGfm yi ]毩*7M="3>2\fr>YgFGHAiޭyMa]Ӓ=`w( a`P'XRrKReM_Qy*1*5zuw41x& XW$|{ i2kZBjdkW˲nnNiΫs;:AJ؊)W"\1 j1'Eڠ\v~*If$'-S=Q/1Ք$Y]Sz6KGg?QImBm- O'[mugEj:;.DK MFWL\JE嬊C~8aČϛ^N!tLzҺ*f>_rx9#{$݊N.E$w5PU-CE;$<M TKI>haoT+G[ux2J'/ X巌m #sf|t5I n||p\@=.٢$"zI+=;.LR##Ak['w.9=E_D8J,Y8pˠn `F`etmПeaw}0c>|=%ޝ!by3|DdC[TwV塹%n@YV(#Ez 25jٕ*y,$?YӴSN dM܆G:(UK9u!Gp. / X깚wx` R &~ hU&d '|\1<{#A[8ӯqK ,~7@ >zQ!#?fkm4G51#^z5IwG@}5Fb1J(l@9Rȉ5qOیpuJF޲";7'Xi| -b]&_KІ[)rRX9,]8شt\]^9$ 1ȍׁ] 9g&;T5}G5oXK 痭hT ^ DIEV߇/%D1W&Xċhu (1Ԋ9ـȬƎīZDqaA{NI^O{ѶD8-]7u2'oF LLL+bTmH -&U㊭ *AnW"60g k+Գڟf;Gt'MUHOd'N0λم?չsl/[=:*Z$:z} iCJZQ-\ʺI+}w1KVRs-`arWv`y W $£ Q~X QxUV A`%T$ &}\i%h8eiF@d.*%uy*0ۿdH,`׼ -ɡ.( HVvoqHE+HC.`U g8%JԠa8!̵-~}YyoܮJtr"o@xUQWp Hf>~YM]o8HuzksxrX\5/ۙ5ɪd=ZUKepWDcdeiC+QV4P[Yweգ51ƣG%x /[@lx kaFa#@{f "7m-Ѧt50n:[АxUkBOIS^VlPe7ZE 8Í'MEWLpm)n1/jZkfĿ``{W%<302,lv..n eB+u>D@_Vu{uhgΏ,gUzQ#$xSVjʼn SgeyJRDEÂ@@Kg(XVDTA9J T\VtkkkVwɋt1j.hxi' <V߮~2-YNamsWcp&Z.Ȼ"Xgc.{ ,+}) dž!ێqgND`ٞXdʬԨWN3s{Qы%SB?+Lӣ{KNaҺm^M~+t4Uxs/YuA`]uodMY9gY9uq%βa׌+y ],Q mZg[es/.x-Uغ#2Y:3,:^#N3wpt6+<09]@1y1|Ƚ.%Be>{\\%Ы~Xyz8ZsK~=-0ݲKl%dرc#zxpP*SBQ 4YaZC6Z5緥/&qYlcf@Q$0.>3#[S+oK;XXu0S\09ҍQ8)M޻d`f0pT<88\ĔcX)1/H˴}" ƪ:/b18sqJɋjk4FeCȰ8 T礸21?dcAIGRX)qq$,G ߖ^tu/k+,ş?H0eO[D aV7Q1~hbY4;zxCc@Q|Π8PQj2 jJ64$u+ sm/stӪn6`Ud|_ViN⦾̞jA*o0u@M Uêh[sDyHIx#$K>ܵݍ3~+ oضy',Sa9W; 2 }~UjFf}~BmaX}B=T0/\nDqZZQ-r(tKеY>6#/Axx'GGى$1B56u܎$2g~yZ*}T[>:0dPfkԆ_#FOJ'"eV'qAQ<28 KAu]bahq9hH}/"!M}s԰]Isci8F.kl$qW}tX~alj͂f~d jaXl8t 0 [aw+3&> >BO];#7^oXtEt4ʯ bĎT @4U-C{ZْU|P,5[q>Ji|e2f}Z73(5!{|Qj[xFfn_/5.8nSI5yY,kjOU]!9IqxR B8:L& ~hc]%<%MO@[cs4W޵Vl YkRTeQ^ @=糃,5qk|PWw-0Txv޵j[d@H.{[BaNwt,Biʇsib#O]JL @ e%sm-(Ubnn6/M;ð'/qqDȖK7YTWL|]%ɘ'9ϡK93o[U2ޱ"^ ѡe6Z:O&lX]E*C4piӨ?Wfd^_WЃ{}2-m\mD_8[:i]uf%͌2ykپ|_zCLܰA6j| g%f-lT7q,tOPA4 hS)gfFpSsfL:赈ЋGБ. )A(z M͂MR+@mK/ɄM@|\Jr~iDK;v%^aw 5UGdmPGAu *Fѕ 7Ho[9à IL ISW힩 U;ԁ<4{,<2~$hL-.(eTj+3g9ct/*AYrhUVpiW揮s mN$ ܄Ay1)QZC f?eh !v)؎tr"Ey L]!F!Q&-[(L3&e[j}bdRS}(h ʌ1Agf)Z'w^G*h nj򑊙-7:CQEa̤ LHq)8.F;RS]y~0cC29 ,m6 -+\ zVafCLӧP`\rn6j~gV4z<֒",::M|̮MmvZfsll$bK8a0/(gi>M/ PI e͐ͅN^]%nZ7P̍,ۘ , NrN|OtzG}vи>WRHXL}c'$5Ϣɂ3$@˝pUo(KjaWTdEVp L}ο,q@ˁꑭ,~%l"Ջ;s'tr \j/|O U{eVufC$=Rݩd=|+T벐7Q-䬋1(պtCIQм;4Wf=@^~^3pwmRձ'%L;9t-LlwLx6%R>zkNÿܩ$s?juH=E$M`6\lq{ a)&)VE~E+d684u4nv .jWba9nM7t>w1KS$n.ٛg^B^l [vM89o=bs@MF'v{pa7FEfV^Īnjz38mrc].8 }[>ME:7:CՐn䦜h=ćgK:l.Se2x~pǟdJ?mHħ4al,l+GDnYo ­d/Ipܱzt#F{2GrGPϙc#-X"-5]BJjh/nۃټ@n0㌪2@3A.VylAoAoҢRx X#D,BUJF%j"L%Uo(-XP/+J夳>;*1) #…ij-硂;?oy1YuWd)j X]F{{>`.,g'p\īU jn3$*Mћ($XAΥ! pD)NtR)Vlğ~a)\?\M)㠀YW]c -7qS 2xkp//KmЖpQ8b []FjATTr|Q )D0K 3`{H-y b?ðxsp%u$y7늇9pgtgs]h"[M,l+0t_d3:NKsyaJl~(( 7.f. `{@塤"=4m|y_J{ lOGuSLF\nA,#jћf ;:x>GO_?;PCR GW>)mw]ӏhc1icr6*&Je=]=]=ZL^.DR\M(ے)ܖ9gOB{P0 gޙQ}hʠf6O%Ɯ;0·giqD3 ҽxt[a:!oLh‚|0Sq A84󆙫+GbԑQ#-j\yl$EA{^S):5 80OplY!Ark)= uS@i!C(Kc7ĝHaqxXvg= KvlObHi LpV% `,MNz&?M1{8%GW'D7l|áȨ5%#YFy"u!Xr4oq'Dͫ9N@IxI 11EMͶs| fy L.kYڥ8ەo]5oT^ ޭr/"z1q^->pk߉=GX9µM_iMoYa@o6]O\0*Kg˺ev = 1uXݚ,b.wPZ[N5px_ˈ'M`n)aQ.G)!so;NrBLyC5~4 9CaN\T:/;;zZ5UIF 3ankpIRzb˴TyjPyLiec*>65OO̖KXny_%R 8dRޮEWKA s+W\R[ d?u֋H@ez8o/*)ՠ6#>XQ'Vrtmoȏ=Dy(q:ĭtOЍn kW [8G܉RY5넆+qIXx!'˅Y gwj&8JZߦhBh=v;}i1npҝK[.e>yYCcn[@\8r˶ɹy]죭-fnlnNN>iU*ly'r<gOS  /e bRR|}:cA CFܼQbcR=pkBn0#nv4:*;38&.֪>axvll?iH+lFμvR}RgETjMt<2J w bE_vD1]k͆^16+RfaV>56c\OE_E'ʠF5+i놛"AuDo-3sSX!-Z&=ݿ%9,w7-Z귦͂8#|Lk8}LkJ21LGk>PPaBB jؑd(bDڌ %TS c KLh⺴H:d%h1VY䵠 NRK8P8O8~Nyl/Túb8X '\BNm(f`!fIov>tNź;ܣ]a"btj6k#aU?'".0ugF=>Dza^3~{ ~XaL/:F_#̟{0vI7V˦ Ӄ;/} ?eO4G6͢83K R>R}6jjVz`W>eÒKAZ99ccy]tږ|$Y.m`w19J48tKWo=`Cɔ`+H\ͷzvY;גE1Zl&5KA䋛QT$ҋk {jNUX4^k: l5sk);0Z|<boZ_ӸA՜|vJ1#*oQwBOx^~*{Yi Q {<vQ6gϏQS:.#%ݩx)̇4T6rLL* 3cz(0[3QwXO1I'V*"Jz:) 1H9kL4Nho+rQv=ѓѫ5G/ ?~%)v^;4I2ϫ`N" b.ASov0p 0I6Ji7,.2d3)O="}_L XAL>6ڹmAo_$G@'̈n_:s~ѲZk6V5ZSV׆(PJ)$!e6yQN4D[1G+<F>TײX&HZlx9bbi{Bge]n.*΂^5W5 [#Pujh* ѽ^*o Y=sB 5vBp5&g –m[!~Jǯ|9H2V}zf- N μרn~☲2 a ?OEN9׈D@̓UىinJIV@&[Nkc}6XH@Z}kjq}!Z[ Wl#0(mh&~5e-hܷkLz2boY'y:~b[ C&cj.EHgCtwcA{1;q bzaPd)M2=A"qz/cL!mr>z4ୂ_z%ڢ%D@*7XM|Jrt8hqA#7x}wRMNC̉!8oݠ#x|k{Ib؋꾹AJ]/^;c4sS"xn9Λtf?c?$͕M%?c^˼DciN50* ixVGmm2QJfEluUX/Y%'8G%,I+([Х$ B{ [ '2˟N!E:Gp!2p_ o5!WNnJIX>b"ylAd0[Ӵ [z$_aWL-c!yKȜw\ 5ju>sM} 6hs!K5_ږ*89=Ċb+c#sf9] sa6T*Fi;SwU![hkK~PӆPP$tƫ@T \8^Z; )Weԁ58ŒS&a0@4/. YMwkb%Bne@q]=J S04aQκf]bs8Pm$1zLՑٔo 5ր{hTp ])z;/-:&B֯|QSTN1Bj2lnԥ1dr4x$e|Aϩ[{؍Ѕ:w7=iۿO/^[.-;uhMf~ dr1;)U?E67tv EJSIêxEFRyKJ!a1-v `y  :Q _9ҷ=5lYp.0vAf»l v{ߩRDPXe*yozh>:Su"HΊ57A`NMP>imiZ)z%yb)g7{} x{،^G +lrShvFswr.=7rybiJ cx r28'8FT=sn1E3[Ƽ˧F2/)FZ #`U&nVԂ׭hwF=Z9RY/(JSi)96gi꺚=Dkͻ+b W#XYfmYcMgy*>0о[3{[tn>7wFz|ؘ5v΄NDqnGM8vjeWpcAeigxqJ|CнҲ |vn0]dӇXs0yNѩcfjJx]%sL9;ӳ#J-Zy-CtG=h25"θ8AiEO(@G"3Єk0 'sm4ŧωk6NH3! 0ȵP4 jn+LΜe[K5x00tKRѱcJw짆#C6$ 5ݬٿ{A~޽?wv?$j0`w/9+NRw̒ {g<Eגܿ`nt.'xY2j"A$:P8'):.NG)5,L 'OL3.a8.䑠~1\:`Jqk6qά]am9'!zd1c^;Aτ0 '4;.F?+ؼmc {;g ~=ˍ.}C^`i9=RQryz#uSA- =K\iybƖ,yg~ndΝC#*\W@KW!.jz 1foCwYӢZZw߆7;[wүU矂m4Ag`{{;{}8ݝSD?Q1vvePQ2j<|m3K~LE@IG^cQewZ$f>[ok5z@  gVӆy+e99o`ޞD- 4.8cAHg /M \ @γB&&\J8!XkHFup1'i-҅vO<]T!)A ŽgAI$7 gJ8 b%Qf6x&ڃv*3+f }ۇ0gx*!ZdK"cDWwl"&Ge8"] "=rX!"hĮ(ZO ϣ8v4X|kJ8:ϊEQ (#!t>}Khڂf(zIosK:d# >E&@Il|{w"}k%❦l "Pem%Cܼ4z)p^%q܊9_%5:>PHqOhh l4p4{5'dS.A[[7O_ֲ5q79 Xy]]_tGk f<`jJ]T vTp;EV= Ug <A⼱9g#HF)׻{ff>InD=qj.̡.N Vo0JA_Yٻ{ν]~}O~*upN/f-5̽76y|TOv @mQ5@pq$'?Vzp%#[~7כ\|Hx k!U:,hmRk4"^F4c=kR8bW ] VyWLl㬛kL;GO;LvպV:s{]µX]"Va%&c9t=0f| ]^yۘrw@2 {y=rz!@K}Oݘ|e` 5v}}Icg>=*jNe*\-*QW]A7E8t%ߓ*mFm!`F^u{KYuY8/hT)BejZ+HK~TPX}U1ox}1ldDl냕g!_i/BlfL9.EKTqϚT'Uʵdx s<;,[&C:D=l'%':a5_I;-߰5t~KUd~m9X6luw=Ésa:?EE,(vh-UYP qC0eG6?'mY ͸-~Bq_Zqz y'aQ8?;Pj [ܔXtNZ ï&zOm^=HPta5Jԝ.4l::Y7iq1!lUCy?Ygp^$]^CF/1?yzC ߁k88bO[b/OOOFևĒON3oy͟tvͷo 8jϿ{{f'~/?uLw ;߅L?W/5ޠ1x},9$\sĜ<hh @rL1)X P y64<<X#z& 67L/7f)Hnۍ%u鵾q))Ei7)N ]r)80KCIqVb:9U[k$7ϥMPIH˷끽~&tn&I"$& 0C 8x~/V0hfvৼj տezzy ~%".UV3ǡ 7 ] !n^ۛOu7nk4AC]+#y#8+`d&6`%t=vrG k=P)b$ gL}Z$ xIۜH;Mlm"Xf\ok]N>ިHG Fj=\p_rWU@ Q .:թ̓z-鼭KR!׼{ul`Q7xK~oYEծ"&H(lp<[w=<z˒%^׸I MB_ &/wq0& .sz9uy <|x&kfNE7g&,f{bpaj7r|lo;mi"'(Y|yC%190\:K˚ eb~M&&H aE{A =%2(%b%^Gy`X%a(\dC N@8IJ?<.\9H9@,ғ{G@w9tĂ'MDg _uVim828m1ͫcO&[NbomՏ2a{ -D@͡mLbAiڅw TD|~6%L%^~J-T W! ]p̝IYBX'B>=zDP~3MHS7"˼;&CKֆ'p&_ JVzDW'D!7'2ۏ*&,.3TfF %(~\:*W~ U*^03y?GRۘYYibj;~:`stddam>k& ܧ< ~ *Pt"ަVo@_$453/3O?zx=R#N ?oץQ#V+n j.ɫďLzODni>.͘\b } L[LK! q8:!a|$bG.})IBɊ@btJćvYˉ-,Hmke5YE[<~ea3S3D滌#K8^j2ABXӴB{ڃ$֧HVɓ^2Z y x%T 4i6qm:#hyD}6e@x(b?u6RIⴄp诹כt 1<'Bk!|:bIk0uD%YD#^̀g))ho FkbvECG92Y2[qv}]lcO'b~}zE(^ۥ N78unbp᎟.۶^y))s2  -R'9<>vsX@\eJv;D<{ShIuk,o1,ź}&%Ru!P0I&>!XZ(b+yQ2sHQ*_xԸi#؂Y1 jٻYKɻ<#Ig9&{N%Esb7!Jg2|֌ o8vb]IYԦƒ/`0g#S; )$.;3n>'G6`&^ WcPI0%y08K#y71Ջxm*|Zq/e."jG40m^öcpYMcaV7⁠Nl:-ӌX$O.SE-t-A-2E#.3Hr2}pa|6@2.h/%T(X d?:, }R{P_^Ǿ}@[5[F|)x;YDF-芦 5 6S5`֞h^ḯ4Pyh]ʙ:v:~oɤpIkӦ*O>Y5Q$saC T<@@6mQ] WǟC܆Fn;P'_#k}k1F+z:,aF:Pi?K&,퍸J[t9fò+qJVrViaQmq#\p~)"Jii.r 8-8*M&tRxc+QH0yuCP]G𥧎=h]*{2ܶg2#>t@Ԑ6w>x*PSC&ϒϦ]!otT ?g?}2s\oK=S3tȄw#)tҍ+ƚ YR8|*,lĊ_U=xZtڊUܭgلUS`a_9H;SezhOL1»| (a$PF+PNE-WN<&>w,WrAzr]r%r_m@V 77PW+ΊU;)rLjl~1:7ksT>E9;9ᕑIr 8IְLcC^qv\B GUbMrfV9[ ^!˅=ą;V|f{ٺ]c?/Lok7¡ # ]٭.Iaz%. hHJrWr`Z;ͳy xA an,D(˞!TY=gx)5OA s`o +p1Ez++'|ĮL{3CvyN7%ˍQ5|C^{^hoh~dϫsxYS7d^tX@`A4 &Jd`cü`jhoMB@◙3Rgu%6vv vnP (gO wpwK*){v X䒮Z5]=__$(ѧf #VW0X@)Gt%WTG.$85+b\0:sN}o*hloe1ϩ0F"T__oHW](j6ޑܰ|9^,xav}lt9@ <9[d/}8ıUS 3:t>4 ]rFpr+>x`+OliAEe`0t5abH __v'7Qׯ $;Y!&v\ fdFy[רHaNy6و_u4;WQvE_򐀡,wDt b"7**; m)欱6zH݄"Tfwu^jYMώgPM7C6KP<l4:DX3?JFX˥h <U$Ι8 COPHe9rz=ש/`7ɍ-E[K3.ό> 6M1A h0yla!wjmM}*)S8"A="e+$4w pcD6:+UCuӆ"-,] \]5g 5far\`RvH򑴃1y:GO^[>;GƦe` q|%G C$U)^ jᗣV0w="y,@>k7w]# Iq5$͗rL8W&|lXq9/eM^BUx?)}P2%Wq#]9 @%{ >.90', gV=٩bgcH[zwf-OB^-63;2BϞ|P;w}&p |! zj&᛭Ξ ˑ-u4qf?]h vv|ݺuu^}x&jcٻowwyW}`1V[iߋcz  \pX Z޶EzT$N u4W%s &FXؓE>{:H12j⨌{eW~kbC?UUJgֶ"~8ǎ]Wxl93/IPTm69 ۔_`.'=;VE0E:J]T 57]'\qi.CBP ~z~s&bL3JL6FfSwsڥD@赪C!bh}Dro4Tl˕O51..5C$!u?'N&٭&u?C=#~)~AV7]}~?ŏ Jy`K 6?A 0aXsxB10B vFE::yaElb4I6ʴN _05LU\A|ʎXx;#E+yYq嗠O J NG ݜuxNůzq|v:yoXFR-: Fm .D laFܔ9io i\䣷W&1d*:IiGqRB]d@C 2mD: tCwEAߔwmx2%8J݌^k(]d#qx? 0]\MѴQ@aGZj֛q`m,;T Ix_:ț?!*M žḻ5+;omކ"ŋd+ܧ]7T*>DdBAݵDz{8WB(G8().z꧵ǧaק\TՒR<-bhOtSʭu:M(&GU 9pnrJ?L6\iM^s: E^k _@.)|=Ͷt9=)rY}N-ܼ\9Z_ᒭ7!o~Dq Mu}n5%o6]#Mw[˹?zrm#=x>D??;wg{g/ 9)K|rgI:7 *%Ԁ @j!BUxuIWVcVP9ijBWބFnK.8Am' 0jHK0vaFk8xIe0!Muj% Ukŧ+Oޅ__:aI.: ~G.'-I3FAbDxldbA+Kx{X|z ףӈ4t?H-Xhz@c9K`RaT0B[g[rq{ȀdO)]jY:v0"|ѕDɺMw|o D{͝uMfo6fї/|Fqٗ9}JD9Ugz\ؘ"ĹΝ*6p3sHĽwAuϺ? 'IppfS )rrE*V/ Un fbN!Ӥ#.O c;r˜ڄ]oqr2Gьu ' Sl'"3@S\MƠD2`Ń#܇Q k=P9&4ģi֚crd׭U rQ\ ?Hń&LpN9JnDf]dȚiC3#HbVVԦ Oi>bu{hj!i&ߣ, |&~/Xl=<t2&HoP%4&Xg0wggbnGȞۜK>"-SuCI* UlW 1UKNrƊaX-'jCQB0Є)hhwI1J:jp*!%O1rɒV=1I)"lݘkJY\}IR B7,aɦC!B\rt6ˌ 1w%5ЂX4UYz@[v[|7tGlKOxfƯiSuH]u뛃nu:~yf;*GCѩ4 uk U'Â6hkL&AN>緞<k61ExN O SߦvH>٬"5: nß=7X+5mZ#6 Gw6Db<;&F+03^|bcXmOQ lowقY54kܡlTWAGCN77f=8 oS,𰡴srffwqu lεP.QrD7} vڹşz@?}g)~c}n2FkX ~5}*_`f!+gO3yˋ[AO_֢-[\&m;v0wCݝg)~|u'`K_~sW F$Q*p~IK s1:0j.N /'Pr.M%:}eY (VjǏqB#@* 'K#'L=DŽOD;~zxLd]GiKMĢl܃V&iqUQ<"s)B*'F,R`ЏTlaj4an_4 C\:-7۾݃;xb8Oy$*WL+q -ZPb*XP`KdhTc7Ҫjc=38ѝJw8ԏCbdy 6 }Os'|[OM|߻gޙ0 {Sxߵwn?|}uK)% 4S3eZumExhѺT17yҵ5]^dsu [CN s3? %G]^1jM^9'Gi ܧROփ^$KwO% {RfrJ >~[q6[$-.qy)^Q2/y6e' iCMbE/ GEݺe NKbJ~}9_f:f Zր\ߑ~^ႸKp0H*&Hq\XmdlrNFtk)shLE:#xb6Ktr14䊇5쀔g2% @ 6uWip>Fcln rO0o[kq д73Ką2EW|REX6'6̎[oXK"tqCd;L@_[3;T8reI9K[wm} hjC#Z؆PѤ$l )-G\TJ$TSMϟܘ_c_k`X!@.'պ&b"f#LvA͞!p-BQ]El| ȶ܇UiA,PZr2Nk1U0ްGeƙ,sgnGEl3c嵲Uh0"WU&]v<'kaz;V^;5ȅE4/=9[x++qVPKTz*zZi.fAIaB ov??OL; 7QVZG$#;a!\tDpf.T^?!̹VR41 'JY4I*Fm.A'\8PT7Bubѯ (]UQPBngBYA$,`v==,'HiD^ 댩ܤ̢hOӶW~:? <@Vv}}CE~nE9\rD` Er*S4@`bZ\\U߰dCv~yYU`$?76 Ze|U)=0}owa†1d7P8ԣb]ta+CyLH}M[)&pUn#ňPb=kh9"逾;"^U˦B.e6VeifA{:I1L@sZ,20^]-\jeN>lUFr2e% "ړ|^lؾ!-dYx]a`Os|'Bݺ5'0{p9Ǒ«$:2v&"^" BB)imCsΆݺEאM aGA@iŐN/Tth@@6u׎;X!$K'r&l.xx5lܐZ`h^ש}T[x5to_n<,B+F _e1iеm}opkVŔK&]Q#$-"͆vڠ҆Gt~h !p kb0Ȝ)4n(Q;7of2Y_}ḤWuUF 䪾ߠ&]m5C_" xg"ks΢GSڛ l[ݍY Zyjuʝid%)Q_FxشPjt-*:!TQ4R*Z&>ؘG?~4|z089ξ#Df@</ӽDs_نʚT_xD tPm^t[ș"q~󣣗Ǐ;"ꘃ໏S{ޥso|=ڝZpMc2UѴmM#KJT[r6j/Ny6gďnB]st2T6qy¤R0oPr6{˯1pȼG7[jBwB,vf8/:fG|š[cqfIy`%Jp.6\=es`aQQt'HTѩ^dELa>McHp FkSN&6w9j^=exwg%e,xuՄ8n1?pO%6-#neeF\Xٕds4n^xrY++fSGwP:XҦYBںYPW T 18 Ŷm!Vů֐7yR5: ўggԲ<e ƧC]+c\qwO1bdN,l!P0MWJuEӚI+7ϯ8 +Z2..piՖ՗ŗԁ_4RΫ`zeZ)LB޵gj9u]$_P̄c,8^SrNb+!@袃R#v4lg_NsՔi*'JN1634H;B3Ay8B^^`(Ю( !gj`dH@F f`hVC؊T 5%cZEO oay.\à1l"wm#x+YgexwtFutb0y5p&RO߹Ung){7{|+!H#u["X`;:e.c ]1ݽ9|0U2e[$?3BO"5]ĞQBuǶ2{5O)هx w?]8;{qjguoA|=oMܽTb:HO gY%$ᕑh~F"Z~i3a6RJ5d/x5s%p8cM"e( pm [;wy 9)?e/A, OQJ9+U@' 1blZ.)X 8*pW8ֹyO3#2J3A@Q i)zyS(eɇZ`(mA؎OO7y)TnɊl}d JUuG1?rU!*QthIa곬I1S_,f%h[*8vKƒoAArpUқ=y!olƷ'ijBvO#=Y.peABrv.ѵ{I#&̪1Պn_mSfS^$AI뇢,dH2_p)Z2DN-[]ؘe6WgaGeoC5C.Y ZrGL L-M.+3Kl_" S`NKyNA G0By$X$<5zbKpl9KDʳ TQչo\ypRZ; ݿ~]藬Qj~H!:n*6/{[l!trfVuaHv 0Ȥ]ɕ~rvIޱ&^k4[#Zh(5z怾V/tdufhsgqJwts+x< s:y:aZ3>[1SY5;ʯXi <@y"1hdu*ϋDy6`¶m<3Ԉ3By.L0>GP#;Jf0wsz/z轣 =WEcF)75YpoƥqOn3$+o-@E Oa]} u5JZzEsyسWf7%7GPP!a~v םx/^?>~y|OT5 ^eP1n!Vۘ-Ә]$sΝc٦^X`^[ʺڹknۯ鴠skM6^Z s]j>:Փ'=k]~?d99 cqNjZ1^1?VjRiEiQc*SfGvsA7MT;؏(F^g>ͧJT8c3`AU4+d*kb8*ˎ"R`IocQž,|{$tE~i V Xv lf91 ~YaXaU&%)ITqTK^jѱWJqQЄ'汞\D@̓B7)@502O I@5}Uk}͘!UFYKc^`O=O8 #:l=e]?J1T =7٢0;cO)w JN>yfW"`gQG-H<H(K~jK~+zHƭ*"Fhf{PL''kzHrPJ0Рh9]}LAg`Yv.|1Lgu03 S;g6(ML QxS5_R{zY/**Af=d6t?P |4ЄՅПa@lsGKt%4i[E*;6*kSW}e.+I;}6fأ;~ۘ6c1Cˈ_Z4~(JrX%]*'E٫/;ڬ*YͨJň-8 JZ>jXY,C YA8`h_O7,m4o%w^üY29Y.33nkENϧeP~#dS'U"0D?NK&4zi_l<{jMSŴ7#x&zꂱm20aGAű+P|q[yĠt6 5cw@GEHtw.r/~^]J'HUЕF@"nBnf>Qn Ank!~ڶ*A}Y˩]f !(6Vuۂyۢy <~Ks|_>3!k ɫIovU"TٿƷZF 1KuJj27zD,fx!9Dt(;BSW95zFWtr!7NREI/Y 1:I%}f͌Kʱ<+E*[ R.u!a@oqՍ8T4j*NrZ/KeG/n883,5f F nDԮVTkCjj_ښ4,vʥ6)f;L!ɕM&;%cwRXN|fW:1"m W=JA%TWA7+iݕ̼cݴn<+5p%IA;]i_R*dJ[XfW*wxjd9s? _SQvcݐۻ ltj.҉ |p>iXVBJ:d&PFdI/QQ"2 #AȆ TD/f,Q)D>0270(X, 2` 301L uֳŅ/` O尹Y,QM xsF*γEo 5 50a9@'UGǴ fV0t=7uz؝BQw:D$^T@: Ϯ8#xMVNG:~x`U~bp*5[RB n,E\챊-4&>Bo^C5 R37-Q -6l J8&;4},p"WT?Ubsy_xu(#"/P, G9"P3$b[jBpy6ݩF'zO&0*kr`JnXVc݀ ,Quz* Y2IsקSa_ ! 8fExNcG.Ҹxа5H;d&{d&_wo8`` L)6(㈏/f3WX08yO,7h*zk>Jl`z=nJNz҂3<%EOEfȋQk/Oi+e90ae,xlOxQk2/t0Ŝ`3U(alvhsf$z|m:Ya$>|3s",zm5VTC_dpD5az`w0?%/mH)!C @2 vÌ VQ M򈏁{,|F![DlkrFsHl;*٢VP.|0]U\ǔWx"`w9uɪYI;ݘc6x(G_w3/eQ0sKj!ݢ!WϨ8XB,^n>'*Vc¸?1fSS"]`]>4J<FrX1$4blG{od7gvi {]'WM4{U:YD }7ܐߡcC\wPâ#fO`wyǢ 봋Wi:rU ZRC>3kin ~EoD nЅx\m)(K~z$t͊B*DdddK|73UfV>ڵ^)lgS$P{ĀŨԈ1qeD7yCҩ_; Q).@?*O#.,_VFI^Hɢ*R+7M!Œ`XiY6HίE Q]2GkxdF<[wG^\ge@bĻ O+8| C?Hd''KzUI\$3DܣO}k3pamxN sv; P A\eWފK4y/'z2`kMpB=6ai>UkςCn'?O ]_s}_윏kOҴFD|YqffgS7fN ai%LYWyi?'XR(Z*T o~Kߧ7 z%DCx֚c@Fɳ20* q}QE|bؼ0g#x%VY#;M|mL8P\]%J>> AnkEOg/|'  r"Ap|Tv).PS|t&uHwT+ ̗S jz!YE|D$1PNɛTuZ'[NRĩ$J[܎8ݶWI[o&x>$!cLi1( 9P];Oj2gzYzr J_',2->ֈwܿ y67460PT["ƣguȃYkO!{:0?u*IXYX@Sc&1R@/ǯ:%%Qf}4†msju}(侥W@m6EqNp1wNA28 =wmQ VJxW\E2_bzNmcӯ4 RiV+ss ?##${Ǔ%h9"~TÇNfZ/|ʁef!d߻v3N N N!U~>[ oLKZ'>̲PweE&:囄MyeI`j?Be+Ҿo|?WXu!Wn$uCXO/*@;>tQuX)ϲL RI;!뢧Q]I(%Z0V {OjGKtDUV7dG>v|t]p=HCƬ©fR&0nhƒB}1UJȠ/s9)βV>=lC8E:n bI TyioX88;TDHhǮ_ÄP1ʍ;o||Z;4˵<0{]ݎ>~n3[#e [r S qq1[QAFo;$^m9!Vm1k` 4ޕGvweČ.Ua)q@7|}C\j+S2/Dڍ? o"(80OlEЫ6|:/^V u֚9($y240CKsK ;I JcA"'ԁQZؒ4E"r,>a) _ MlHw4g1浌/aŦf?AWnwxoc{J\sہ0j&?m_k.?ZVWgpY55~(JxmuT9(FVdQ;&q4fQ25B"=tVVܦ+DnP2VgHҋR%\h-% D r)gJiNh 3f,[Ф P:%|3~. }t9Ml7|/Ptxpn-ԶgUC $obL#S,[B8".GL3soٙpOo0lI^>V)qx:22g?th=>{?~=~qGSM LX*.Z$jo ~Y\Gnt0|W'7֐\P _yDF0Cúu!o>>~D#()sV1≭R+iS#k42^cw?x]c)W+wpGqY#^;!E/e??gM8=%0&6;/mݻo߿`0C!V,I-ʭzN7A{]2 {;Ĭ7vHwv%gRJϒٝV2'/QP3 H-8_p8B!Rp^^qz Y64JǖlIҫ4L e@Va\gh\{6quTb0brM%ݏߓi2%G1!h=û].n7IeTdEL]Q Pn!TL@KxS?= )`|78|JG1,@A2잆jcui)!yЮ h␳k(U3YfkEڧRg|\{(`˶=HL@Iy>]kI=e);nbrL{VO 5gl:aA A,D}X8<*E#J& JKPJ|;aSQ[kmXX9-b5%HSZ6QhV*&H^2^ IN_ma=g rKA%TH{Pq)$mP9rJ9 {sLbDQ!:4)YYw;S!; I7q[B?|T[茦Z(Ux`.EUґٴ3'k48m$ W Px&ޚddgo01s4gb9o~jK l޶qb7!qWQ5I_~%CXa97j+z9 ewLE ^MCA2.]YA6̴+_~ׇIXT}\I;[օ#s`ߌfq?"U"[ 矏ta+1}`+s]w?'f뎼ݯ_ ~yP pK~ZXʦK!ƖjT ȾFnĮF 5_.ϼ*\ˏ[ʌa:7'Q{%r( 0Ygt#o Uc~ß+Y+{77gDd" 2z{pޠ2h P} D$}ɢ>.ғZGwi>|%%J)INkHRxÕoq1Œ&R$(lmӌtܣs@Ġ.CD B8.~bz.]Ȭ|D3po>E7\,E6'\%QM1ĬqnUKbEi,aD>R /3t$p~yHkN8zteH>6SȒ TP5;uDbl\AٲfEi%0$!h#Y%`)\Ag`] uh:R]0L w!c,Wg}/$Yy$d M̰@t r-鴜+7h %<(Dx|feRE ~i@ ұ+k?VHteG&@~WMo;(m ~sjcڙ 4&::-[n;QJz pǑĿާTL5 )qC຋I9K[<6+¯ZSCWO:@5Oɶ@m]SWh 8 ;u:[Tem_6S1kPHuh7k8u XF϶o t۫{`oj/j~/k֟m];`$?$FYo~v/}pw=ww|4l 6LytYigyL9MyDl4!.w9ێgq9ݙ{h(Wx:0ߏx߹sK_M8+l &|,wϺ?w)QGO^H: LM_2N``{o-C_7e^Ä3,EHe89+G =s]k[[]Z>Y|ӹv՝dӳ9M7[ G_޿/]|>Y{ϏGIZ"W-l'Qşb[D5in;RPk-/ r1Tj-@grV늀{hS^.SnݞI3E``.luQu{y/ D0z1+pL S &(Xϱ0]Ӯ JPYU)<U?x2=b=KWъi&jir_m=s {K\fOx_z:cKʽwI]آܑ+RߴIeHFVe%|>PR泇& @^ZT0|CU 4j18ϋyVǽ$9LIҸHGĺ΋+A r!~OP3DW}nZsїW TѴ;J^uQWεft31iUpkܘ?ϗS1jri.`4ۉrQlHlk 13am-2j~ .#Lf7.*OGf`xͿjM֎F4lF*BGO^nkzggf#!>'o.TC+_I.UD{VM9Bs" )Yk]Ruc> &[/ՕNR.䧥+G_ivV,n7<=7H OujZxgYsypӦkND!WW_ s#u C# U! TJ"3bXy1Jb5R-a9bT0[ڕ˂XW&$D%t4,TÄD#> ]ۘ!h[v#I~(W|1*Mz2 ?[}Xkͦ*N)PO]]UDز3$qkX_N^iZLw!w߲M?l,~+e+5cz#nk\f}okom=EYL=Hvۼz.r6+掦l?mL|Bx6݁FRDK 0.MڝE ]Zp"{#ud:Qu)|b_a'BIpXo.xF $/^ĀB*ѠCuJ.lPt bՖOt:jrgU,]Sʶ!/l+`?ؒJjէ\e@9&: al3ȷ/|'g_ GTog`(^ћ\YJ!r h?siOĎMljޑfcG K w䖘͚BC%~z,oKg FGNKfD6耀HXt-~Zs u+G|@YYt1^JB,ؓKKH]([wqH(cO|Ї*b 8 g"J:/=4cd)z5hlKԆy>?,f=4:D̜SV r67hYJm qGOlj}"ĆB @$ש-whcnInA(=:]xI- =_84Э뵡$Uh(0sϨ#s.)^ȱ>"Z>\︼Է!n9y$Z}F{<vI]f6:żEC 6 >)]piZ)#c7,H"NuE+CdqǙl pψ 1Ȫ!d;ժn4]A._\PVC.,;6Tu⚬rx9=+\mwApJEiR5 #,{eiJT @HQ$OC!QS)إ!\V Ss:# e yloX\t*_cEfs`Wԅ41#Q TpW._T;htJW*VL<-Ym+ 3 (i.5M>cB>t0׈)ΒiWe[|y+l6inO (4ϔsXBv)GБc3/9+fSs{1!9ɢ)Dƌkݲp}Pfި-5{NߏfZ@5KYKdafw֊-q{ʙgoXȀU#a2~Ҧd"1u9m.;>Fޟds+v?MDtYt8 +Tx 18*s+LN'o▰4<{+[r/~Š륔VhlzpP6\]oJ j2\ņbcUh0RUsYSFKd}\CV. AZTELÖh/SǼB}%y +S3ZO~\`0ߥeq]I9>kwXy%}("gf{R&KK4<`Ek{SAP5lmO9 :Hco`sKT>̹ nFWF2-ȇN $n"$,ؤ t4@OC .0F1!e:~.'z 'VHP1M)Ckr0Z!2$`M6CB3mQ2.-.h4(ZcBuL^ 9$l ]&n"- /ḇ}r2)Fo#BFJgAp$1~zC}Qspd1Dž#d,/H\5Fp|ߩb+&|PMYM ^V_ XJJJTdINPȳ8 #.,ePw Tc[rզR %rG-uL`~"G2埜l`3k7 ¼g~PN)Q]*{/|(1/4W%H}sh5JHsJ*tٖ- 60;sW4D-?7??}5|tѫ'/O=چ\gϢ?cf|p"]sEDEלA lІ)J?HʢM4Α.*,O|$j.:ȢKU\`񓩽Cy;𙔹{Fhk E>檖Y_Q Ѻ]\e n|Q%7_Էy oY|12OgYXX{lݽ] o#S4MXpa(KLWen`;;w.gIc2ڹe*$B@!Dͱ8Ʌx 5U(s^ay.PcTb V!,iE1R| =hl+[jNApl0n&[( ~Q@N6Axq1󃋹Tĕ#0, x-1n#XZ_x'@"W $>c&I:I6dN'~| v鹦hr.SxQ5CFMVz.C 5Ly Ԟ )^b#PЬ/}o8-} s 8^Qpr KLjJSpu Y㻤Do&0uL)Q&8!mf#yt Nm (vNͼB_D7%^{mA7`* TjK{~v- K3F[3xse˚#?9z㫣O|Ϟ>{z,nnfd{#$]Iᦰ`݆ݲ5/w/]/`wx ǧ磗4㑰/9%t7ag8&G=-oQ7 .wAud*j}gX2#wz*.)yK;\uJC\شr43]'E"K ~/gטw>}AV t"#*txXբ<'?=yq\mJ BwaX 9xAkV2Q>  _s6[7MF:ef-%M"m>$wf,eJܰP&:F =lU O͉:wۊcA33@ȕŽXp Qn9GTbf pmtUj¬4V2c uu+{Kmy ]L2_?^$ϞR>Te=zHWjx?;R=(" kᛲ'8i <|rǗH7o{{?~񳧊됴yy4K!;E8lףj"~maHWeQգT:AID!xDm-5_D(lQ(qyo> {$bE6d]6ϧĜFӚT{z $c>D"qT֐)ܔta{ RG㊫mjeX|,q3r9EsI1"ah'tHU'󄻕1{u{}9}@^zU#װezd:xAK eah~9x󣣗-ePPگ8nYZ VɔP$NrZd.f 1m/jvs/X\ƴpdn菟8~2y;ثNdWff3ѓW/LP7f+0V w΍̛7xáQρ)~4* ɡ*Gy';m95bَ!ƐԂrIh="13-v=jXtVt3S(C5\2.r֪\T %JkKiJ0.\5DADcୂ4'cjCOb:RF%n8Ĺ4xn98D'!p5B }y~B'5xH%Y{E_Yywq>`t\̘J悖%CF8 pW` ˨Q4wrV}YReT)[PE`euZ d 7dPx"즍Ճ#XpW5qt1  `FwOsDw9BYtAN#x<\MWQ$cJ>()e)ryb7H r$v~ Mn8CW -xiAsȸ8g:/JٰXטT_g5RRHpAVmUⱘ^AadL4 H9^jiz]!9 'nAy(173bKoֻyσ cc*i\[˒챵ܱW#*yfL:Sֵ47R%E KM 9)5}QGrJ#5"ޭd2XeM4i)n!1½Zr G|`DX WQ12KeYzr,u:k;[18>KA3lIXilE_cfQZ'*Cʏb]k*,D7`"'Jȵf3"j,Ź:jc&I4 %%rm5ʋZ|/0 KNYny1]`lcDal <~7*nq=ojN.DF3.kN%.^AQUt\Fd?[=U^TA {ҳx9A*dY&n^w%gI C 5H=|7s(;F|C.xOL&چvHpJR9lc~'2֐_ʟߣ6;? wo{ߟgO^o{ _o&{wZqqA1tP' VX)#5 hZ-~O[mo~0 {;5$!ٻ^_~k֟mw;A3?~7;tmvd?|s1 IJZqiu˲%lA͟KxDz1_P'&AC/0)lSsS~KZ|K1LZ"d}bP`=(= 1΅4A79ģܘ4-={ Fa@rJҳN|,T/gΕ9ψ7%c=}{NQvtW=7.qe6ZSnF㇯x~h|6vJ2- Fgzmߟii%B/Z)/MֶjӗTmf!V~}8&zC=~HiQ!Z\Fj8Z{엧P3. y6Uw6ؔG0PH|j1_͠2{ hVI';J.*}=4\'.iDZ iz!N"9pM|!0ic_ #ch/0!|2#8/3:sd:ηпT@ kYHBa ve *uAk CfMpߗ3iύ_dM* Ee4H:AC $WQSdOXTQgUԿm̲Q5ǵAz'Cϟ?{Yf^,Ԩ!ޘagy1o{`m*;󧏟x ]  j:盒#^drhح˼Ə7'YDsM ȥ82}1B%XE-C<=mk r|dkYӍ;KNc Z=]YŘ3)ޛZMi/N@;.Mz3ɎP \ ࣲgMx'ӻ!tLGMFdًz/8g:fBٸNzsF$q\BerU*Vy A5B֭Q"=:'uP5`#hp'-N?o ^JVf 9zU*^>W .L.  e3h|^f_@gbAq 1NxgnnBy!d97ųZם2X^,)딡.anbyř}31YY̤6#XD`t)xFHF-^=nA:~TmG M&Юl 1 ӈTEV(:Ŵu PSބ$BHZkG<;TNdxӓU&A0υ= -'= yCUSH &~2T;V6C5dROM["ӹl!INFK!W)J3a1[\ogk!(I%?tz;cQF.̐)>!![+u hZ 0szSBZ,g@B8xFHchC>.3J8&9.ri*y e1ٚlcA:H!>(8QlAiLCpȲaJ@Zv+(Wl* -ʯDiȯAe^퓱#ukcsw뻫xʴ#fX5.>tjQ/Ib]cK<szŘns ҔI"\4@ru0;AѵxfPf4GoFPQpi%B7 OG_5[kƐJwFj39o@,~ -^)Dʁ僋k9^+k~9̛}÷uCo`†^ .jnv$DE}t="fWS0[zq&tX^[TbgzQ/:h|9w- I;Š"35Saa9h=0%:yWH&WԐMY*N'!'-@R,@(@w_ƌ*: ݹNvqmSl b%Q].ƹ/GhofvD MU5g.8Ks>íe7}7O3Z"G}e FU =3 CDQ =0y1CUBFNcP]Su ]ݩ!4r0(LljyȻS-WIqaOe/D}컢 g5\PD3#K9)3ч niM9c粖nJ6$z.Ru;-]?&_ -+ k$_-q'o9)ةJ7;d/8/EG]#r ;8:1+_( ,nL6fWL'md7g  QCm)Fh ><Ƒl!Ř${?3,*qCѻ$L!"*!WSA.e);^2~4m :2HFRd^7>M'Յt${ֈdYST/U$DPgI'F"jAeم" H9jX7BX5f+"H9B~х-9j|#SD3zf\H$}B䒌$ad m+㽨 ؓ *Gʪ\~3r:s, e»ةwH`'1*tDau[v<4kvC͗@*NFV*fi‹P\Os`&@}ӿbЭsWղEݶ珨,u%%@ G |HH+=;|rѐOs7Ha!A>*jRZ5+s iSE9lMJ˅c$26ˁ#V:,BWp]~LUN)s;Jb'SD#UL;M?V CY5 .H!8TdB.ʳEi?hEmGU qÂt*.PUB tkc5EMOb8]1|$t=0C{ƨTDŽ X9= cnH.Ndd="kܑ@EUu a,c"K;Qh$ڥ5b6g$br^! Jr$E-fs yk^"\}'M%1ce FB;SbbE%IU /[],1'rG}jv6OQғDnc r[ݢ%f26xlS~@5jōwX=_ka tnug {kMho:jz$Cq@QR5~ik:$)'[:_C&qa%*X$ֵZrk}8RrA(:)2 :3#0ߊZ#mf2:I5><\5uٜA0 MJ)/'"@JU%xQusFӸSQ3Hr#| Zw8+*H  ݈BD芶t\-Sd\MmsEco>zȱF K~^2R!Op="Cqt91.Hgei=QJt!ު "^Qaެf D̠\V=fYֽiZf}P T*C,6{4p +`xpjչ˴aLuh2a AQS (<)Lu:q2S 6-]W==)Ny2R8_Щ{[G ;_FHm[_/8W$;^;.Q%m:7w6#-V -]0}(x%pVavp922NR,{]'5*+WʗyK@{*%y!瘆m96؅67U^3,y~q*O>\!ͼ.SC^XQ4%..7)nw?UʩRuвxbN4W# Z5[ d2#M*67r4'k56Vvwa]w}|U?w{;P|lݯcS.Ծ CThsǂtTzXϳtʅIDIHBSIP J24ε*z mky.+F عuApCGnSAp%|qNE':{Q'6W 9}_3psW%2\'P_@W$kOr[׾{?ʘUd"w8:sO*E'xE)cSیMIDf|::Q 1Řυ Rrhz1'%|웫,FZ%#KSWRX{5ֱE^1%-槙$_ww 뽽`;} ~*v/s@q}=qa;Ca+;H bg[eѓϡ$T=#+SV'EjʢK_3}SW 7tEӽ\Z؁`8hbU_9Dts/oc+1p,Ӄ&:o sSb4Z3}Rr4 R-#9Vޞ!Ai]I;h|<?/VO+LZe}5tJ=6hmWݤ7./n/p$a×]mB\?뺍Oucxqۙv, Z[|ޠU6ƈ7}0o,L,g g0ܹkk-yS6IL p'kL40=VI~6] Ж@EeTL 3_~sC_?tշ99V]kr$=rT JzCwKKVg#MܿVۻwϰ>OTEhwN_ 'yn}A>'Hرa  v d%q&qFdStu!Hk(. ,E`LwvJAzeB8$02a$d>E=@o!zٻ3V,8p&4N p{Xd+F*GWapBˈ+8g^t:+Q`CmS-Nǭq1~#c&es+.` rTr"X6u­hOXz}z7,"蔤QF|:Ck+x\lt6phkզax 鄓@~HcM&+!99=kS}מSDhH ~Rۀ'*'Ӊ$Áj#1Ś[c얷LF..3;<ĕ$m6{%U?j+Ë$ '!57_oxQ)7#95}e67LQ u)#҉Fvs} %Nynᤪ|Qf iY2'[V8xR{ǾL= .C?.Օn@XfVR-_ɩ|,|]:?O&@Ϥ 0 $snv*$p\8 {,4RZkU2Kد`nn=7s9c,kc\VM'OȆ>BA M|U'h"+{1T@$Ŕ3Z`|ڜ){6\UB4B'Wiaf]W QYMH} wͻ {RQ嵬ߝ47}OULJQtqCMjw׮o^~ pjzvW6}]uj\~D𑎟6C+̭t+@&]\֋͐\fk'+[vhXՆ"&1Jfh">n|!~k$ۻ҉rgd]17jJE>fIϖr\J:yb9M࣫!ONM 9#t ɠg6". y:o!f G@!`; D;@盞'el Q>-u^:$Ɇ"b%)۪<\(B'0P q{TlvD(7jrXjH6An _HB<$g|fd.D3:t %Bp廸' ڈ.PφfjG_:z|h/[Dd$S1WvW^JZh T!eO\UfGF8qHR^֠r}̦cl*i/08AY:Wp0~GlvmB1s;oҿBF&Cm%Pwu+HIMdBr%EYUd YRrF>5oAX.AQN!QwI~|Ex&tE2[ )HS' +){fPM'cѮa _s7`>hq-3d0]A.>\ڗ@jӓ&_'Qfnq4[\a W)\0&ug5H/=n9j u%YDP>s1#]6_EikD0=[C $ Y D”4HBykE8l3]*>&%#q/O qI q [=|%ȋ+e>67V^RH 5hWjlo$FEzλ&34 5 BRD"*cKqI(FTP!PJR2ÂmIeId'd I1$, 5D=I:<QOX>0JRcqAuᅌɂqg)G;HS"v}#mIٵJ,~-@6<#^L#HQåBSo" bM[.9dMV;t} B)󈛽sx)* q2h 1[2Rp 2d8nA7)tBb&60ᆚhkkDeQ.VS_@$*LTE*/ST m:ϋeY%$д i>vkK{XNQ zaS %0{Mg+5Vl**+sAvi^hlL8\Z*nҼMXV>ǼsAn=&b8O 16x PQCmp#eaHp̒Er-:Bz%OhBH,߅!L~D3B~ePޚ#(^E%$EZBH&S4tu=j3T Sݔ,eYtvњ '0?NA6 }Ab;f ;\;8cjXhYșlVSɄVBZ 1(0bDh<2cִ `fvI>16L+0#b!4IeG!{kV,CF3{[<8B:2 < PMaq5 Uu.YRIь%IC6zJ#s>500t0N1y^ 9ʊVɞvT+Y8?N8"zOa: zq7w=okZ@6WQPyr<@IƮ,D@: bI%^+rDuUO_'[R2;OY(;HwЪXQcZHH]r%U2cƇ*xhGcMC 8J|O=`m ]oW1x ~}! ].^Ui:5;mL2m[f xraqLt+F?[]2X7+_^*+NtJHov [oq;,u,=[{ c{g@PYNS o[6d{Pe (oklGf\ ?p St e&#R۝~U;W71mG6 F kes- )pa}Z~|GivrAwEѥ\(?Ebz^5ܴȨOrd>Uf;Nʄ sTD:;q+ׇ.y(h:DP lm*;}/'l"4sk%P4*2NR>qyZlhUio6pwi" %gGmh'Q>5,%LoWJz$kh? <β7Ǣ &+ jئ~]pH7tAq6!'`3ĭ"ɿǪs )|E8ݐO1*sX}*[pڴCM-,\ݺ K)nOXG]RMb)GIRCHҚmuPSX.# s0uœE|'u*u]J!]2Q%:OSCf 'rBRٯdNJ92d(+l֫XWG qy tP E6gR }uEKy\v(D\3WĹbzנBO¥hO<'vkLi%Cz ; I57Uxj'E'dM|URO@(d5= S rC޾,joe /ÛX~puKu&ugXLv'2Tųٸə[3i?f`y MI}q?Ҫ|U5X}TKj*_tڝS}pVڀ a_qe:(TJ֊;CT1xOrGnuM齤Uf!7F3}hts>'Y]]Fifyڍ:hbRP?Ol2p3ʊ52f0Qiǟ)J?ƆC4w5B(n1A80A|DŽ*3zr'- |}$/(ȧ5_\gRTE^WVz}WR\&k$Mtb.3,"ca]9c\ΉYs׸Y6ֻa֪8f5|Hs+&i|MۼC ",i V*.@shClM;t)E,KXͰÍ} [QUEcq 0OM^G6 +#WͅYx{HW`*vV8}~ƾ`yNB]Ug]95M'e1Y.2 E[]]OmHPylpY` )ڧD ȼ0^7rýBƱ+oE8Mnc9[۪?r(dž)N:7DA-YĒεזVҤ3>_ci$Ϗlon[Q9K"U͝U֓?@apƅ/^e{a KtpD;95ZG)Z z3Ȟ 4W7}"qyDqm%UEM\n{Dbsòz0DO1Z {9OHQ\ uSփSMAΩlCꅍi$V|_ڮq7ncI[@1xJ*/,͎M)nm]Q !){Hc7hQsg %}꫇U$4CsC]Yau/~5>gUX?A`)~f|ܟlOJDQͻ*\zlkbzwl@/?{zѺQ?BxΝCsc|VT3i pfna2uK7;[w~c f[u͜{{;w?ovّܓolۃ`w`rͦkJGϤOJ5A9+pO9,_*X8|`D䧐\ݱI,ۗXY1rkd\ۛ1/+gLxWШa#ֱY(:5l D]R(&8GX&IeG~61{jl:C8ӿ>;ꍶT,,n>p%Bj Dcp*ᨾFSL Y л(@.)L'Rs^+YEaFи$O}DL%z2`=N%e L%?s7sR%,p@y !&H.{IC]J;tLRpvI6vCR;*#[Ge 1/>_,fCx$3_*Ɍ|w3tsRUdZZc4h0;}C?;w\O6~'Rle\YGѓ'_^>g?n2r..P] .?u{,ۨ9~d?q]v[.8QŖ%vXҲK/ÖC qN4]62st$?z'ÇϞ?~dT%gɮg&/:Dq !y|}Ֆ/3 (Gq[s&(Cm 5ur*4\MdHl8!2rɂJccIqA,͠0 (Iȗ". NT"?]_:!W@1igB8M@)`чRD"}mKm&ސf~p%Orl04Nx`0)"oa8|X:3w'~OO_M8׺H~ žtaE`:JEp$fhPx-I:=[ZN~;Yxu+X! $gI 06tRO6:uUg$llxM)g&'jوU ;Zh^ųVf8vC LgDHm;{%_@ʶ_kOvzA@$e1*D!)8uf%`Wl<f\9{o0=< uRK #$_8AZ V'&^8c7+CJdNEu&0kQ)QV!E]qMs=δ|7:7hdf [}CUJA弼rHlj ,j-\gnoC4>3 ﭯ%e< 7?__*WI_)"{gGk2W>"ҨϏ8IV"Y"}y*J'l5s8&c 7k8d dVlw`5:B6 7v6YquG}f847h5{l5*z,4虥gfMo_ců7Zk#- \+;/[l toE9?ʾ/t]8_8{ZcQ:]>b]YͶoPG-cVYt Ъh6Fh ˜f?Ǹ}@og^gݲ3P_<6u`p`oZ`EsQ{9$ڢ@Zl2TT@Yٰ͢PU*㦶i$l];֡ʪkhB#φV\S4MA{b9F?^9>zMky tu(xG fgK8~KAlzUŪ'̎ 6>B Y]̺06@nQO:ֈ^l :;A*uocz6 6@NzoַfZc:+Π뙡uG쵿ݞ=h{Z!}ߴ4^[׍K T/ADê4` +$՜aC62]oh`XoW~=ӌO.E8Ϻϟ~- Ǖ_5q݈pbi|Oj3Fxs o%*]]Rbi0sE&wfƿjI MH_cj*"8!_?|P ϊJm)3:/&ф12D]P:[=K@u a_Jk ._l-Hҳ2WUDr"RsJipL4'HTh Zw'ʨ)SsjSSpӗԐr-cW^ײ(YΆdOh}D+z??m~ڌl6FՂ[dSem] F{*vzh;ѩݻHtk32-PelLPʓu3(w .>n!Φ˔LKǫ|K$],Hz~$atvu tr$oYj+r>l|?G񦽿%k{^4־rLyl눱Y03BՖ pçxwc_N ڭ6!>3lnoֽFꮷmwc e(0v#j$n|G;hUp^;D_<"8TS[n,+AF36lq 6j] {8Bg)VO McEj%&HlpvU/U]+,cpC ,AJfQ4Mg!&t9F|ݛv2>y8cLQۭ :h\žaz `莺t=?A8FS#c⢈"1؆P4ۈ.[E= (f/ 2nL7 [ =Rqí{_# 6eUQYy/mPTϾg2,CҶ|ѩd\^iR",IJR!TAkk:Rө_FCôlOkxM5.0)K5QIfְPy9bgD伢bi-H[kQN!ˉxoʟWJkLeMx)G?n`Y,S/!ս@ a[cnBZL!n TYweP"OOV}SQO"T-+?5hle&r5/T~H;`85k|p9wYg #ֱ-*Ț#%Iٴ)Q,U8AaHAFP=]qDgEf`p43*( *4<)|~amnѪ]fj0zpޮB_SkƝWQe/X܇0( @ x#6Kax٥Q]eUSˏq jLCYCw!ѓS?X#{)J!g̅6;R"a~aE y "0NLɛ[-LדudNHLod$L&7ˮ e4 Bҏz.띃=*_`E'piD(ݽj7fNHm\V3Kff<%!۹eX1̍@BP>Y7cyz#N1M#O! +_8{x#ì$2/.LyLTUX~=@> 'e$x$nbT[ԛU;uU-?^POR r48GۻFC| |i XtZnCVss'.(PcCotۙ!rR`Jn4Ih7F="$T$DEQ^F?/+jQ0^g4G'i+ گ{ =DF[VзhOzX,J>@i,IAgT >Y.&rƒ!q(,|<"Vbpgp:-1Izڰl| z]Ϧ!= Vw}*LRÊv |vA[M'Z. AV9;m8+N†Z& ftxfld6 !S>CK Ԧ[Dz.QG0g1ʄ|\7\ *ɜ)px(h"\ҿsa[ȟIJ1^.=5-3~gC-az[ Y;iM?q0񰨔x 9^v~¸2:,A'91ziO7w8yEsӀmۅ-+{B OV*g5+.ʊ-!~dl!"72lFA? 0|$|Oj-Hct7fOk~:BzA ^tMn-ԭ">pl˓H!O4w|)ET5cdL⸲3rԅ7.?=h v9{|r zO17g2'!d |fuq BKQV-"/5~tҡ>TjWys[%*YF:[xtvc+'3yӽ[Ӗ; 0|΋i IrK>KIoY9d}~I? 'wH\F2=rbdcCGx¾9޹C F%A8e}LA >'> [:hR؆JZ<?0x h!IKa.ZێLBB㼳b+((%79f-9 )֣ D> "O O4?0 vЎΜ*7 z5`l9`sr*G5hkؙ)U{^ a>˭ yT4 p_o7(osc C|rתX 6{0@ZekyDRW|_7Bc \Ev͕ÊZZ3=>yroͦbnlcv6e8)pphބ1x' ܴ^ )p|w ׼:C{g $EOGO ?<;Ew==*!B;~T'!$eG7KQex3=4A޸L]ߟ@z@db3*y.#4?m[i'FSgҢ))Jnk:ہGh򵴰Sƍ;KjWMJ~^ 6HJ>E#t6d/Rs_*<[VJtJYq0:8!9HM/$3J9oi蔍?ye g?c\Z!QeyA'lw5! :63ԯV>c|w1C@c yC3ܐ/57FqDx|iCtm؄mxV(BN$ j-"A,$ Xn_`R 2>ys9N:?g; 2Ae`*[c*&{{_%ր'q䡈%ss}[n,]9(g a&n_Z}Փ 6o w$gnk 31 ueAcVha  LfW8$<.?Y]X4LƘEf̯OƲݐnt&1|*lÅJWȗ^Bɱ"Qhsk Y y鏒\R]Y P[V$ 'laTXFqJF2 R߲L?&Y=&;˄x=24juCn@[7 'CY' V0c f: 6,ޠ>V|e cVXC3K)`DOYܢ4BEsIy U:W1!lzń`bwM̋31F^'3,Z,wY-l"^~[&dH=eSdB2_nI5+m93qvhF\eTvArK5'?hud5-؆^P/$TpL%fxيnLvOiV!Ay6ݲ\O1C;ƞ|m=FfDEg晑":ٌi#,&ın2US 6|>+'҄r-'v}ʯdx͔RRǂeP̐4w#ءUTEm31J0DbS:ᚂQ*U4MV]UDNfGv~ W.Y9Լ`;hnXYu08 ;Z_ߺʢSgaU k&sESV̞g-csrrF_ kOШyOv~U TxD )S-N:!'g_EFO{"#D. n=8`mmT =ۺH5m_ $[s'#/[`o *f\ȺEɢ2eqE9ScB-PQp +/grB1"mtn%Jhw{OBd ?}䀑B\uEb7b/&bR}ee>]X rs&Q{ލ;0=ז@=]Ξ ~5w(_ڜL7YB ]9Pv;LJlרoGؙ}8cf[M4CvvyiUU%ڃ?ΑY/&LϹ(YҜWmIj78 ~@-0vplpSj73"`:r6I5jjؼ_z]:I,E]x¼-8j+Y6kMUDQ@ z\C/i͔ť:،Z*s fjt=`Ct&J7ᖇC|vzO?J F3f > ZQ | 8k7depV%fsR -'Иq4zKY*(4ڒӇ^FZC%ڳv\KƅͫK[:/5y'3\U- $k-tOesXbУKЭk!FVc hn-ճa%5ZC`kbf*Yg9~{: NN@G!'&]63?d56KU] pRޯLc0JZ|X!`6 V%Q(F!"L`z6±~1> $6!1r/ MRiV68蔯Tn#(5pߟF1_ ^u8-G2H4lLU /atd~ 8v(~8$g=6U5 ~ϱ5*m#m"b6tWпA=PXBrkP5Ra0!`j:$QuqTMi6߉F!kx0d !iPR2w#XFҢIدșMJXXa˭ODPjFi` el ᬴5d`&``t;P%Na>#5i(d & KM  Y1m`_W)Ei@dxd5 8 XxU 5%6XJ.ِDs2u_Gc5~Ee~30Vw+: +{eZ Sp3%+1'=-/$N!pFYxXZ6)4DT? &wRb^KEdDY:?MRoxKI?B;q;I'~qJZRG=վzK\fE;>Dkh RfTI;B#:qQ1eјxs 6M-"q$?z(-!g0@FZѤ+4swv<ħA3= F=׻ LZ@DjHc6=zy)33`:*_~YH5/3Km&eQ#kc~ |%X0'1c Sesʼn/t#00P9>}]UƶNqL|Š4IE@45w JoM*zq,¿M4!E4!M5!U5S Jc7M(lRɀ>s3PLz:!'cȭoOϮS}IqcBko@a-h=09n\759՗F ߖ5/Z /@c [~R=\.2:.+,*Rթ]22c3_߯=B-9zLXrg&D6&U!qbnyq k:lfF: ,_8Bx%ϓ5sx_DukG󱆒И:KbdH5xY #oR3=7;} X_%ΠJA qئTUL=NõPdkB5g.G<'3u5r,za ?$[n~c>ҭP`|'^Ⱦԧ`(ޔD>X,*: t>oTSXMβL8)T7c!%'-+lEM34~=G^ԸsnݖdRsJossfGb涙\zR9 dy>j1TFXS3V^luwf-ҹ>{Pڏ*RQ ߕf1d? lJ"'nrֽ;DbM)3Nrֹ#7,a⩉[$[.?vW@ Gr3bOծyqNeZ儻fo[j毻-IB"Kb!=#v>(?9ҌUbނL]G7el'jSأ Ȧ*BP |A͞(ޯN=?q>zzxw>;~wno:?9p<` GkX\:CS@'{OܛR?SZ|f^D N?&]c<8nCV 5S?/_psծJaņ'uzKqlqbC;r0q ([HeaAbY+QRTj[?F⡩tL iKtTOXbLq*9 odc?)51ܷWU,MmR/ImtX'[P-&Y㳈~/\N܊uBrTK+V+VxmӰr!xtda1ۉx$D6ҧx~g=.x$o4g|603 S!{uK 64Z(Ty7(:aPcpF|CUqHZ؊Ɛ9 W仫Ҧ/(#Iz'>pO:2؇yDTܔpg [; Rߍͩ.^yrRR(H< M9jRbHӪ*R㓓g0\$HǚMXNnɉ9"e fƑj;iN @ksTZ`Rڍ^]q( >Dh'ᴗ8yZ>?:H+.ka݅"&,MUoY0л0Yd#ӲMqt4U[ΝY7 Ka4 hی/p눆*QETh81;T^FW1GF}u!j\Q~/oHe!2g@\޶Ikcr&CHNKѹO9rV :Rµm,-æʪ&pbWD7Nx{ Xay I,1ZpҒ 4)j6η\Uo-q¡'Z'uW3z^ѻCW g=X ;_Vb6[.sowͳR/N)CќvJ-/%dlԬq W ڞNWf]jZivM-ZW' S;'>˝oKDK4|3 >{V,8Zl?k񈑊xaz6:1ob48p" D;G!.>>9Z;|zdՀx;: 1mZD'$ "Ŏ4l σ8U* _%$…R]Hݓ@;/l@:9c~@`(]YEИL aoKR^|?*m'++Kʗʋ/^VU/+5CdX#؎ "q1 +<Q_6 s\<U}^4_r_׶nv L".GCEF9 |6> rB<^TUcxIzMl!#|8敯: =+Tjsӈ;qh^)SU#3KxGYYV3|@>4bd_zء1#l]"4_e5?V\⡓65Q&бr67x9gfqR3ٖnAL(ͫY>Bl>;cK Q2.M &.)rh ͤO!(d ӯ/ʢ $QO?$#n ,}RL*{h]y#@X8Y"xSV3.݉*c&R`W&D:U[qBIQYזx=?DF/SM:7&N0Iʑߓ$Ӡ fJ(ZluKWZ8.X j ©8zhU\KJWz%cnG'="hˌjM)>ғ)qb[k/@uE+|vAr>RXz&jYE0H1[HRz)e3lR5cHE tZouMGA _ʱrx(.AMr>ZS.VmC03{/) "OHҶ*& CL5=\ ⴝu2CfIaK(0rTYlc3H\^}_ne/WP8/}}w ;5ٽk@>׊QR:Ec'>zf>*50rGsRu!\7wY9<Ցm<y,BUPQ㙈gQ_]>z!cL UR9SJצEXt}}>Ok*biYJ3!0uriÞ #R&fie>{j>QX}5_OwXXOAѓ_3Jh'ֺ-VCpouogwB NJLU{ted;;@ީ GR/nDŽaT&a?0ķ2mkPDj.2z6;(:+N : 1YStG-#>\~ictH~h#ˋDE_C`>Z\rȰJDvIc:Jm;Ztn>=z =`r/,c,^A1 q.+*(zz/߉3[3TP%tKR)}[#ڛ$z= !3ۦpރ?>lnZmּgG{<;~ aR/Abh VlL}Aφ?\e 4J?,}qiNE|Â%P)Ϯѿ?@kN$Z^Mu4ೱNMojfccՄ4Aszm}hy\^^Qn0w`Yt!&l55^^y |x{o&ފfW?s\qnmYz Zp6 tf>X[KLK٥STm2a1Z\LeX_9_RJޏFN`TmT v^c '&϶.؊QH̠Y el>bhfUn߅A0*r%7i}FJ:_E=Y=ϭ 7( rRlD~,`Z<]r(ڝ/25#K۰ub tm25yd"T J@Ki ;)`%fՊ.]SaA8,B{??4,cwc\oh[6fs!>k'w~l[wZ^soZ;=2;YO_z`O,tTT@F+i4E#( F˔,p6YכbX-z}prZ vtVhG :a;j6څ4Lyy T:KY;9tـvUMHC O>2.$d:*gk9)YE;s *^4O?gLZC|r.jҊ|, \TFǃ9&bK dZt]XuuRڧKpEA_{{v C*ܦz[ԟةu?@Jgi|?'wwWSWb=KjWNn{}*)]wlv,1h/XJX )QYMceH{on@E:`㬹 NNf,oTG1o['&y 91zZ㵚felhxMiƓ;T!O\R8UEn~V"64$㗶j4f!2(Y*\&)Vb9[Ƴg~s3 \'峺-) Y]B9 ~e泺c|Vg*; YCǘun5bc1NU5e—a㴗pdr&ŹLo'b9Fb8t`l q`v*-a\3J6„R:]k^)&ڄD-A7NKrpjJd>\$Ș#T+TܒįV\ͫ.5 fiNePX⛔QgLcK4jo䰂&K9^~12 ܸ N锡c-@œJFU\u2%=h8K~I@Vbj6Î; u9~kxS &gpMgr9 .ޖMt,B[Vv`=E"s[}f:AvU%L BRQ7\A0NԒH[nVL'jmPq+MZ>j-XN2Oh!78OGX.ݘtRcVU(UM1j V`}k 9ez1&{,$g"7#'t _8ٶAЪڔlfn0Cc1m}a+6f4U{l+`f,T>̻1'pdz)E& Oq,HP$,f(Nv2 nhM{[4o JB/fK;'O,$ !-ͮ:"H$F l7}tV]ˑԎ" Fz0X|Ix) B̼:(5 qy]1Gw[Fiz^iT9*\p$3r Gr&ՑUcĆ(;ci\eA .SOzG:~ { =cO%-_otoP+I]$ ؖ[Ԭwi PY U4Hp/>;Oq=se6'.ZrO77p 4mc!hi{;?Bʰ\eňُS(x+ƁZ(TX.kwf@PyE9p3w"`b/}6TW9c!hc_K%B*RB̍!~r͢JɿxTI*Q683nfWU-VJw_7k^?ۢ)ZxE׼|(ž̽%M!IVKTp9 B`+>C_+JrIuR ȣk5F}fC_{2+t11^ӆCy<.|ۥS*&9xic[1`J|]#hR#Bu m!tf/q)H*\**l޾[ iȸ7nj-Y=6EՐ2g ! i(N6B33BԜjJ&r29&/GФp2 <]U5c+/b)z!gBXߋG( 3@bRi~ⴏO&*@Ku@P.a Un˓sAO@(0) fz^,ajaR*yE"9Aa?aŹך`~v{-%Z&rr{!׿х\K۾˿ZNO?ܲnk y3jOڋv[|:;rţG[\CGՇn{Hn3pW zc|kclfbuMOJnߊoVQz20\/}CR4S[0}uV<)wjH?IOntI4mD'^ې3;K=EVFvǿ>?};:Mָ5:N}fw_rxH;n"5cv>V 4Yz.gl7Wa8Z-exn[{Oin &c0iC 5B"',A|7Ji LiT5/&8[5qɤ:q w< x;M(_Q;wl;ntۭ&?'σN{m6[kuy۸]_|G[XI ҩ`V-:4cB>'nJlR D)GҼҒFYL[^̧(;..Q}pO'?=#4{GhΫ ֫%')k.eDv`(-"<֭4rWz,psCGkaJ5$u"l]-0lz"܆ѫVdjٝpUA*y1qgxktKB$B0*2 e# ^/(&eM> P'O!ʉz(B.S< j A*䭐.,'F8Fb$aͯܪѳ"ƵL6YyOC$jHش2y9[~Nv:y[sIhBF0CE5u}:Ѣ-_ajbwq`hγ#`r T ZĈ.YQÆAp`ysџU˔⢀^5Q۴Sa'ygf\h֛B.HqLN<[35ÚwG)UIQ \t3aLC&=a,'$!U8%Gs c3Mj|vyYG%WR=gMtm ii'5:v6ú:1h$C'/T6>%dBV81tn?CJئt|Y(ȀY;b ׬=2~mnn{Lҷ8p_hP8]g_Q:rlYhBtzux2,dV)xִM gH;x1i #-v;fv֛@ۙ^4]Ϧ!~"zAwxQFb[]DkR/FB{aM%:y#3d?n)ɗ)Zc)&5ZrxoVkȈAi,Ct㈯F,_D ߩֽ74 {$\hV &|>N@G-h2Ey֡}wӝ>S؛D (C5&(f%`[|%[華k Nk K~˔X8 /9RF=1LT7mUpXn AI@T?r@|?BQ >=xsXk50wgiӦlq]q4z) NNPӲ*Jy|1#32F¿a@b~ݰ-).h dlPc古C̢T>?<1q8%[́72v|J < l;~z7f Pܛ2gWQM-iVOi'TY%^Kп$w>3_]UwP7s 2!RYD-(Tf$,l r"6I5h2 /\kRm)Nǿ! XW(-YláVqR[{CKSGjތwu9Cڑt]ﳅ*:atM*mE%|˕_[&_Tri$%өo%XƳ HGpaԾ:zȗ/**+xǯe<)jq)_\fW{I Mn%eBDD$zif(ʩ?& 3F!:/YO)$kS:s5'E>ov0)Jt RhNWt,Ct#Qj?E1Ǐ&+eVk hNZnĘ[},1b{:$[ww27p;xČS̹Qi>eeRm&9>|:LӢ B0gm"&?1wwы㓓gOO(Sƣ&7L&]r"~NR{1/Ĭ~hprR#5(c)rS0V LG3o_],Wq-oMƎGBY4+5TOC4Y`rUYIT\ٟLF氤%!L^|> ϺBI߂~ WAd<5BmݽkqL]*24PEg]WUo֚]•-h57^{>l1q qaKTUga&u64"e'C6ѕ+X6˸7 wC: acusEr]b7%gU=gyp (?R~hZXuH7!M :" D(VA-Վ L@ qi[] Ev tk U8ZchUݶL~DQˁ҆)蠰Uy,q{2$WiЮѢ]w9 >3}iPP`:bٕ [hu1fEU}sWڞy9Dr^7\p'6sL3 Ip޲綶 :d1̓h! {iPy]76ڶM l9&h*0zJ i=~oU>^epƂY)h]lmbJ1M޾JۂV&Ij{ل|V45?^gWԱM).B8w2'\ J€9Am3QA>aq6YӼƦֆk\8cALS|jd}Eneuj`ɴYr6+AѠ*zZ}B"bpϗɧg7 0ugҾw"+X~KKa03d!up*qg*488%e3?`;JEԳ_qd{[S|V-Z ,_jwfIű1QdӚf2?g?0Bx ٜSʹsvI3 S>_88}c "{G'$%+NmL@x޷;]Hܤ!ɤȖRHEϟB4*KéLHFCC˰"|@܂b\3uq+ddfJP?+iSO4}wK9 ||«& 4n/TdT=C6<.#D=n C*kâp/L&MasA0X4hΑs;6J>}#lT'&A:q*֦n3Uk3ijd.m^td2E,oV妇Ê?uTɷw(TC1*7#>2\noiXXa:$ ٓӬ`"*Ca H΂[1aiڍJt=Es y$a4z»2 By+bbtҷL2 &syB%+{gZnV欳X&a!5ȓ*XbrRHK l)Jv(5>=98j/n|ZsQ@,h# /t4p04ےp\Hq K\%S}IS9sF8 =I NJҿd6_܌Ov.&@܄1KcT]<;959?NSk_B-G*M*Kd$-'FԂ<~%Լp񙅸 ;!jC+#Ҫ뼇zXl$aFsղ%(3־g$rnd[!r*m?d|ƒȡV⥡ n\f;;:؛O0%YilIiw`ڔnUnynO#Ѭ%*^"$ :cKZ%*ƟO?V䪁&-uR:A)va2M 帊>a {3U`?=~UT g =qyiT\ 3f5k0j6Z z//ں͗VE ުUk$+L]ƍIX1K'݊Cd-($z1[L\xc Ǘ=ns&tjƎIzc}mnM<<ӈ"Ri}>Jz6 +5q`O)u_@1ۙFC]Er' +#ŠAΙw2aM\w*@ӕќ`(%0 ÈZݕ Iy+`eac_X7v _9{EZז+РDsƤv"³k3p|hvkfe{"9WnSsB^ Gy%0:ǫDLJ89t]Ӹx= &?~*apZS֭bȞR~]O y1 뿎ۋZV%MG㠸%ð.e]Ʈ{*{>#=0)J:rnMBg*ecs킳(l&kAğ1=f#2Qר;Fs? o6R|\3 [ aB|pCI뱂Yv3}pr/+(w.+oQ)X311>;>}.Bq޿׼77vhofk^Y xyyn7:{ |onv56:VksC FXhEuνVg& Pfٹwo4VNg~^ U5޴NmlV}k767:fnwڭN>v0ey*~^~shT~йn;ۍ޾Ѿ&#р!蜊6!|C!G#82#!` kH:~mAtCH#LQ^rH5Z"^#K,5DX8{}bUod27{M+-%5ͼ^aς)A!("{F Ӄmrjߠ ,G䉙L=@"DJ"O'hICD̬~rw`G5hqJ<')I~=_K|\#pgq*^˫*LB)x֢O?<"g+:Q cW(*#5[6 Mc g ح[Rt -=X(j$\+hZMĻ%~ii{ް櫫A$R ``MFD~LLC,i=vz3!mT+yLŜQ0?V2$}o7Iƽ7ӰbrFMب : LNDfJMJ {KU۴ DT eh{Xʟ>te$HJta F0X[Yƪ4L47U`*Vܼ#Qog|l%F5_(FҌ-~kA"fI67Gnâs!JQO**zӓ{7kW6n kՓ* 琓xK"Ho0l[c/gwtO ۙM5N4A3YT+y,ܝ"%اrS0KN1wlB|84n'pgthRJ?ئ\@wElv& 7$Z>0:qY3$wR~;jҳް\Hfx81~s2`Tj浔uI?ehM=O]􂌞pQWYɦuaᎤ!// 4͗${Y\u0EzSUy>Y:VC)AȪBIYaЪ8w=ض)Əg|;EydW,Uedf"Ðģ>O\\tGnѮQ͛ \v>:L}N8 %i@~zwzwprq VF34FT 7:Mgs41< 4|"XJk2ђJ=~~S)A.q/ .2!3)CHM>9r\A4XE\S(MpLAj* +qqMk/^9u9#L6/SV,V5ʮƪ f$p}`u ۬Zy6hV d_8B7| v@ qy Rk^) ПUTDSyNV)kP0~u{{bl6/$}f1r?yR2YChB22|(byПa @)Qt^L칫Wl-S⥮Ey#&PO[TٮNsT5 2'6uԺ @Y!&zoJ" խ$T%"e9"Y0RRRNN$daA´25Zޠ〙 ~S M灥tx~IYr6ƛX򯄐A_79EJ.'__/>Q?TQi4z\TfIM={fV.7/Y,R Mc:>cs:^p eLg>k76w/\T%J8䓽D6'`1"< kHh2:\z5@߻45 2}#lmV+oط=35vhKCO&N9wP9iȾePB~у,旵$(lpz:ojpJo.Z*e38iI_h•@|GNUhRV.UVdb'Ð ?/1O <}E}CQ珘LZ<ϠXڌgqEʨxhzKY(5%2^fhi%L;ZX6VQ8V߳J-ȥr؏Њe0=}CWQ;k1k&R9$C36ĨyiI GT5ݓ8"6 _wdʢ`M3D~%[=sթVMJFT;Tͪ!Jg<#0͕ q9$ D<TOywwы_|w|{wn U:rCޣz'Gߐwak5γ'eI@/H4 qrM6QHWVfRJz"9;+4úJ4Yr#Kl f=b/ck[+'Fim6mh} o;<Πv?O1O}????'?ޟG;o'_?'_?W޿o|7?}g?OgG$/LɝUxxwqt6?F i Mɛ_4?'/ɟ~/Ϳ8?WoҿoN:oo?WGʿ?Dw?/3W}&2wڭ'V>Vz#G?&|mvFvUwG F0 #6p`eB#nxMsLdk( EH5a@#| QO7A]q󾄿 (Ҍ.ˡXq|UN7Np[+&V$mˉa+T1tr39_˾7Ë>Z!Lk_/ꨟavR(DQrXSKC FgDT>鶄g(Џ)ԌpBEh@4A5a3=H'OK6`.Wu$펤*AiS[E$s; PyY uotfIN; H$zϝ" GHH !&g9Azq[i$ ǽ#c{eOc;ϖ`,zI@Y0txx mdsx :;n8}"ǚLu$` W1ٯƑpTMR4c\$3+b|3 e5yqSQWL  jؤ 8|:f[8M=DΛP|jޔ"F;[t6`ɛ|1n2!jE4qpD.>z;3r/ckQQA3U)ȴ* 'Hxf~NwDT,~Ť.Эb:,TbN!$8,k'?ӭ [79 N37NkPhMVvt}tWxO Abo!qB1d! 16.j$皵d ^=GҸ%骗9tUL%3dF\zkY{CCNL92;>zcZu,2&ތ(ڨFJ%#} Ðς}, rvCNXm ni(3&%@khn%qYH6]ieu>ΡȲQ5ȅ"AZE6]A.clzӏp` <1&R%Yfo&SNG HN&VqxF({o}IV R qɦaw,AV(@А"׍Oh8Pi@,FÖ%߰U5E|* 5kJkMK״r̚iΊx@'MU`T:jLd7 {$Fzb5K3|ewh8ձ+oRsg&ea(5{0-ꬾ>ܧ5y/P?JeūF=ڱS٪OT4V26aG)Ɇϊ?ZZlȁ1ja+á:kM^SLWw஺E*p DE zKԻrBc@. *_)h .ʐK3SCڧmrNH& ?S֙N1[ϤhK>1 ˊ .N#PGDz≩}&ٿuCM1|N}VPDNe` 0 i J6Wb𨥐 1$Ċ%$䆣w,}NDc"% aYϏsA}rֱT:BցV4 'ҝYНV{ )AIq1CX+U~L,/ʲeKNj Y㾈(:jBNUO+L!cD[Zwh*@žPv702N_v1T>5 MʂH"(?bRfmX֬FOVy ˲ƺA~S$=$lnnkX $8=ܰ"RjWGǫ ;у X;280 Ⱦ,4 yxN[r`R-!@ЩZ 7vo0MJK)dM&ٷQ{544SMtЉZЯ RݪkGaQ_EK"34oF;s{͠gPR)e-/myig6Ö>foIZm"1KJKWu,[Ǐe\MN0-R#>Sh:nbɉn(I a.A6B;j31[xxN,!cLghfPcMJ*+Ǒemc0pT8ii-nj%L&Pn=ؑ;*fUC@7=V7*nSM@;j #vE;eft+o16 V4!~GjlyrDA )[(p*"):P'0`@ PdЫ<mS6`dY24ژYvȌu'RTe&0Ӭv+x'Oa%k=5Uښk4Cy۝{5/݁V-J/Ͻ嫥 Δv 7=|Ԃ+,`,y@+e)}ll˪ܻ@B\t YwkZ6N\?ZcQF'UXZrn-EͥHYˢiÈ! cjspQwS2w f(T0TVK[tL/۠HzhrfR[T-qIh2j&W7cr%rf~ 8U^.f9UgXR-//PƳբdI!ri 4 ^-F̎K4E rrdϕQ\$LKX-ԊuxSϛZ*ֱPLB^!|զɃ:1&@\yёD7d]3V̕?$bsmHA#,\i\zrpi%O4QIlīh-LGL5-#}e'#L_]'C(XF\klU6`?4Fѩ,R"=g<[Q-mcJ]i ]5vR /꣓IҔYUcXJV!e* A^ʘ . S eYyy ٛyF!Q^NtJt6#cb9,aΌ|W>e7#3֋t O?y(_1&,=lطtVgCOWiՅ$k }XuXE:,H졾2NBUu2S;E@~r"n`Ayl)l>I.CSIcI(UNÝrιIk\(sKeQ5\[dyЛFZCrЎ>鵄2~XVryE孬lrr״1,^d칔ɬ'莽W'GVΪS6j%g]r&’̱*.K/l*=0*n6j~bTqL.xY.ĄjI5oM-\~ u3(0%5qy+afAHBxu4i:E(]%|Rd_aDG Y/Kh9sE4szxMC8WO^мRr;fݪU.לK, $7 iߞ1Rܡ6`RZ\׉Vj$@7'3v;V)! c{ֽ 9qbm "ϹI0[o(le,(<eJq|É` AGeevkPpV.@UBc A[XN]_t;'@_!N VrX`#aVXU u[wva89O…`)sgSͯLT#Ӏ;ȟ*s% ,,EZD!ճ 5u(DLHByBBaIdb@==q;dkKiWF%<{-~MľR3-&0S~QL&eO+MkXhp+P)Ԡg> ,TqF@ަϛv ͏_?amfk7% dfgSt^@sGs1>pvsUR~xo).pf4ljhؕE$ѐy9mΔϢMX4PM+v8!J`>1Sl"JiRQ/wFLT)\c,t?f-AތV~fbŠ?SiA|gXXs:BsM14Q̉߇N )2'[]<KtU' N ѷʶ/i~kx>fdfX([ 4/;X5tD*ό64{ku#+^vߴ۟gϚə6$T2fk'&a{ʼyO|rv %9]TV r*U_F6X;ٞY8Lnd:وϟq/o/?sl7;phs}Β5,<VӴD~Lr`u,Eسh<[H]47m*TgZU՘ iM|Ht]o 2Ϣ߭in[lw:7-fhf yx>'‘|Ju+%Ů3%.NBp0\8*gGO?=;UW+aD6F/ig8rN]籑|vϿ:nX/:[]`dcm~)U L2 ͩ!Ժn՚C/Isփ\P0[۴Lu6 4Ĥg7>phRP!F& jgt =#ӂdߡ0ty 5:!AfMk<ڕ)P)t#F6$x4Ɨ`iP`po_'k@%IR| 9X|c8bgw1n?Ræ?R D%#8ۅ7Lاћ3:Hxn\6^i!_:󣃥ӢI` (\<)9j@;c(WO1j ] 7`2֥g΂CXκ-fg-ܰH'71I6ApI6!tK,r XtNZxWc4+ p23y{'Bˬy5l@pb4, Ԃ &Ɯb*AO}r2y2U?@Hv%BfHa%zIP_Sjr; *T:F P"X Z`>3ׅ#n-J%Ōq ^Eʅ]<)vgbh8a}:.05Jvz/ xpT~!62CFŚFqZP–d>f8d.)9+;vfYYEJF^k OqP5z>(=U1N y*:[$@˶HAݢE+'~é: .m2T|zW.wd+KeP.MLiT$NfNZ̳|TʊZdy7Ok$ɴCm8I/36?Sͩ\)oNo$<7hbN|d* Y³IIW]r]:ӢkV@ItrnT0y'W;~q["呺Ĝ3ۻqI3q/+8(e?PnL MWuuW{gfEDXB i.W+y{p31 >mb1 <*b Z~.0Є9j,+GA(WO^Q7{S ڍUR?aVR4i:DnE*w|Y5ާ$ibS<.vHyɞ;9&^/ eM{=˂KR3@nf s-mi)>udiVx*$+)[NW嚛ZP%`Ze)q=ߛJr} c`RZdQ+acw0kGp4'I`V,Q[)M g{-e'MD&-,OTl4t6<9l7Gvgk}q*4GXܒs #ve64XVhkqnK3KmFI̼VjtXV+XurO .0"4z!{&v,EF+'1AId]/d4nv5faXm/zN)q`$^2SĞebVzݎVpƿr~O J'OO>u9)C,MފXNY*&;8G轺Zu(fMLE~ &7_kEGM/&ޏsr9o7R*oEimI`8r^zlcNDd<еxRjҊVlxcE1s$\(GOouH7WdM@2xP"7|;.P9\VAdg,^\kQEiGCL??:LV93yMFEƘkB޷-Pu-5鍪:x<#gmEgL)㣓*OKP'o@H:LhHQ*6yD!^N iֲ7)(҉ ZJ[UWU"gnehH}yxQEhnKqg(-U6* ڣO*LQ[m,baL ]{F0jxP > 듋}Ťt 2쑯BbUM\ŽSR!fwg4lwv i$/ʓ`؈^Y,kNj3*N9)]JRɄ0JHfM;C%;ۺbu/3naz27K/&]ސ={vegx 6Q3W2~KP1c5  q]]x姸dLNqZ*J/pqe$VavΤ ez '^\x(mxq51\7]h5>i66:VC|2_nl[klz{n [ &x3fF0#*b((phx-脱ØQ[)Ҁ3.4Xw?Ģ(DgCFc *o|AԪ)y2uŗ^+O q0I>Z`XȾYu Hi0<磒"ǎJB$Se鉊)9.DEVNm_ gSA]Ekxp.P(iPqeO1V,EЏgzTv2%8|Q|80uMhLI2b-}}TpPUR6M#ki=[&U; ֱ67Y?';.?[ 9&J h(#!~1J*p%,l,8!g.fOz#bR@=h W⎦b] ŝb'l)ĝEu0yGP-C͈~_UmeFW Sd]UMOuuoiZ"=bxZN,DDAgYqn14˺vcќ.MNŦGt3K96"4EK"T0r}G r+M);2":ery XrCټn\#$S1a;U;1?7!cK%6uaаhj&W~TzVb%}3 !BM',Nze1툘YZr9d4_eSC/s-HnD/_W笲DNjט=sĎN8CP8&"ܺ#-X=#WR%n)ahϬ|/^`.[Le>ҨHqFQw'Ա'ߍ5;S< ,*we, 6Ǩ~Q*=nSmQLs+'䱡coi|@H5LeSEk^Ca<٤g =|ilSjv]e a`84e Y #2o#(1(V q"4Dg*!֔Rݩ, B!:q*q:Vqن&W",U۬b%;aSű6+  \YvlX>"l#ֿȩ3g 6c1q/0="Kռ?LlwM&Vw#,\"ge[y;L"YK##x Һ9; P.DZ/lr Yp:^UmAsRYP!rI.~V,[qg\|7}k:Z0X[Y8 PMsZ#\D. =h~Tݣ/GU}!ݏZ*QuF'NBĩDU{؞X K\ri$A=ro-,@9Z7;woH#@5zu!P蟛XnV08:V,W9^ 3Ȩ(ST!.&~ڙ|ĩ *qN<cb36D#qSZ2s5[!QܷU9j9s^CޘTI<GgW}9w2>['7ln06_~g4k]5 7ԕqվ(DԐC/'Qe_NGXcV3w_X + hq/j**p#ўG%#ttAz'y<3 eٻRp^Ig_< f/AVb]OZ;eڵv N^{iof>KXUr =fc&@X4(?@{{2?jLR+B[4o= q!Q=5 C;>|N3kRtLV=`4% e+B P51.3 CwvUjyf#g *Uͣa#*oC%x51N%1:6 Ck..ۏft7iS5L7.[$.5 =K!g I$7{&\ sK.sa`,jЭܝ%F.eV[!Hs]͠I4`ilnods "ߙ#"{$<'&ּ͆I238 qr4ZW a?80%.MPXZ`Tϩ u0as>)fE%&뻮hNd,I(Q 3KmPA&||JM @ĸ9Vׁ̕-*$ioBՎ j 1Z>&#(CRy\T[kjAKYIi= Z†`Hf}+ U0鈮zc5Z[3qoW8芲R^{owVzrMY?̞ԕeiCuG885dKpt K#8wNTY.iH>exak cY} Z@6]R-0*Ӝ[z9Esw[lZ7+- \C\iF`Ms`y=q3Jj]еJ/҅qE]죾?@\=Y;r퍁s))ߗ'D8{)_>'5? 9Eߡ34 ݺWs9#j,.zXffg΅?5ij :o},QH ov.ikGWT 'ɂ Y 4 w݁5;H2IcU {T|ZT>]hE-Xq!T'{)cuIRAYVuMjt3Q\q5s ׬UW?󞣾hP2CkWѻxo]և螟DyIl>(qZ8sSӃls@djާeDw=vR]@I!pw ]=Wco0:[l_쭽fu3@ZßvH-z`OydRpuZq/?U7ل{N\D$v<%ak^P=lcXfxgh1 \H~i\0s[F/HȘ9T)*[Rdܐ_e(Ht,QQ>wsrIxd[*̭\?:I7BK#J#咋 p,\])٩^Ҁ*&ZB^U5g͆?VRL2/$Ec6YXFc4k $C4F k?Ģ!H:T)=A_F(aD,k!rfZU=Ns,`H%}DPk\&M⌰_xH0a^.,\l{g17`j@l%w-m>G.xtZ!L(X!0l-(gQr-U= %S{)ؐުhX43ȷwa[C Dœ, d"]xAܐrZK {$3~*o3Y{0wh7>i6Fjl[>'IFП-t\klxNw}̂w^DѫX#C9$a/16"95JrDN2%LAIL1[RG^kk8IĠ*'o 2qB*W 4kL|m4#ǷFPχCǤTޥl|\u焬hn]n h̝x~`vn[^Teф_jS[`< t˵Ml[tYdwh_ӆ8ؾpdxeäH~Gx*w,ˠm,#@GC̟g8zŧ@e CJݚuW) iNL1KKY5=,+YnUW-F4<4 a=ߪ W,QӕFJf#zbFM.kZQ'yqVY^8sGf?-c?ec}z,Y?n:dy`]x( UӠaemkelSF50*) !eWr@$DTvLFyߴ ]nf)/%Yk t.4,P)jݿ9ӣr()l oBДi$1x,E"&e^0:N Ud46 r{$u:~5 "-0V@"rGiDݩ9|2<=1 4N5rxiJ"t(lD܅tnzA/.JG><̺phbBAbU3~XX]-=<{Uf9NՖSq:ύrF%S>?<|^]8g;,)j.chQX69z..W#CAdߵf~TSvׯhl}4q~~{ ?wT~XOb5 w"ǐipiLzb *Y͋) pHhaC1xOH8 UVH`": b=ŏѢ@"Ko0/UL*|Ex:LI+fA5"K2CM mo5LIh ]Y|N#%IEы$r- f"u+֝ah#pKD$HN ,Sv 5"r۽ҎKa Sj Ba,N<֓6 wK6[tj/ft%OF)}Br5 -[Ja:X إ=YUkljZ?fz.%0O"<3Ȅ G(4I :6j N#JkQA 3G""]YI D Q)gA]6J >- nɠ/+.+u Ӊ0Bn8$%G}y1UƲ2%0ai b&K:Af&V"Gmq9YcvCԼUw[1/b/+X$D&QXk΋v+9ܵ S LcEzZ{/xU?mU GռJYryNjv.ej%FÁKȓt#D&(>uó! {؜fwYD/텗Nk8E*U*tl VJxYwNB8m}<|VEeUMFu,8翍F F>>'#9qo5uNH^Q`XQ酮4]?$(ƹ\0Lo*@>LB! PYF!!+̱zr`E|Lj?VIH!3`$P8=6^TpDz%3خ]2):ݧ,*v ݵ2!׆L0,zN^0* ra ֊1pe 5E~E{-hTQ'r ^e&t-rTz64EY]mYkĽŔԌm\&Mf2Qөvl`5j Zm(3I ! f2 .}~J"_ud;.mA0j]虎v]b螇 kD=yNJR'@^[⟩b]X 57i͍7J:E$Nv:ހXRqNcdMg|HJJ}M+{ӫ:4ϝk>z?=C;w}c, TGSdu$;CFkձ/mԛhJ\J05F*5[NFy攐Z@[fcxޤ+As_OmԱKcju m8]cfw]/v%@8SQү9NcT:. 9(p?`Xj[m|l8b3,@9v 'tLDlbg!u,<#5#1>g_NḀAcKXu\)8V/f*&бm~ bpO#tz.l!{hUi-@ =_AMU1's tԼ1[9$ ^ɑ왾F4* kr4meG|][35.o%&Bh&Rܪ ҈*Ѹ'BٝDIxF8,$Z1$Rc),/dznmiszl lMn,.64H'kxu rINx2cDI|m譜Hh) We,' bzfh(@}56"зTܺ=I{8 *HR{zuUKUbG`V2"9j @CQ 7i脳H3>Q/i Yj}PEXw +=lѫP8Ƶ.]QP=fFk`dz%U|Jcۤ ~EF^wۄƼ9/z&d>dr5E ( A *Cvqu[UUEl_~SLB溫?~ϖ%@ \-*ު z\Gχ䍿wwX[MovsXͨ&5׽frш|&ټـ,p@/Wax>z}CڤLxf]h#C|X OqM:kE \k݃m8x- L bVtP8'ӳTV_=t+F񏎏/#=y9Q\)s+QeΤnRq_% D## qJk2 b3cS۰=JLE>29L iaxS[ύ#HXc3i?|츕jlUTJgti;>Rɇj^#x3 1 pՑ8Y֚F>ſ!%Jsi\Ҹ.`\~V|sswyuaG=TSR';$LB\7D,j9tiH|τR1b #}}"\H׷K_VGֵp<`}T=T|Wx4Da|-{Hz9E#<ӪISkhYZM}9x :mԀO0ݚ{S\*S *cpe&n{B& : ]۷EԜlQ. P@F aya=$ LY)סPxACzDEًZ^a5RͧjW(\9V6&SmI߮ѨndpI w':ҔOL  aA}.XvƱ oH%o*dO_aT[#ka* UrFq@Yq`r+Oyyd;;t42էZRfq I$Aqi/B{#4FIX)Z?_};:'.׋PX.A#=#S4q$_.,+ 4K"xQh輏mqى5Ȑ)l)\v`G-u>afXoWc}}C| X[k5{fvv`{7 "> P( s_.&` Zsvg]tQs*mb } V5pYr 'm:ix:׶- *Xq@^^4yxX)Z:I2~cux΃Cg|i/&(\E$%*L  EOX`2XX=an긘Fh;սgNaA@dLB'"%G/0v.Q2ҩj-8|>gJ66; ηmm>/axß}+Dh!>;nA>9ng!ooRfhK?%n56XgyUda<[ #&Uz |q^iixyaVq׳~.ϯQj31w(C0.87gH'[ ݾ5o-Q3HQEh>R(55U W2icRfvν]c/,6/= E87,lcT r*HT1 +aU<)QƵBKuoKj[nv8%./|w}Vx |閏*giTJ|Q8r땷q|K]Z3 pjxk2e͝.oo[xk6 -~ -g/[2& P:{9A̓?Ma>f8=|pu,`fhQFqO/Ί u5;n{#?#zDToY_/DPN_ =5!|$Wy|@asD*޼:Wa|VUX\KVhv8078{*|/$/3EpECs͊ϽZоp9cD&%}w2Bu90@(Hg !Ai^ jlgzc=.9`ёA0s::BnUa'wiI`jࡑQӶ 8 `ceQXKju4:qJy"f}5_%)K& 4_ R*57AVM4HpuZy10 vgthyxكFìh՘O6SjaB;|> vdW@]EQ)^(D˪1õVOQ񰯇9 /2@3BאC0L]iRdY TaXkW߱QWv,JNӛ;TU< ecni/aG@͕k[Lyp+a`0&aS WFYmh8tB-GTw-Թ?.ɾv!zlGGºB4Wb_`:꾅Q Zum{ɻ+1udNEIXTJ!Gk7$k Bo@W*u4iUW)o+հZĜdWd(!Me\dB!$iVT+ /NM| xɷ({l^FrN|I~WhjK$Ejp^M"%D{aMh)9-RJ4Ct3猝'`: ckxegg6}qtS E]|Kd[Vm[dVʼnf$ /_ xάK˗ 3]I6/!z\Ȏ;6e='z!px[t8jl#0 F%=8GݬPL7E)bU~c :͚3ke x̹մ:7 yuw^]..Xt#|3H ]exxY}[e%Tw녴^1P6RݚPf 3:${NHNvԱ^5/f7D5|uZ幖w` BMU SiЉF]7e#$1âf3%^悲KxG/4z",+V$Jq\$970oc\e83,{{pn0y2{Ek DwLq̀_Uob{-J<hDA1{9#4e* m6σpš*%gEQflzɰިj-}T%@N-;I.l%@!)rϥ GBݒѫT:fYdFvZ Cfuȣk^Cx_ YIk-NvbtJ30/q")heQū`pGLv 3#܊UԹxy%bGzF|pzC nش1Ғ &kTW}ƒU0P^L؝o{I+uuy Ԅd8eN>vN_>6Woj?KngA6gj pIVѢ qm=g[ײEhΊJXJuVDY!t!{58w#Rһ3s|e{܆=6m{4H@"0pD54-$[îe?=@/H)Ng\(vNY f ZFDJ[] 036Ì fFQ=ۤ|tVGCENx ^'DP#oˇU^LU[ Obl4Lc¥G׎%V6NJ^|cXQ$rFD/mL ?tXSxٍ*YN OKHwl@{".2/pkq=m{=K/!"30 Dh,B"0!> M}4dOf iY(EM쿞i.QRhg>LZ QUTT ޗi#yp3rmsu,]qmLV3g)s";[ej%&m; M\gNYKr"Cih{xq{"Lo޿&'/H]:j+L@ڣT^[i1+:uެMb Fݨj[QڰG[l{>qh=NWLVR٘Jyf[eUQ]wW:j抯:ҦK[Tpn'8hqrl t $N#̗|))KWvn%'xqfc)/Z?l.7e?>CLϜ$Q6 g!* -F{420AԹXdߏ` *z5rJx:/&d`fkMOj"چ)06HtP.!1 55o2XZ2#re_5ϧUec`4.FZ\!u2`YsF8ll@vano~${bcu.=;^sۡiZƏkFϻHW?f3wȚ^L3>fY9xdW6.0kKuIYQS}LѾ/S?Fojxod#U l >թ*< )–}+{f̮fO]#,Y70£#PΏd%f7duDWUGdetFi)c:YVqRVԬ*Εֹҋk^gW6izzJ>t4qѷ}&?T7vFh۽tK*ʞX\vɒw}מ' 鎽*)+ڒ4|}SYIKabl^Wc*aY4~??G޿w6{oT6!7 [׷6?h<9G5ϙQRt62̸LZ[WX|#Ȉ gγr--֩ 8UZ2Րr~p5(:ճNЦLfa!h{j;/wM@/N$>>%SӆDI0Le"-^1R>=!xp/cR?jE( AEYzjdcH MI I$B>G3kOX/R\LtNf pw Sb^2yrpOQ$cJ$A```C[grI]M'R P3G kgQmŸG{|Ǖ0؃>DjhӠk˦$[f3v9Od>FBf@HDO&#P˦aCf*&=3Ca7s D l|:)JE@Yg fIʄK*$|*3ecZtQv~-cVSݦbdFkSg{ݠ-P%>'4mBӀR}'B&gMǴ}\ X@ Sqlۨq Fb.@9|!0PSm :11E j@NصÜA9s_Ӹvɡ9h[̃j2zi@ X\*T ;M G' pZ9Ia]9@ӡeOh(o΍zWcG,vlN/pU?[1 KL8{*+\e\lGG, 1N8JhHl/{Dŀ zN$3EE u@Ǘ~LU-Su^D s"lJL|R ۛ@ŜL gJ[Ie^zZ! e_iڃ܏Ēۍ9cӓzĴR1V˞)v:HkzڷUQ z6h|7c~cJb糌ș:YEXOBUxXewxȥX,^3 ʶD& bLh~>&r:MOUsHńM`d;}x8&i~'Z<ϊ̈́{^̉rg LREwWL2dW>[T3Y> -ۑUO4NsWAL%Ȝ䗽VZVFmN V"$o4:eƒa%FLпmd,Pڐ06 `F}. 1ǒ jǗ;I[shH5]$L|e5o.ET{x׻עwfB/xT.O%|8l}[baؚ}$g*mEM쥃9af'i~#@5  \:t.Hۘ$+QmL 9}.;7F3NƧpd}DLaF, WCNhŶȟQI[kJk#IIa:)sehPx s~g~"lhwc39HHjs:M'gg(S6s$R`vQ(=}lt#٧تj^jY pT9ذ^`&V%tA#Abe8Z91ꊡFE tp ϸa8ya^ M1(ϰx=`bIWϱy4,ÐggjW(bR7aٙ@_Da7%g*%^l$y̦֨3z_[O03ϣc\c\ǸaiGt wh5//Ge(b93P~vo%zI}XyVL#.î}t G838-6 Gf e;% %e1ZYdȥ[vI~emXÙf ac8'f_0 WdۍxgM5 9g͚n7Uv;,3skT坰#IwjjTJ;ǻ55k^Xc ͻnP_[=0'Nߊyr^٬qpq:z!k;~׵bz:Ju.Ҽ=ܠpJsG`@cہ Uܰ"B7B OF|zӼd%Qlї5:t?ZOecY-HRE'Oee̟<`CZU 2܎ddXÿ>pArgɛ%x&ۀ`J'[3e=/BrqBL.ĤƠns8Rt$mIj/͓?-'wS<񱭐"S UJiUЁtwњ"?IG\)M['ȿe雘j^Uk)e0jvKV{zݾSմª0t 6 6 i84u&ԩfF/D^L|+!A@G ?صRMF,vv69"aS҃4eXɎ[*S6cċĩ17`IJ ^hUyl,X EU IQgWpB j3pe!-2iBw%Q~cf"]8bTS3jC!AA1oB\(ǚٷӽ&[z[-ewg> Ax#h$+Lً ŅIFjm^ Q0a .PCdio&e? Яp>WWuv Y% 2̈́Ss]P1oY5 XSM;H?o 95Ob:(q滁I;9zE275kv݀Z-s!$ ?WMwV;2pJ4"Ӣ,FxXH\1ޔ 3D<Ŕא'b6˲ll"M?ZK H$[V %XApǏApaU˜Ty<rԝk\oA!&lX#o^̓'T&zz2@lN̉dU 2xGl8;˫e(~͆GWERrD:Aq|A.4Y*p^G~%R(~@.=1eڣ"$#mc">@7끄A77A<@]"\}g6PX4 qz "%=fHDIUf>rPƥ SW“ųss 7xnqKHed@1S_tBa˟^c u`Î}H긄b*rm^\v>`eTaY.$O~NѮfaTHA Et3uRq<$:7jyXB,d %G ń̊,6{rם? S+Cs+2]M 6m@dlNZ?pbE΃(ȉ~6r32ҳ`8so@S~'Z7 -01Nr"f1 Ys;_dgmuCf]Zz1:5{L#i Rj%S5h|YyN-X?s^"P|$ N43gO^'9@}ɯe.HΑB|~N9ArmE;׍*˽QJs:}tټgsnx-M4.װPJD0VR.c}(4uVF1XgдzH%u;|Bj:|u+eKVؤդ ) 6Ou"< X gEWP"H8.FtP.!Em6SqhnIR쀷G y&&sllʕi: )rf>όd3CҡF.Nw_0( DUEǓ|Bt[9 YKJog&Ll´ L;~gċE"?W]v6.b2Z=}1GqDN'_@ٱNVYi SȘ2bVK!x2|a/vMQ'1b0N:N cNѡFAbh}7#L kЊ!v!zSt1 ,u zr+A*6 ȟng*[ۡЏ3::?Qᨒ睃?=y`իCdIJx==1ou B; e*kuZL%ȟ?&OVDbU^1./p֘RQ@nuܰ'%5ˍ6dk$:K}Y I>p\ŗZӺc 4IY@H(Jw 23#ҤSMw֘xsA"tΘQ|E}#nIwA ]|0vUIVX2-=[@ xm:J0~A~ut*=tW `U7רᥳRLz%YՍUAnbg6AefHi8@[86~Ônʎ4J:oT~S㑺CX!`-33lc6"VlDf0d|DϙhkqR@*9Y:=?4T?5G =O%B ba>r.piYKf) Y]eUarTڌuEvBV}@60YH׫ Q͐"1 O'A0ńIt5 b5P l)$8TΖ_ :NlyteSƜX߼@fQjVCLH=]G0pI~a 4"  ,\W<4E}|iw3@59;8Izb' g%Ah-4e⤀5i۟tK7q.2Ol%PՆFQIsC,)( X>={!2²w΍ erN!BjbQwѥvѺzwr;Yg;BU~qrZHLH֓E]QtcF 6U #50%7K!?ovO_ v<5-GT3ΉP\ |˙󾅤s1?tbn4E z4:07]OOf9ܲqK|roۻLGFGi~bMps)=`DkG.6Ge1v8H;pG_ʯ־p2eΛnÐ~9agևLOLspo?+nS~ F~v̇OwvV޺5|=X}UݗE|}wWu;=f„F5` &1@h{^vC eЌ 7_YyKfk 2KfkMv0/..a zyqj3Y~i/~\+*_a7@@C}. ސY&$M\_y ր.F֝U<ݛT, l7.P1Bkca+UBсrzţ sQv>4$vqBP9O'6c>u%ȹUP-!@栢[ w}<61#² LBqy6v Qh|A 1Cko7 fFeN&$I`=m y?}l`dރׯu4C5{@GlxnHpL K6"|c~9/3q]Ǻ?{{f}x{b[ߝ3J t'a#È'of=ι&h/3i1?=#C$Zu7xkM:@xfk;YF6.'=s*~T oh7!e@:B)URZ辢T^BepzVo?=|Lwy*ZP&`HXEIo)[gLSp`# z@M!9\A53~Z\H]HћtĖCŐ(iMڬ`b5ϚLtf:Nn*R>T\40MC+>7撊GCnsr(S40lKbrVVjg5RҙK2ėB{m4c``d\N}3;m!^~s=Agbxa603E> w|}̵Y)?rb& {lmst!)5~^{aqA7A=o:٤.HwEnnTjO0NUL+n,MsMM,Y멶|M1Ʒ˨݃Bu6yYZ\N+W?QW:`I#_xп}ϑ'K2" \OLfPu^;Lj_m&@yO\it7O-pTk ʉѲ5Z'ٽX6%ZcYɎ0R_? қW}|&m.)M2(q*=>讵ZOo_ ش.K{MGDb$43UCow](?-Ub3Xg(@]>Gϲ\qM唞 Y:e"D>B ۔ d"$S@dH@7BG[i@F XeIAiWXi6BM ҴtI@ gX?9NT5k_{ L>,+riSBsN3C=CL9no9SO K a"J|Sh(49VnQ fϦ5'8&ϜiOcp0nz[p~0ׇxu{-zΥvQAU9eg9ē AAo s5ۅVQ Um՚U]ڄ)`Fp͆_2mc Lr 63=+ e'o.Hߥi<1úr^(\9)aB }:>Z_n1=dGew8s-q#<Ԭ66!+ mH[RoPd,,Ą0FLWȟ߈#MBdYɗ[g*%+b쁝l/?2wirOV7,X`UQ[9~MWMo2{X7|?p[##]%,Z;>vֻ, s6yEIk2B_l%M:7_u~hx݇v 5$;,>rK+zT; v>!y@mf"V:Ԭ6IuaF G_`D|oOܜ"ࡗ|K_t1TE ^~f Փ7uZuBD} 7Z>|Hn&pC,! HE!mȾJG"Ce{ )S+8$ mny; i޷{ݻDs= 0=G,?\G%'yI]71+@2@('#D|x{@ G+ Р3ed cAg*5r^1ۣU6L_PuLd"@ j_Q Df\$'>j&w6a=B'ڋޢ|(cNF{8 {W7؊E ):bD˯h9pt;9 SqɁeub+ģGH-F9M]E54k?盯n`=6evZp@H:Yz$ԢQ Y IP! ta^~i_l"t Ȍ =~SAk}]'Nt$Yo+M!`Ka-m+)FY!NATSqV4kan5F T"vEF8 `bjy \30kxf8Glׇ_ \(YsŽn"E1}KtMm;B`1%1 u=3赿wWn&T ,RS!x~f+[D騯mX>|*yg&26*s# YQ-e y:2nsevZd6̵#v#^7ȍvˌS#.u__%(,{obP5{}R9+z,ZFh!Ձ@ƕŻL^(D=Om_Z/VLEc[,-=H̶̶̶ֿvR{鄡?u:oVo6/6ȭT :^eFo^}9&kNz)`YNB}O@ 2wfaDEڨvsOBG~߬P+au!_ڠ^k}Xص>}/3U~_^,H0ow~>!`ߠ>A{=9aDzkV4+6rfQE._eCE zJ ްWx+z)h1(t P #oj QܩDb2 Q7 TVKcԩgB|UmEe}k1ƕo =4B/rBC.;gheHѬ:=FOΒȎS}^>Axtf 0QCCZ'|fu]1LҠ[ /2L] Xdw GPqE+4Ile/Ɠ{E{K'Wu%,1LEZO}O.&t9O+EmMǤH:gC2(#A (5鹚(4ݜ)v\7&p25w_;G,yʱV~xyo&iāqqnƙŸĤ>ȎG.;.s%:eeCQ2Oh9)Z@Va &kdȳG)TD! :eL?2[#Prmx4*NYw;󧍍ooܻooSrv#3.Itqq -z_oۀwNon޻ oX072?&gK `{=É7$߬q?YuiqGx@+Kgہb{I P[IJ(9Z%JJD%8PnR 0 P-ׯ0 %Ga{ɆLQS>?F O3'k f Y+#!(n@np%(D tqA4gplm,ecJėo!$u8ő@'X3ra5?KF(ΙbɩX .T[-)Iδ< *D Kۀrp)fy 2S}I/ּy?&*M~nƊwkne"E?A6CY@_ݠLy}knsǸO~jόm6w-<}upާl:<}f{/ev X@_=!i#iQ92DdT܆ NqA!D Dz6&iXiݳ16ܞ |fSs6 g\n8([ao.3zZJ|tui8'=Bj?bq .)Qw$I+tHf'%3 vg?%KF ~Ӷ? P8ۍ!siK?}׋ɿc2O^G24_&-qKnOdo`e`RsXuET*{'S8]l6Nl6klT{reMt@6 S4#ȳq>%l~٥GuEP3Ft:L$ҪpnY܈sY$$ӸF謿BsOc49'iUv)5?p;Ӹ>mnfWou7f-#bOF: USa=O3V}Ikml* T48T0b9dp&iE;UsA EAX(JbTq JvU9b<Ȃދ碙@0Hw4,Z}Pv}𡙞҆ ZI əܫU(٠AIeZ$ +VoT2鮸=y u֮OjN!.j_q ZCÙ_YIwv| NDt} VԪR-%XKBZ1]—9(}haq庁3/\.ſ bn@RauXQ@鹼lV RBRe/ aȝᚶENԋNfS?P<ɩSbf$Ğm%t9;RQ!5( ;z-M'Ĥ?NKu!0Ucsunz/`J'lktA^DWAB?ceHE/ I*-3hw!( UdX_NsKvg^Ic{gx|PZWMwG6%x8b~zC"Ac;HћNt{Y84y=w2xa҂Q (6gh"uZ!ypk_vł0NP@.H@܀Y=P~c_C1Sp FB 5⨭(| 6j1_gyHIQQޙ$7͹֓ήx4v.''؍LygߪO1Sn˄sl]HN>5W:hܑp|[l4gf v:o7]kpa}b;@琀eNnrLs1gH>f=w7N' [t,?s~wۄƽ?wRon|uomuA]B뉊9AOb:,[d$IzDGc]MHUnT䯻@r㘢߸ ! ʤC&,j ?0ívu}su󮪾u޺ji%$pcݻ t6'hI ?߼ m'}o{[w|C6ȆlՋi:=\rdT"g3PN~5sln<[IT_tӳOoyol޿o^&?1/;1Flnlmll݉m>8V+2K8C6y11RC\<& Xi>% I`%(7^)q |U_|d(❦$wB [5Y6($Ѝ/</Hm T?ҡrvrH,3E5BiԦh!f gfM]>K >{&\s!=8g:GxUH3YT%b_CuwiE89UGAw:* & ^Ș㌓zS"遤?J"(ɡ1G׃`9倴z8V*f-Z:E2#O Z4b82<4J(xlNgja<Hn]e'#%!J̐ (( Ƹ~O{P]֤ଃ (Bp-EvoCٷ,lyJLKN7V8I%!I1 d6ާeob9G==x;<܅Ӂ`W\׭`g-`mm҅>R&gXρt8eG p괩(##fwے9'g; SulFSvCO0mQQ`ٴsOL.j'8}KN%S|}G^S`&J1R_e6sW+eAI 3 N0[jL=nX_>1R#=H(PzG7W!P#?zBV5{[ gū'р Czަ(-t"l]+rml%+.cs#31+ɫ|pPNv@~ A l1b9c4yu}jiƟʻ.S0C nAЏ{]?*~bXz՟9v['R& / a>vERMSi!O.Ŵ6fzw]c>!7; 2B$ ֐ؓ |v9E|='c @ƌAH83? 67B?ŏg;zӃWPM?VccM$ DH%Oq۵t8([p5ݸBՁ8#dYꚏ]cs9)@Feu6j48j#A% 3nF;458T|l%'iWcaݤ]1 =:oAijAG0(Wc_ Ĥ/͓T-8D lwό4ǯGگٱW/>cy񷑒 Szѷ;`k?W>b{wy<}w&?r%G6nlmO66ֿF:ͱzc$s̆}'8ߋss~/s)P!Io;`$xCZeiw:2=:եki ÞduJxvPB?Qoz>L>=â|͍;so |`c6xs}o=n(a?Ȋ2PA_<e).)秧H6Fн@#% )k@:YXR(|WXKEon-33p8hp@Y){ p2XtT(A(l ˆ$D- pAp5"AT6r{?4@x@F`^3"?f `( X.*yγ<}OdSV9~L)&8 4qvau7"` y]ƒ oBֈG[u'JNB]Ot?|HEV[*19d,Rҍ͊a)7[6rL caX+]칭0Ig]N#u(B0jꢴiMm•Ť2HHRtP5@<=õ_b:%jsp xbE.#s9hcA B< `cǔH|<1G'*Z08A"yk!JA/3AV$%hDg #6]Ҧ/5)T:0Y5C.}WRCjo0PV`(+FVͭScBRNvGS.W 1%DLKN=P/cjQ{}i/ك.9sHw2x>LYX6-)ۄ+&Kq6WoEg*_3s@FMߤKm' UƶqۿbVtbX<uawX8˴۞P;ə!=Qwg}]>a c+Юoby:?4_gp>6"LϦ)T>p \嗍xB%j}/e@n)<x#,8`P\ZB0i:%dr(WNxXy  ;!1M9gL?I>|Nۺ%7@,,ߪɶ%6 ȩ~D@HG̓Ff*©^sB)7?:2AĮ8#h TS<5[h,iFlkT^I>7f>_E?xwuNՅKn1,yn BְP+ RcB|L] O0`~j=0YA-d.DUзM˂ &%CQ`3ǧ .><>U-X&?w90kA^hZu.'6siD4{ :!pTxGcI2 J aQ'R^ G_;' jfLǨ%EB )g*"ƵO!e܎v VX#D5M@RXs3U4BjNy<ˑ9N:72̣z*hB%JVjJIEEhQ|lUK W3%X愔sol"rcc\dA*y={>P^3kKnKK1K'[3&PTӾeՅ3IHl.|\Ԉ]I Ao\fˤb6pl2iUD4ZV Coa+H1͗ i%V՝[4vr'PAu$+N(tX^mU"Mm%͍睙O pь%cE@+߇mwSd9L(V?m]UY2;b";%igJb\) f)5IZhr4mZ46!>m:L Yn\<} rfe!!C9〫Jyt d(Q\74M# ^i3u3B?eOKey" C.~r"-6Ԧ?2W tC_%ZD6zEϗ%l&nFN4%13'lir#2.a҆jM ;x" [[$($*=*x$҃:4s`=I^<[ͦҭz7]M;x5::mdxd3Eu<]2ߊ5TJ'lekSjtWxEVhX'~aQ; nؤco~70 }Bz'(^~m9T2VאbD=8uR%YR`&Ƹk5B"B'f2Sa{֚n|ŷ@cME$oc7Ft5cs̘N%63(1̸%;7!*0ÆH4ڢ] gYli{6Wԅ+iAv8X'Oy 1-i0d &3%,;ʼn;z;=Y$K&Q`(K6u߰ܣyVv„> ;&@6[luB/f\[ /*d| ƇSDžjZL!5~U>aC{\"蚋zX7Tw)Ta/j:=-_+38\-(m|V+@cSE\<v/[P_6<^7H]Jgܰ  alK4\V Q y9!2E#h+R2[Z.4Hl}v)*q9 s K~?Ii?l$&i$=3EK_hfd Aά-6wךMok|qy:@G$.i8{pՌ>x V"j!x 0pQSK/ u%FgatmYOS7eTnd .V#)EPd]ఔ5{vijںU:q+@U8kD5 *ljE<_e 2< @#DxG7jٽQ8[S_gúz 776oic{ `O뛛n[Tk dH ecϘGCg2UmEI/%[.Kav\xWƒC! Hgb/YL5Hy:TeOdc5Hw{4ҷfi&6WwQ5M"1 +CF(`gW~5Y% P a%:ԓf&(aPbC%q S&6^Zڃt?yz+x4Rkܐe3 s.dE[$" Y<۾GII?Y'ABuYC̫?y9ۯAG."wFiwzP.)r!]rlUhH^뵱g}Au?zڮ;7 %B6(.4b@o1 $;:4Ox@*2*"nM# 9%13mlB:.IFUogWh RaLEW@nQRBۨp0YҥN3å?OlwsӔ۸wgxȟ {5d㛭M7c|~k^G9CRv2r?b %b| UC}x}b}n)8R ]$[#{8Ylt\"I VN MCŬ K"m.aZƔadݾ<\O3X|Hlit;9?77qt0٘U *l#ˁ}%ok0=Iub.crFveek;\k[|sgvn'Ov8kzae80-(߯vV*Mq-T7 z6Qp%r6`*T5~z|aʵޘE_5 qiW>_> > \SI =(F g^_(Ry!'m)؏Vҫ'nrtD{Wz@EN:'<N2I\M묁̠}P["1شf䒎 c ALkc}suJZf:ٸ|T..ew:]9+/`Q,^5rr :݉ R_Bزlk?[1žnI7B^ O:9zX=Fx$as;Q' '63xk恪e] k`k0l"CGrv9B&FHFnOQ7Kv3ZLQbmɏ?ިBՑe2 :FO>L SH%91! w/^x}O,BOvP0Ơ0/f Bs@K.:ݶe~c.+/[LB[@UA*ANp3JW1"X͗I#W28!nQTL@x.V}G|27"]SZ_'U>Ԭ%AEZW2BOxpTfĮ2:b"9IQ&PxB_/x,A8Њ1O(. m}.'!?e*@d`2a\Yk޿\HbfssT{"-L{5 7^{>AR=*dFN8yvhx($HP"#4?>CO1Ĩۇn{L56aunEa S9 =uL7*!JƝ1Zoj|j^f-<|Ņ:,mP%{.A~SՏ%et?'>^Fa-oQJ0j/"ڛL"/P% +gP=WK&bmknfEaN, =FZDh--D8eSY !8-n)6됑z"5e*itq|UϜˈH'>d⨊u ǔ1)KM&QzB[!U_c@+XY nIV@^9c(.d|QpǵDc~lf|ec=+ފA8#ymD)JRjзTEfed^9 2H᜺ ylb*<,FUgbs8_GGDzf.LA&6n_vo6Aw[A;P/CE%4AL %́GN"Cʜ"lx>+2!NON@+]6JVﭳՀC*whU:))Ѥ*Nm/NLsr"l!q,?0 WⲶ*Z836C2Q&)$ҒUzI Kí /o%t Pfa3+6h"qr(!4QR!D>'okW28Jp?NU#Y+Ya` 5k~s,7OX-~O|6n߾{n޹~o?X[D?£%o &y~cӴ7YzmR7 iG2噝uNp)̓wqaq[u*(s&p󃗻@MnȐT#ɍgdGFV(+@!UeW`\ t 8iJņ A Hֱ_ԑJOÎ`]Te!nl%v`zݶ]XV8xiQaafnf[ؖ1d/*=\u8UU \!Hnj zգD1ԏeA1aucFH;߼a~vM歖ae~:N B"F)#J (J61QcSQah*YT,܂u.ԍ?0v@x&o;*W"YiW_<>i㝃'W{O-r"W V}Wf̫a֧) @Ci^=U{ 4y5ҿ\ D0-L3G>DГF4 Z"¨"w\ W#` -I>&;GɊn3;~x1ژbfӕ/¸r X1͜,Jq؞U5@o3[Z}(Ӝ$4 hw*L K{3in4MPeq&3p#T7}K~Ef0}2{Ж'X|(&vfW&&\Rh&\ DMkzଧZxt|&"uwQ*r:TJ8- +*242SΝRv1;N^e+F9Н"FʗDRrk5 J}Tr7fJ1ZP &o+E>Ò*_4(Ri$CO|u3-O?d hua-@%U/pb4H6}~Jv-]b(י*9`oQ$lKJVK)ǰh nT#ҐVNo;٨Z o411'eJP͙yV\E򥈭m$wHca>(eӋi.F喑D[v, "0 M(-gb3#_J, Y,Gz;5}0rRa  rcI{_Qep~潅o=CT+V4Pu[&[:,KE$9Mp2M09Fm/,}53r# AoWyp+[o<课߲TmJu Z2tyo?H%T {/V_z'^Edzv91u[ zL QfPxW WU}N @ 9e +Vsff Av~tr&π{Y؟~r^C43o ^e`p\b߬͹P$.Y{N#ס=gE; ʝW͏9pR|0np߱W#&+8i|$H[ج).$ֹn0c9ES{<7 ֍=@Y?ơXd~ į d%.=rxI#vǑ7{TsKx$j~(QLBi״PT;AtPJ1@hř ٔ7wsݍ7RrwXy{ !wΨE{Pi# h:m0O{T;ț]O_v -󰂦 25iraOR12a]B1F.aو %4C&Y9kFtK etzdz!1r19i~ >L,SY_"Lp]_)7x?WyqG@W7\n5 cm%]C >l;2R{[žS|6uOK~{;s}tE]`RzuH VvpHŌ"]oEV6f4--!W㯴FȆ"u LhrRI:+#9X=ݥNP@u*+;*ͣV,0ō!2R5t2™.2A]^>s'LO{f:7D=jUz?P0;9=|\L9-gw/gOvg_Wo޾som 6Φ*DJ 8@n`>ʬ8`7YM6'(42D[n:~ 1Ԋ>>%Lac9n=SF.xM%OK믁nrz) -}mD1U7#s2L1z|6f; \'c6d Qk]pn:o~u)u;2T [=!/k0'BQD=Q* < ^S P.99q.&td^|S]祣Ȇƫrzh(HPUCOHXF#/FlvI K4ROiqsWyV̍tZ$.A ٢AE0L֑@c^1)5Js |)qQ!l'-LG{UX$6g LB$䁪a)o68b4rn.6eYVE }4w䀈{Yl qUpΰ}gj .$x^&1>:!'}zGwי'/w*j8BFku+ֻ 8\橖(teyS&fk |BxgcpfEcl#@Eڽa(2m:hǗ|ٱ¡:Fjcڍ+[;'ZqӚgkɾ&F6\K5.m!V&l|XKhoNB#N Rn03k@ƃȸbh5C=v0 `daȨs =oQ)xt`ۻc\;,Ao0gGqWOc|cc%:LzeXIߙAR-3LoN'zU'_EjkҪ+K0 ( - ![a@p8i;rk_a:9|OX3FYR1 7$AQÇ,FeI_og!n͘S*,{xl"מ])f pO4󻗩E#Քo7noxJ]30{C$T&3CXӮ]?"tIt/̻uul)ceٸqL0OrlCjэG'GNNQ>~ͧ#$k^ٹsxhGF@dvT$"KuV&:tsPs%8 5y{یU ,„i,F;o,!Y%^C:Cߠ'8Û1fΞ 8\ݡ$-oV7 GT/6\_,< > }uext[ꢻq9d gL]m,snD+1Y4L,9.3a(w)J.v$p=$&p\,Jk">9`[;ʁ\7Oߓ9WT,wx(ؓ>(E;*֌x?x?[T4Px] -!1Ieo0z oF uH%vpݜb,ڦ(I3W]P۴:c%?}//w+k.4<}✨e9)oFG0lAs(Y Vl"MF920`o @5+0F1-(CX~h~>+~iiBljh=&o_) Oou_fP QI,c]KŀrmjV'> T433(2:x7F]o䮪L>|X%v/c>%z^M/o+NmmˋÝ t{pMon#o}m||&]m|=Sk u[::g,0=֜d1!/U5hGuim&,+YcCϢ*Gc[0x0PF,Xz厫NT:l8O5D侱Nͻ@hbP=g|~lg:4%pSvRʅ,fֽJn/C]XmVc|=~x(?PPek)|3OˋY #qFSTfnN0+O$R֙P%egsҔ*F,bAQp6dle@e\^w+.vrlÉ` Uf$ #Qebv(c IIi$B/+fpV;ͬ1 7Őc_z*o6jЯ$|.Wi2je|i_̓%/.*jtuS1 y..ߢ3<W$WRكnJ&gٗJe?hvPN =inC>Oܘr)?ȟ9hBBht hb#%Oɴj+A,%Rմ ˸9Ķޚ oϥ&ssUF}%Y̫%Ǒ%vd *Nmw]]]}9Odg>>2M+Bd 4sDîB2HS.a؝zeu QAth>MGIAލwhmSmo_1оTs(y#~cOt!W cBliPo7Ayk/wK/s|A%S6'gc'/j椀 kD+[G2ˢmYIf4L3NS8ЖΗ?lTu[fFxkJm2} aO8ЖU<01KFm\ci^JGZr*EVAˋ0c*SG bR8'P{,mف;,5W+mT>GW5 ٳ98P7NM/i&#]5\E _6C80O1'}!>π!]t>|pnA1Y' 4kZ'Tf(I=qtgG(ƗL _JZtɇak缷2a,Ќd ulx$Ҫ#nѷb^@%3ɶ(,(&Q Bw@HB7Rʾ͋独1gmwWMٓ&|7pžH AD9/OѨ&lgѣ$`a>6KP~eE^6)Kr̞ q\hI>,$ǵv|l!xn{Ӵ+,b l%95e64)gc7xXq^mY7-aX=4,0Rx  4eCMX0k.򉋕 Һ4Gx꒗F?Fg_<}(' fiݎ; 0}O7t+S?Á+Y:'݅q_c PüD>P2@W ;-I_u:ؕhϸEH`fr(<֖SzEb>ESSSIChzFd=X[2E*ۣiRvNԋ(i"ܲgi,s0-H0 .YŘ6b$VPqUbuK~jF8,~(/$hr[fj+"Qjl5l([GK96sDcHԇ!'X8\'ոrSmq[cR\ qǣ4S-V0NEQK"jbe lqkb&Wu͡cV]Y,b@pV5 \WvL-)׳IL,{)J";ik0ꦙۅy}]EZrC7R 4: 4{!C3ýrgU.ҿߣ`mA>+1Ԝg_T1[!<]jTЗPW]pߖAgӰ9ڂ 3E]9?iԬb̍SpYJe 舛r/پ;χ5 e#Xq@zEc]0n^D@GZT)i࿪~o}٭w5`hK宺զ"aK s&Y++UzP uNg'uuftz4Zn\m4[4^ .h1G Ί7zXQǯ*/+HU7c3Oi799!l5$XtmH,\O@ k6֞$*;.8KTkD3oޗKE]2zmd)םnhx֪kȒ',H1?q>c7"786|AZ^D3"hm}YMhvJ Su>1?zt|iZq|W0n0` ,*EqmkͪDb-#YJ/.0D"J;+>ȭ _.,`FUҀZo2ss ߒG E ̹2#2I1%e1Tfғb: $AB'e86K=%KN6-_q:wIiN3=e0qFG䷬ȥ\A9ʏ;Oޯ;&IcavOKtɲ >$뻃."`PϻBA ;TeܗjϛsGف,c :ٵL#ALB5_.{Ѕ fҙ61 axà!tE$  ͠Ц*gv $[@ࣀH:}̊c׏q|ypBCOᢐ ;')ӷixܐ(d2*ac.ڻ%)9l2e-PmSoanz[gH!#SSMKD)xFQGw%ѕR؃(p>%G{/KD^CB;|9p'LIЂu\NC9x..3ټyכ[W #9*)ƙ*r܋K2GOLeϽ^y %YՊɤ{nnH1rBՋR76t>Y"pYV(ϤK w99ߩo*SYdEaPY?yy W9iPCi*3e}a-15-IoK eS8!%h^f53Gu-#aWAēӍW{[┺4@P*,{m a̽dSKhAjh Ktl[-|p%aFat =91 }=//cBqFT2[pۃ46IөS)/u'iAa'G9>`)OH%I"t+kMbiE$'5;Q} S98xrY/fCvň"[ 1rXqix(hbR kX%)XSG']LىΛ.HL59lc~FK:wAt %PLfԁ*Bبn)ZKxdo*!0>D.M pR$@) TJa6mfgfJHSC6"arѣZk$KȒh:qހ}ĵ_.!bIt vlXfA/(( ٔ-aV%> ;EF.*_U>Sjdr)Ɋfp ,!ڭA$BENƊXPre4>Hr%]RGdWJ~ERԍQTM Sn ucRlVsRԇXqg~g0}.VjB:R_CE]KJ982!(]Zb<r)SmM)~7$܄PȪ 8$dW:Y/nuqj3Z'oMdpQ攑ʮTe.qva/PkHoN]o>tlYũʪ0M!vgy F\>3DiLJ|p-V"v]^wmI" SA"¨d""e݁_,;ҀPrmX7UJ^$q1#0 e1a_")$D| ~&I1bJ*hy*@t&1UNN7]? 8!tY0A@OVO1ȵ[Г$%Pݛ.qv#N]ݫ'hlvzu>83L,Kbnβ?<ݶey." @,HmasɹM8._5fb^.lANgb@8gSMƟ׿5V 1*ۑå8n9 uѴ弪|x2 -v+7gavltyǟcnP/@G} }~Q#ů5 ԙWcW^`=A Ѡ5lfS[3mֱK9(- g`(M-2N)IQ!HjryZ tLQ>qrFgV8fXグBZq&nJni>$9^FW;R'~IWUj2aڞIU`iG\)mT:v7-Yʳ>)&nfEl[ Y"V V,Oi1u>^(*#EG8cS]= OM<&wt! ~E&yز9V;vD Qi  ej9ZTnDzFql(p>LtVr3=;Nk4[[ 5bm/*QTOf = $ [/g2􌉹s'/~<qxGَf78\Tdqedow6cK\0My#Ϣ]kz ӞjJ⸻M9#ޣ^3b<)N#~UCOxm8mTey6[SUC Rg)RD;jexG2A"8e%2D G `&) ~2Vx {1@Gtc!JxCG %"=k&Q~a}VE^AF~зhq6EfSpK:aY0lFkn Y{Fz>Pӝ {s21;#~哯uUؼ.Hx8&&H?#1 G ıkNicz#;‚kM@E=/Mb (r:gh 1 ns Ӎ bG ڼBjxymص.i۷^Ys%7Ae#3ݳ)=n'IAWnɓ{s/0-iJABHmvLfo{зɋ-8UAzzVA/*r%_ll|~j|5|%EZ'^y:y}s#>{>%S=1I9Fb^МvCĢ YG9} 9,&ꦎbMfCMB]>r#]Kqo릌D!{t:F!~=YQI ^wlzuxd"~uJ}IH7F=mrFLvm)X5xPe"4e'{!i1:yA%aR3b54.oU| nC%N A^!e]%WgFBmXJ6 ` 9!bDC3X.='ʐ9p1GMч,MJv!~*Ynq۫tAU?'nw1scDDws3 7pEI_~ucSс*i:ܥ@vD c,hIM΍ mJCBzhUёJ,<',a:Ip#juWr1lr㪭fsʛ0)\ Mo@}VHⷝ})_ZUU/w4(N%:_uci$a7mt?PvGpHc/ە8aj|&x:nW멜9kvgFb='D`f_L0cX,Jxpj{EO0R兪>i/6nAzf"Ȥq*@8"ͯ)_$d _t*AKB0!,čx"D0豇yy!%33_)_J{Ri))!^eB,R_[$Mpײg4xn+Z0 &X{b.X7m[o>#x*M!8y5(dI%ߜ(iHoͦA*+_uz^&zRU"aH鷓+mA:1rYtxF u ܴt4/>+ e]t8҇mJhkY56LB5&ͧ9-ipݨ-hRl2%+ї6[x !Ez%vE:7#.0G'Tm?y`=#Fs_ þMaLPL9 xu5EHfYz޺"] (t9]2~Df>ץR7K%5;'=R}O TdZHɜ8p|JIݫ=Og#zRq( w8xg۽do %+_?DG>ۇѨ>Uua {!v%{dlo_)ѻ$)!|<$y b]XK=bfS€ ߉/&{3f!iB0%~3C*f*YtǺY0e9_K4QyKPGYF랱ȶh-wLؖ( By2GM+E"萑#> r]W$BuHP@qY`Mg늳 =<-N҄M~ִ$Ҕ;9(Oaӕ8?3zr zQ6-aq51Ef!b~6.)❗R_BW*_NB 0꿧LYUmX;؎(Mk1"4ĨQ4RUPlOBWzzE%k¤Œ{Mڷn*oj'/^=1|7,C˛˘^+6\@/O1(=u?gDfˑXImK_fS%u\<C|E7EH6'WjkՌ RU ~ w ds#%19pV{J>Ir]LdrU-a!VQ6bAT(GzQ1VHhbo\q ͱ&Ǣ,Յ?KbͳmAl/@gj+^/7 (餤 !ٹ)Kktu%~]ec%,/HX%lOoZFunCGA!1T( %7k*KO 7ϮڐH[ೇU&NXkkO+R.\t&v CJwTP|%N/C `]1'Ny Zig7ʛZSmߩEhkZ^Ml $XCgssj1DrQ,єWZQz%A狭/|0f Q~ G"ZbċKРX_ћÝe&|]b爢^.P#h$Ku뜈z6=e,KߚH~#hm P~6as?PaUpDž~9=f||lo\uׯ=ۆQP>Oj^{p7>{ݔ90n]Q=x4%eab爬^"^x`b } TM&n ,y#Ťym$JKZ.kQ=]/f:ϙ,8V%`j ~uQȝUUK1S7^l[+1FfX sաk0?9 կӯPPyTޑt U ݍ0DL1GUO5y:f 4= B4<(ɎbNѮ]!5Kck~ȕk&-}02*9+u~)˸(xs \5D`r\%dABN4'2}?t3kN!`/#b?N Z:j^20<$濡.(YLî^d'ƴ\|}$.PcZ~ )!$~1uedf6y̑=}XڸAÊ>-RIB#DA$)"fNAc8_cO𺵭3.9#^LXg*R{9`8-]ddjx[ f\ 3SUxV0)$iyrUKv XM 6r@֍ 1(3NHt Qll.e#7nC/=y,gyiM(0鋫{>5cD}%S COaPeEMB3uXJagX9 a]u:<` NiB[:w< BBRpS̤qzΏ.Vb%vq"bV X eA+5|9RiS6|k(($YU ,V tL>Yw鉸 Ytk۶[ /,(QAnt%0 /ϰ"N%AjbޔMj"reT5 n㙤)3vW;l(Oc;ķvkGzL)FSM8)i}BF\Q511j30’ -mpMK§fʤC p#0h,DfW*_v^T:)ki ႌ(Iך㏭}"gӌ! iUbVT4w$;ŨVHx9&/ʕ#HC)&yhjS~n$[`Q25!_\Fs{ 4I?HW|82*k}GTAm$W\5z-s{iy[s%"꤇G.4T[5^H#qD(1AŸue] Li7]˩=b9&@dfϬT9T5gmY:maمu_/*'nnpP=[^G鰛~rFۊywyW݄ɧ&C#Ak{ ЎtZS%H@]zo 8H`p%긓!^ 62w aC$zM2{`8]n ElьGRԌt2ڮت{~ Uŀ"YвuiRa48hpp2>J{_f 5cH_W M! VFh:|,I ݪ$|Ȼ/CaǸ5Xx߀vco+ǹÞ YK 'V-l|@5.':P{^7n(Ӊ**Ez ^]XSԪW=hf̆ Wzn%'/\G kmb1$mx׊[3)qN' z;5 4G[h㾊A7P؞aADm Ί?GX9v<(g;R->j̭%}T 6"ߺo|e`Z^'"\'AȢy>{FTb:5Ɯs&AV' CXࣔUo|I4wz biA< x*HZ3:/'b_I*-fpFwxXXYZ7f/ LpAȖicЇ\+8xV4+x|?ya,'|2 zAz2N^i;Xn"Xw}ok9@PnxMdٔyn΁9j*ZeVnR|wāa'mRp=4JڍN~`wqöER""?4?KFl:% >P\W(J_N ߐ ".;F 5+yNq39;C=:a5w {x& Of);F[s8ؙtAy .4ojk3c{IBR*pi 8:}N~D^M>@;)Yx-&ƼXW9*ΡZy7 /BvY[w-m~ԩ?-y 5:bJ\DB!9rBu+$̀=]tț?'&&\Aau׵1kpu+A9 ~ClT7h,zY5Ę6'TR <ʇ? /ϼrWJ])~ Yve m.a+=!U ?6L0~j `;ta&KaEm_6F[kB$Y{ H/K٥NˆƄp`5G H2U%ikh5X64-HB+\].\ltBN^h0ֱú *B[f$~1F|MlЋLՁ+o`9rڅ(&k,R:HqE>7wZBt?G~.tN7qv~.31a>Y.>ٕn Wڃނ/Y2'trS7K?"?Q  1i5R!l3 V'}=F%:F$򒖛M(]`Շ?Jad!5M9=MZ |{W$L`(+z%\嗷[^Qq/䋧ŅWagtӳOWl3s= z/. {ә |{=Lmw~Ex>=?[';RE6E_Xo \8WWZji?;ec饿 fٟR63Y;]SsyP GK7=jJ#씯Bf yќh¬ijk-Ƚɥ~0s>}y^ E#)$U Cp _|sk#w>T|o},94eOO|$s>Xu#Y<",$9r`h&{>3o:HHE}tnmi&yzw?܆ 4!Nn:?y#o54j1@Vã.TLϽ{ ?*[9gh侳c0#VWxqvhmɝɢ?AzdXν(v>j $=#uSY5АT<kSSqZRD ljnfiҰJS( /}= ʬEl& @FgW( 7,d8ظ7,BQH`3dM LX3Dh,fS EŤ鉡Qsh(t>v Ⱦ Pa1LS߄ȷuUԠ>M-k>bYPH0Ks_ڈm9mSpKZB1Y #6jL\%tc s*D.1p>9Ms=/ՙO-XZw6|9bYruv(Yg7e4ϕld@0eeF׺4Xul3D4C]@OqVE򼲎}׈mp! w@S1ņ'b0|4Q`I0^0pƑx9.YmAn߂/,Ƀ83**y7|;WߌK/D>T;I _P$>sl0]30խcgpQ)z!ߝ].6g.16h:5`WRɁE ,Ɩʸ<0rZ!Si1+s+fhAx=$݌Pq#19HDrl{ W2uj]a: NNHEZ,+. ]>ӝKLuR/?<[Cx/vN[ӡG^'դ~29,WU b\s' B,"mrq䬾]݃?f٩H_$Y=\uވpzF~8a!!-Sw[\R_s MxƳh}E%"$-Si%PW8A$Pj(K?]ĎW/SœiDݮ87 y+U]|0+XO´evv̖G1,GRom!B8n](#gTQw\x}$A_paW.g7Bz^~GrJu}}XG: F{֓U"(w) -faqM}<:rvd]C|En[lFِ] )ܓȀ|!„,.`"jxAi*ڬH0ڶoccn8b'TI) 2?IlKL++a"1^@,\Sټ9*^6.WbnE`K?O|T䏓gQr66Ͻ;w;~7~6ʆ$*Pb]EC~gN!. {l7k߬Oֿٺ{u&'ɊFwX=C9O Y'lf=%T vlF_͵pu[9hugP.E`'?s#d%Rt I.ulqcۀěHϪ[..8\?yYoR'ӓ|QYA^@]eвVj )?7A 4;|n-DrIG |!Z?.q{woo%Gn͍dolcxaO_(ugtJb̈́VGىh7;>=x͙IOJ0}<wi>^Rw6zMgGEjC6A⼡mWHwcVL ",`1 KV26M;vI\ٙ[aG _gL .'K0)?H+kGne?4 Lo%>ܒNHLUl~=9^Y&7uhymH3j*VsUP5ެ ncUGthI1y|q||`8R- ::U7XͦvMH7_sOxw{7o@x߱7ݭ[w'CDm;hC ͋%0$IyYcchpeY̵+@ռq\ ڽ7${ @pzb.KB5?+nM93oW7W7[!y@n98P66o߽͍oZ7ah^6ƝMw濻 =s%c?U[x7qdo6^;?(,hC6";ҁh^F@=Ȋ8ao*4^Æy6r \MuP|Y$ui4ΖlP|{ 0GnxM"E-T>}g!Rzg M%j22j4M0DIGɗ QS3yy7]t" rDpb c2)= k˂ŶJw/!ip_MN-W)9KΆjfά@a `@ @4{%Kr ?LനGUx\Ißx`/GVt': R?wf< 깎MiBI"ʿJ" +$@s4)&zĄ{GY)aaySSRBV3" ٓk=R W"L ݳ,b9_=pͷLd)poCWӺ%N𙟃OV,`0je݆3 =|6;U+ϥw/.Y4dOn>}|`-qb|q(Jo H,J77zy&`n-}?9>vj=H=Gs`zk'IJ^)%RjjrOj`6]%ֶO(.JFsPC,3fːo(tGHe /PJZi+mSi2'QTԂQ/Z<$jZ|aTgL($3"߀P:;/z~:ʊ'f"LhtNUnvaZy Q4Vc ˁ ]I$S,KؓZ⢞ܤuNVQN+UmF+W}*z޸X^0H]_9p[YE4)Pn~| n8eÖPhQ]._1Uqѥ&ë"@)xW c+ jTRG@$ c" mͨa./LunQ9Ae0[y~2l|G~T +0C(60#iqA]2 EU qj[jso2"Ƞи\. Q7^c>u:~Xߙm *ݧo" qo7O3kiQ:dm;6wi؁~kjZjMiKu{H c$b{Iw5.X~1Z8r'_҃˹H(TgFZbqx,|h e(K#!0F֧W EJZrA_X^vLSb {Gllr`~%EO?uéuK77&'߉6!MV ǯtE;u;kqg )iK{ yk2 7![D=eɉm)M!4hޛJ֥O.$=5wٙM@B`C>j#ȹT,>T ?uzUAm׽9ڒ6eYzaP_&b {~}k Q1J_2THQmrl j3-p!kz{BGh!䤗|:ݰpơaɨHɻםR7u^oB]|^ܓiW˷_wo2Gu&PUꤴ]#e;'x3d8{tQ~7k_ctx7e㹍ز堆St9æC (X+SÜ4(P`]LuܱdQ '(oj3I徼xdU HkUO`b!n6'MT53R+2:Ü{̦TЃ\G阪IGAűlkZ@[7 su=,k_щt~d($#$Y U@r$%|rug{ }r qҪʕф .Oehg2g78{X*F 1N8T@TR0F=A8jl5"7E4s&0`ZIaEUlᅱfF ՟z_铮!,AK).x$EU2#  ,e^@(3gUgE^z#F*&< 1$(z-Sn28'<ZēƆ\짞'zQg|nZ{w_${^#됴,;O_'[C@]4tabbuY1x]*X>^6zɣoC)sg 9@BpO>,MU\@ _> $SHp?9w4w"G^%'l@{ OmL69MU.@tTajy :*d̙)>Wc꘢yC$^#djj9>G$[zMN6B d:bGrWo_J$V+Zat:&שZimx3&e#ixER÷^^nrl~҈ވ?KMni Q:xkA 1̈]Ѽ=**bb׺XԺ^ ',։ ,ڠQDH;#*(y˝/ًW{O냟v=9}ex9"y AwLf$|RE$fa1&!!O5Aي.\EWc$w[b6@g |:{wtXl€.)Do%-sdl|1ӑG!<"\P|Mpq+P$I1[8_k36/^3`>3D'4@j,ץ3Ghs i@單p/-:yxkPk/_`wooX]?sjy`_u#<&yyM`Z:]ukܓ]O$ !w?U\&O7䥹p)8``wwn^u[Gy ޼L+9/%=ZJm'FE=> X.#Wzו;W*5AurcjiqS9$+VS7"@JkVUTآFZYVոF{Mf9hr(,PJ D^N" 21Y#]_TGW 9NP|Y̳ђ(s ^:l,lgP>j  wF%pk';Yo:+)u쵏frKALx /l$xr\~Rӧ2+tLUBsjUӧ 1 #ꝭV\qz;4'X 9 iMld+JU'mv4*.l]Ɨ"iWJrtOFBF'kL+PV/81zlBO#wgK\cSUeIs{/\)E:-ؚ dy"@c|dcP}\<(djܧi>^xvrG+{-xa:4ˠdjG{u5DMߩ`.G"@ d9?'u>{ZCvLW%Muk" a&JW%Lۗu8TW{w*M:=JP'YtHGcIoQlC<_ek ,cy~7fHҕٍ0I$y"gcU?V'v]>gg*s|yαsˀoo mU=lW9)[ᛏ_2߫d.vH (C gAL\/ehhVLiBu'ꮯ]xlcf(G5J8QRrXN&tgN U#3)gD6#ԑP[q:>DؓBAxi lWSJGoo絩c^Z}%"߰Z+~"u.{SyVgZW,~MR^3.3_6b]%(<9w'hLɾCh b˦X^̥ Of(! jPՎ5$|@7-i6@k#,7eu}ay `A 㢄^;zERWKT8yzP}/=Y=8|gYE6;oIt lN U!H~]B h./銗)/S]0w/̷|'B5<ff5SSIԯr ą78 rYǛ"BCH^%o]qDcZ݁:F>c{^ѹhW_ ؿE=ĂTz)zծ^S'>{-~>xI7u40ȂO뿈Շv#&;PTu7 }l/DBˇ> }nǝIr҂:^ 'bz *&g>=Z^Edܝʀ~f*$r ƃ7>Pzkc?ѝ"Mc کXm;;Dw>2h ,sӢ|AC͝qv0b4_OiygSuٻQ{oqo> rBJiu"K:!0$'0Ž/r__v}uW @9J,r?~%U<\NYg(?GVdNV[`Jm |bK:Axʨs3-*,l;u,r{C=(؊cF,7~`۵F ʖMW=w$ba+֢S(|NϗZ>0nƆ Y~kdDtRt NpiJ!d :w&WD(FcF@S>0`Dú4b:gc S G2+J(3ZKFę?y΍ oci `ۇtC(g23DE@5K 8eɼNg3Ȇ̒%f9{k|e/kUL}T{4j|cm[0,v^e4)MkrMҵ/N>3&S{a82i%ΰԸ(bFKՊĬ~[1?bN1N[ fogoΝ{a{ov}44>& hھ;0}wEgvO[޺lm߽s/? ҌKl2ݖAL*tԟ:Y:O'm('30~P~ \AßS2fi:S%c'g /#s/ L#@}eDL~zZx-6<4O1FU>̀lLvzm%|K|" V=Cu{~wA\P)#(yt3K)j(-R5UX~ >zle}k'N 3 p(KeapR퐻X zX8.}bDa(UbßbEte@] ɷ:~4}c_ZrYjUEn[dg:z(QgAPa?Z< r#LC Ho0cbDC釂 ,`֒+b^L~3C17P YKpnH`ycalE3Vڮ+j ^u)C2/ e̬ai4)5S wC2t8Ad鵪`8Y,;DV?ozLTV%dQ! ?F s?*+]duH'OϊB?|swsTjQfk^;w{ȣ e2%֞\b3b:͔"(?iwaQ6߻0X@(mYM<3(It!ߎ^zJ(⩇rR9WU0G#^ҽ&8}bl?cEp\?-K I$7Yn :;B99ʆ9G䂰hifG K{WΟtp>5b '6F5g=ppGr &M8tU5! ܞ @/hG%`%8a`brCқ/ L!5ir1#oL@1`Żf 5"wb*̹˩idW+&iHڰd:sm}}RǑk9wDl+5*x2/ uEؿ7Do77?d\ BĀl&\%[(vHgf"fs*!f[nP*C* <(m d ?@" H3<ף:%ౄd[nm=3A*k,F^[5$FGޖ꼠{tgz1GoQS|tlt5gtdxFd$g'0_4J ul va:t~qiq)e^30`4kp:zGOLJʯ/jwDz =f?V7HJ`@bCO?"m @uY`UCG}X9% 凱Rv-M%cll  !̃bbh`ۺ>d#t(=>% b~[Q"|b]q1Q=2^7kSR=PPt39J -@'B9%zg8Ն?iP S#sXBJl_=T~FCN,|QPRrPw'6%Mz7'J>vD>D_#E^|*L/+Mo@,Nr6/&=+u^ElVKDe24TО2N}l7 x?8 ; ,xiJ4"7  IP DH!)ɌݹPa$!IPc-ϟt<2!滹]=+ Hi buJxxB׺N_HA{k|<_8 5g{( 1Pfݾ;jK~^~+v6p2`CWxfZc:jeHZ;p1Y` Z\Fb,eo*צy6)ft(Vt v.*^Dr&V^F_VyK5ٯyzY」U)NWl>M؞ne*>L8{ .}GJGӟ 8!G= ruxPz=y*2'm_̼񋯣ΪIl@jWuzАvZRhPāvX lOQ6R{8flܓ,I>m8`mU?C+ 1Lz> /X-(L\I X*%xe_,|{jP^۫rn:R k8( !3ĩu.M6`z"60ITw7O#=Oi|qǺǑ=`_i (j'M'pϙ% 8Ft rh)xgi8v"g'_VFzM0l$%JN'7j]I`fx` ?bJXCP!a5hEl|% c˷+Z%oA~,F w2[6I6&QAR1m]0[òs5FA:RoDO(A1+/0J.&d^j̆O.c>ËfExkMp1 U|׬<3%VG0t8ʠa`Ӫvڵ$ߛ.'<ƿVRǴ*pb_ɭɱA)+b6 sO!R]FWv&n9acp,V{fڵ1ZUZ1.0I;}Uc-N=QNb~ T5NIltĩ+|U"S[lJ=J6LK/ԲMZ UJlu0a!E /H *1$}Y#xvZz @mTd|-Ց=JyоqiG0pԕ)NFC,|E%3bN&vY"xjW;S-E1Jb92m& +"chiȒ-<7j| ko;*6m)fr<'AT|M;G ܠ{1Ash$5;I9oQq•}gSߜ\^CC{+>]t{ښXWFBgaԻ#/WĴ*r͓;G)bUcc- }։Xmv J OkgzB<;Le+K2R| K>pC甎tzՕ%3L|d;j8 Ļg>ϸL%h:zz8ygǝ/$LE#h?;m%]ʔ#$lbY>^hq]@^vMU[=X rMG-wWKfWlf6zkoCFew@VI||2?_=w旐~::[_݄\ VܴBx̼*,N\"TMGF6[?;: P= v<['*mvĨB503=(jI)l|Ow& P:w}iTG/LH'TpIikrt>0"yAۙ"SO}z^PJo!uꦕN<_dc8m>3˴oT$c-.+zU2eشx-w"é]E/ROu7ynԷg_UE1Rm`Fhm:P<׉ mܦ#>H1'AI.T lR #D ߆bżu݈:@`۵I:^ΦFDc›Jmd{#Eަy x,r14@Di^Tpw >ӺJ?[^"X0+]wM&;tѕR';C>-d WB `Jv'Ԧ8ֺ> ]#_R/fr$HΖ!%$jx^KغYFe/ѻ%59K%CarXtBWF(VV\L~;KϤ:-]|Z/Wkeom\+JRm-iCO:ZS0u~EǻR5OlBa,4 O^U}HsUt+SOeaP3~~eIfM2@0EDce8Hzu}lbVr;[-~Zsr@TvGi=ĕh6lgua5F+ ~O's:zcu|ኰyGn#*SLFlߕFh+c4ZkknQp/}MOLY.߮Q;iN&`WلU!%m$(m1h]+cM ۴>y[FE4v"=R5pEhuePwez˱EC^`(\L.F^lBt=CJ Gan̛<go!w>y"S<Fo1Z[qttpU+$")lŖ(N 0.(\ni-AO+ALeU;\JmL܅:L>r!!s;s@q1~G[rt7rv_[qx) [-:xhf`1O7br7U%nJ s̕6쩔لJmC+~\.# ByA 9|fRK[uz(ihCnZxhڟE|+ѵB+Ns!u?d66Wz7Z7kq\fV'͋Kl2 hR 4nb7te==:>bN/Iкy4t}כUg3vbJ.U~'J_&MG=fBE QM()g4bB(9·&T }+ %^q 6ƶaG8h;{6ʾg e ]OQiOOI.BҢt"^t $L0ʈ8$Ÿ N<Œ !PC YO#bznČ\4fӥujqW!H+f1_E,è8~k^z\O:zy;b%!32"12Ptм*ҫkՓL$Ʊ,r(,.BŊ|5兀7^K/\ eLR&ZEPom%^M$8aण:!^BzCj 1y'm!>Mp|5@#&lDYB$[EA@e٠>+ӭ2=0B2)zGsځ3׵ .p:)?QfXTmwXH1#Dzpԝ x,,3:1MΒ;]fDHNK# hxqaxxGd<)q,EҠi2<;tɄ.pKdNa{E ŸE> _G*Ho,$^={G$@Y÷y6Et?!%{95 r͝. ^,9e_ v{th2ݦL%$#LOO|H672[:ɨ:@}4H|>8K?6oX˅]UUC.n q&$-܂yIʧ2.;*=a v3 haY 0|ש(,L̬s15+;qaHjWcRx􇣍P/(>p1 [OS7GJmbU?Q/\U9-~`Uè`K[@d~Z:'[=Vs@=#~ "G5G%z\Xd(ź'O[V%.g" fQ9Ӳ }o i]Љeu*.[{C@tX;Oz{t cTfn-lxL3Sj yd .7>#1ЉU]ݧ#È*~|}+P::; Z/Z b?#(v:lŬ ::kH٧dY.u7g,+/|%Ĕ[WE}k}͇adB@kRb$_Q,>Gv S;'xu-%fZ|ho[*SwQYD?a)TQڸ%g~qk@3`T/G1ݳq4\ ލv1X-(?зyAK>U4CmEACG@G5?u#l|T%)Ur˙]RtK[S7JK3Q#Btӹ8>9‡rB=uE)E;j;tØp@_LDtۓBq@5d##Smm]Ӻz9hkO_#1h(mF#g>>?]ܞe FX6g!›:92р AP8,[/kݚ75-XK$e& 7v W?֝0"P&H|K BPdhrD3RHQW]YCkX@X_K)qso8p{o>R1fbʒꐬZ^@"rD jh=+$u%{z?U•f3TqrKp[μ smLTV40}6kqoTgd7SD"BF I"-d2Eb `bR!"6?D\8;βPL A%ىqOHRl '>Rci6Y>Ј+̙h8 1k_q+7yNΊ:Dg"PVԣ#G6WH(#ФL[M| lYޚęjhLк]Pk\6S>7q4 Vp Z Wї38J&Qjpv!5:G 8*SއwuY}rfW,``AiڱBxInvS\nM1SG6!_2 ΀B]9+ C&ɒ:/cP2+<ɘ7Y xڱ7ך|]?ݗzPpNp,(D^H>=;r~;>H:aw QIL:"v{ mi`!>oWU1c֬E91PpĤJv@#%bP[NP{3|ϥ(N/u9^9V)ogMy!ȤJ|Eҕl.c)"XtP'㿵sqktPV* LJd)3Ѝ:dSvUi|y+H)mV ~{_źJ'0IbWAîh;4 n~H aBޯ11dkymQ}Yy兿ʑRܸʕN[{PXov#7͝2:պZ~G=RXpj%&Ij-\ij"#fu-t!k~me[+EV>U[XPqK^s)Ku%0nRb0kӗaNat! PVC SoE‡/9I1V#!%b >{b@D;xU * Bb*h% VE`]mgD=˟tst%vύb&eE܏V__^= 9u7}]i~2,>Dm =Uۘ:Y9:L/p!cx\{w?:]og~ S#[rg4j܊!rr Q`r [uU)s{|ϏÕMḘfzA(1R}:ò:M,) Go UqD>&BkJ`%%7;tth>f:Z`5{xcdSW#ٌl*U9|f%IN <=&*`@3CW55J]I6ˇH72x@x=7}d|F1*NLikz2I2k̆cpoMLoe.9Gl.8 ) 5Tky Fpvx*Dh5N yW+ tGidVZQY ȏ26Oi*ْx2PgEۤm)nFsw![ԆzI/D/E~8,VDo<{v2ϟ''>H,Pq܉* in ,m.';sý䢸y"h$i]AzlDG/@|P'geu96PU0G;e!|7;B7| [:kr<&Dl@YbbB$H8D,/3l"؅el i/M֬]$Q8YD"h e,[/f36 $ &ROb^>߰g{UTE;$N@߰'>R*k5bX\ 'Z y̎iaa$*H [ppta755<=>z92p㨪J<F@/c &m(z`"5(9y_6yRۛ!RM?W!`ꛈߺ:Yc|S=GD^- \8kzT?:0O+~Fݘfc>΍ u5K_q"Ԛʯq.MۙX)g?O(SsȡMƂYp}`!dz16BA?fl>ʆ,WFz27X@A *ƭ]_%Y⓬mw٬艄Q6` 3 ^de~B=p{R*vzö?u{UA%0Ә.r| \Oho(?9h6X%F] {_+2h\Df4 ԇɝk:8{v2Y4hYeٰ3T9 YwŞZ^C֨8w9(/!xq,;=%wiZʸ grpX8]ꞇįMݨy*|gzՈĮnZ?OR+fҢm6zX;ςd f|Qf3,ƺ6LoV$ʱd?y032 3JyƠ~6O#紂j6m@K)mtD}_Eg9nFa3@C«,@wXJ;LbF5kƚ~@Kr>xk'q:Y Aݳ<bg-8O{\-G P`GbV31F*1pt$(luffCw 0rǽqgPKXT}ؚ5+tj۠z\0ݨ7%[Xr1_w㈧Ίt{Ӷ+0eS0Fe(flO#D'shYr7`p7mI5; kl"1`$"3Q`4'8A~kp^krO ^_o}oK߈oƬ צ|ix:bS.dYHE)AWԜO8,Kj;$ #͡}Pʺ6#{3ɒ4Ŵlҕ{)C#$GjO6κ 8sY|A=}$~ TIۜ9n׹ަ$e2ZGfgGL[eDn ?ҴH$邲c؇ /lЮvccSJ9ysZ .jw^Ze~ۂE熴-SjC4_yf/jh`)I'/ST@A#^O*Y$=V=tY] >yTsV>3:X$ V ;sv/vsb{A@w1 W zdm#ٗ9TukVk}%h:prÛWX/S\a?GBI9wE0֟a-E()?| B1'f`!m;ⶫ"6\9W¸0p @q|zf#؈XE%MW#5fך}[Gd{JhC.Xҟ$`jU#9,IP[dp[AO)Д"EuUrL7~ }]"#:p-M9sAכ&G(2t3hs^gFٝb-JQ& Hl=ޒoXh|}pHORLDYeZ+WVqۗ\d N+^7v+rXPlAǨi&<~Wl6oVk] b|QHY,ʫWG/8xy $Gι +=ƚ}(zf٢89up];_V-/2.wŴ\׮tRFmOgM3q¶-{,KW6ضF}\άkd]dQ`e$3A8p}2t eWHtzz/IjQj-~Yb,0Wia }kH[0|_ކ8wiqˀ M-_ @aY*_JXZJrI/Z{-0">k !ju}! #{*]pEK+(i?"t/bj00wMfk lux6H >/񮇰=OeúV0j- 1%]3z!ӕ YxHt]{;ze1nj0xm"t7ғChcY~-⩦ߝ W"edզָ&.$9b1bcKf"ݩOPEH:[~W%Kiyv%Z:+$*]q22! ]d..8'y/ЕTOo"Ӷѽ +a)J[a(ͭX o8yP96]./b-%v r;[NT)жD)-&/c_ī`s ?pI}Fx""`$Vm5lMyɑ1p8lMRߖpp%WWACvp\7sU,\ϞX:Ph{h!iR\JtDuI@6$V8W?mJ *|??2u|," 7U9w1G:+(R|8X @fWBB ωUߜ%p]%l\}mAQgXE,a AOԿF: AL t(cWC9)[uL䦣T)Wھbʥb7ܹ9]!:U/;EV2\hrb, qϨxF>)**)3 x bT2nrFq EFZ.LRR(l8]"Ϫ?:etv8ۂC7 |ѳcçWb*hsg{lʑ'/raώui f6r9KHQJ8/.zGU8)GsABgs&cW;?_:EZEqIzIEfR^wn|ZgLh/b3+t<6ǬTh#5:ރ_s-\Z~WM#;P]wde(f^wqUr6v tOX4Be.けI}c@m+?x몘x.gpnhdW: M|׵e-uh%s=j/݈ Ƕh#ZWU_kZ<4Q7.j#lx-kUtcb&[-%DM76M13sTUbs>܌GkKäF]!2^t j$6G4o}I75)u R2jxg;u5jLZ]OqJO SsT/B뱽+7qH}?Fo֠dw?!?Z*n}RT9DcJnj {'uNKUM V9D˛K'2TvGJuoj2)?d22L,x%ʛGt{`.W9Dh0,ˎYPŕĨHSo#%N=β2c{ (@Z<{y1YƢV796;f_᲍W\CF)N\laq%j~Xo?ԒTRRfGN|l>?uGU//2NWf?whyb7 6/(ƪ@WEe3&#v]u"uy1fʯ|:$b vDz  :7z_Ϟ9Gܕrj䥃Hj?iK}m9pa[ Oi10oJ<9@Cw2  S8EI3xVG7L:~yVZ"8eV(W&y7TQ6zv P A\M5@fosX ^mxJ O/2E=.>ȽtϒM`d^N~<*g>DyT@RRu:C7]&cřYSnbhVܡc*I4޽(6?@T t)^ ] )8$kd2kuZ\,~1? rkW6b$52=[ŃI޾1+%xhCƁ{(j~˭_:+$*6_S̥L:W&T0"+TH]Z!/FuiBI5>@;V54( } !~D1}[,6v |l_-F6ȶd+Fʆ2!x٫f;Rae K{ռV5_fNjQI͎ 2:w ".1 VfU%`Xl1-1Nw| m+3dV1b_ҝbsUWiZqs3{^DRP}ۄceiߝ~ $vozI"UH>XdP  J^4;@Gczmh^==4B\=:_A@Í!RjB,0qX"S߽@(@֐ݎҒ3P5k+H+V*AO5YMiGd,mz]0 r$f1 ֢DK.}x}Vv[Z,N$Q >{kVMxߧƤZAغo[}I^'ѴbB*zi27[pv'Fy gzp?sx-p2& 7ڲWm[6tlnP#-(1I6dȨ3#d -JF- NtOKP%WraL=dn0R@O9 I-ys6YH4-Hbm#P_@;ϧnf%(C:[˲q+1=JLzQ@"4&L@c>c@Bq5(Lt珿X5}T,>{ΟoŜዎ2BHW08 Kf PsXR5`KTT -fLAبgb\ŌL*]u4ƳC/XA4HSfgqsz/:iBLߖ˛m#[e>O:r6+fN馁Ld4z`!g3hD+AbJr]Qz Ÿou>zZн{:1G\]*IdULmˀS{ixG6TzPMF 2A"7@x%b2 AmB1Bl̓]>YNj 53t Gpp0'3ہrv}!&}IuiAy@&:Aܲw .Z\rXXN!5WwX4kl26U6w4mWi<ޱ܄}kîD0K8 ,o&bί۳8>pZ ~ ͨuLH'&X<{|!փػ.m1I%HZbʄi(-@Ya.]Z tWZҮ 3ȻD^b&D [ֲaG$Q5H}w8mT_"TеPe7A-)$ay]hFNkE#R?yFF9AW]gK:]V%65#_EUgö]a13dgߚ|4PM;ͰrhmfTUӤ "8C5ryJ~'bLo\y9Ⱥ2W/͡?-}o:=]͆zT'4;G:|(e3(,TZt.HYLXT c1:UIP9%H]Z #ζnk/PE EP8ƒ˫x:~lCΰ\$q#'b(X/w?<jI4S3q8/nd)nkiwh;X',h]=F_P\h7N˴ch\!5|!JPUq :7F1J.~=pm^o*fFQ\; sbf l:L$17 o0F5{ L7UcnrSZ-#bZWR]`a^@U˩"\RaSgysqIGw!ԔS4`µ[JnfL"axEs>O! @-M]Uy){rSLMw0'UY~E6Uւ=y5;*z Sy r` }I\=]_{;$lJE#O  @` Y+ 6Et{p԰$Ll1H3F4)>[`q̓Ǡw.`uL+=_9!o ʔ-C.ud.rMOb—g0R}}70m3D:p$c(y# qϒ%:EWr+qjr4.FCU~b9_P4#JBw/Ց{2 fw%$&(,3¬ATί7\$.(2FYiV nu &R_^*̳Ninzy6utlTqFJ#'"k5 ?Y!m"ۣ*ݹf,uǩaMG[~M>`}=~i셼uacvS _Z>)n6Ɓ`9TFa WŁPXHm0`p|kmٙlD0.ɕ+ )Ώ5͟e1Ys R3O/:py,܏X_m K[1F"z3QGȂx|TP=sѪڧӝstZ2&mjݵ %(,Z^[/jsS*/4I__yD4L9єY ^?fdU݁+ùX ͎O K᯴i9X^;@IǴX*Ȓ{}K% :|R1M^ϧu_+_M*wQd @0fF%%Ŕ[OB ]ԨִEB d{a$(%YydK /LAYIJDȥ^78u/rvW"~9^=moFp[`g2DI:F;L='> ~Ft-=4jtk?>FeF2$ݎwHLrB:|H]"; ("dF/ܽ(DSGr6#alՠ.uze #)&e11 /f m-Z\4M`OYέI wkt{,B[{;\k&یi°Nsl mq MeyoXC/6l1 [HutT2X:fKT!a2Ú%Sj k PNg]rʻ}4$y3/m]LYmqBG܉E, VfRz 6}Yi5M j  Ty)7]ЧtXOB)6&dEhuE>@h}(ȐkGgb4ń4eQ ڜ@9BYͧ$_oCA 8t-)@(>%Q#iR^/.-X}СˬC[;IyR%Y)Mn9N,4ncT<䒛h _]f ÍNs]`0_u܎_c~i9o7;h٫ɓg]_ u>U-7n+`(0OE?|nG/qzl%b$ߔTdXM+텐 >٣~Els-$ybNqzE,D@Sb:3nuAY5! hrKi+)l@vH|ur$:WZW u7 %4d2E_QlPoV{.;ҋuݜ,l 3^>Hxtq65!bmr`SNaCfSj?WF n||: Y ,1yTm$>"g[YM5T%-p*aZB;ҹԜpQDXc#=6qYY2GF˘U79K0:9j7bU0ú'Հi# 5]m NP>PUJ  N`sABS6'he@{#mYrv6yr]c44DW kzupQ{@l\OӤE04;rJIZH`fQ<>~c qgq4$W1 PA0˅x!TLUp4u|ALѤNQ$n;Œ;%Q]4Pg<%7#ڀjF"q$uUl@B~M&[Yps!=thV"iL<~*-.ƙMA#2o"2]>Z 5UJ#WCt8ЗVmIW:Yɵ?:޾N4x@5Xj@u7Ui V׸]}+ݸȽN|@Vi'{ dd4KNL_Tc$#L y6og| / d UkK7ɖ.bL9SX;]4 8޷umds_(.e)ԸA@vs,%'mamV̈e ㊏ EL %v}d^i|z3vfB=Jw#}5ɜ\,*+by~*A=7?n.P9أ0jQxAyx>bDvz!;OW fkVj^ۼc:}yod 6[ (8ԸZbidBoU 2 p[tYɫ'Hh{mkH(gw۹8Z]jl.wC/CZ">VmLѐkŬo(_GǑ ul\}#~uPb3 Y Qn+ȭ nҖ*CUVCա}QM5Rt=!$fFR*љD:e/[ #jUuM]AႨKkb޶MF:>9cg~V nq]MtG=JTo in3{CG8ZHl=npPB -nӋ8.Ȑ$ bQr@r-ߺ:HJط>!;˥ƎA[ Bq("-AApÈylZ_9j EGI|2#BZ 3;;TG|:|Ai ZYKd Pw Lu$)/$RU"|ȵQ닥^Y2,Gq+Odml?H;*|OY$Wh&\DB iBĐ~1mвA51&"!-݅^VE0F)<3Y]ePLE+IXHSVq^1M5Wr81s]NF=\Y^|;ef l!VeGMJu+kW>eQc !nwAh%oiU1hAwW%᪌(9Wr+C5WcM9ܮ0g +qIHif9hX"#y3U[Sf`>Rbp8-r?Kmsxף"\(GOp * gҧTGÛr#'x8sI%ߏז/ Y!ߙ7e̴|UsǣSvE Z+7$qN*M*? _̷i=y>rgW<|%F|wZqFomZPSq"xbqNP,v#wz9:; 댝/m#p{Eh$:nF9#GChFB'M3Gzɚ>*,cijצu;ۡcbsQ6 ȑ]$fnODLy{M-]K@ݾ6Ɇ1JkpK/`/$!cHxJ0ݢ*ק\-V.PuҙT>L NQhN%N4$g~"?DLP///&XG xܲ$D!A_.-o4G  ߟ%ǯy>n =\rX_/ݵXջE:xc"yJWR8}R+mR ]9 ig/ѭ~5XO.ar ޟaB9? w&$yb*1Ȯd?׿׿m Ly?<ȩY}o㣶>Dml{wwwwٻ;wwd#giF9O?\^^n:\Wnӟ.fF dMvw;pdgo;$ij6Yr{Q9&iMK~G;>uBF~nm +n\.ʾ:_ǚJxKm|>YιʜYihcn90܋Sw,:= Xgo)O%$\ FMPWn@FD#oQ8V٧jiLsRޠmu} B>13!~@v>t nsZk+n[n/24r;Un4>~.vTԇcB3Mz`T0̻;\Al׍7XX7X<ʬ7&q0M: .NIiwP;JTpb^ _ e vȌMk혚ZȅT$u,QN, K !(p6yƸiD٩$}p?Xec(kP9X0 B'6>BϏOom>㓃o os'2a[j;$ yDB.|½ujӝBE:F\,(Q^ k1HM|H˿6/jmҫO<^iH3겑i+t&&hd x>6e䦝+w(Si8*쵮j.x>هŊF?`+}j ~9^pa.Y ,_D# ({05W۟ꆄ|:[..J^y6go`N7̎ p($z}Qn䠒Wns:<ۄ'q`ODClVg/t႞î!S<18$e_̗E"efsb:mXFI0;Yr֘݊b?ޚ+/ 3ѧ@8[`Ç0L͵x+LI쭆#&^4oa_G4T'DÆ*9' 2>>5XBv g9Ao7J"s\9vpCӇb;5Q!9k0836>coԕLCH6G5\@> 5G%5!I:M*,$F\kf7?ViPI][.z:6|O e)v?d^YN6x1#` ŦbN $Pth__D?xetam4ܹ{?lwwoo?lnã?>ÏQx w O[ۻ[;w;E|wSCp!~b'>pcҍJ&_zx) x>ƍ T"/^2>…bFzci?(o E>V2kQP*]5 r`bH+alx:/GwB Ok);#xL`:>,c`15zaiuma6|sĘ: ɱ`Edل$4@b`Ф+ak /1ţ(fi_YaC˗~ wtT/[c$A5:[Y%Zv;p/"I-8cs( #ϥOgM%Iߦt~u)iYU:OX5MKG=MѥRz¬.<CڿT=T{k` B G(# ^$.I<_J/sW^/ND@Q|F-X )ACW5G߸`ުmAӎ(UdD_sֳI;sTLA4DMBQ&^|FvcPNQ6Ȋr?p"yDCd(2 4;GDpJ1mO2Ǧ ol1=h0+o<"l-"0^ !%pEwv2<獳J-#Ѹ{m|\.hAs{[ӟ_>ibiͣe6XC'??lMmF(©*W3Gx k^PZ|njEy/`|M]5܋blf!o?>kbN)qy1' ) xf9W>@ʩY(>nn/youpuͪTCF53 $}W>xHͳ{mU{EUmjFA$(]s|0UWA.we"@ҌX: h|qL IyFߏ{/֜b0 vL[03ef*eWKDzKN_"o^1UPlTDbIe*'m@k0+gNG:Xn,]v^w,U<ݠQYЯPzZ&>mGd كqZ+X_VK)ߜR&u$bGlo{\T./D{/QLse T-]Ѯjneū=F+׃/kzzL5[zeu /q#}MFut6΍^tڷI_ۛ&a0Sea60e8.-YmVw` ʧLO s`kTgYIbk @Q7Bm# vZO'qz{e._@-7qɀ'p& `ƒ'j3WWG;88ۥpw$"!WT:"N dBX7FIx9*|+$S;ϝ|n Tr󾦄Lp3tWtBW+75&zuSncr[mtmW7אd7WS k.Ht H)Wx\}E\uQ߉ ^Wx1Ŵf6hy#UuͷrX9:&~#:P32A)։/px͚_UTnugK Iinitrsbwdݐ(7ʈ#= (xw.NqlJ Eqv&*#lċ )J0IOϜhRS|gk"׺p7B0fu/o`*7)&=y'XRv`:vd_Y lL!OMhEayvda 7mPOgqAir ˊvF .񈎗F [On/ )ΝiZw=7o]5M"TL2 E) ž Q}q yuǃ_<><>zarx~*ݦ Jz^UZܪpfUT:Êw!Jpo;*^5U"B^M$ױjn&-хaJh]c.E3s+~~/QQΛcω9Vg4aՇ/ZNPXǴ{d}ޚ*[״ e4Lv R˃|֨!CeBm"IxTIJ)װA>7J6 ~*V՗>Ό?"~ Fpjg>`߷ҫzwu7O#p]f!D>+q!h_.a9Vϸ))P!`*1LJHHMN܄]+'}C"~b'sٷn6 X6-98+m;D<\[s+]}[- u`.kh5lgYATnsbeȩ$J|,Q1'@Wuy9ͨg[{b^ eФvoYS4N84SLU[IX{])C5_;r[#clsݽ;{ې''q^ownmԯ=p)GRA{Q҇6Y`G-S F"f*(kw?y6!oLonóqz._Y=jrqQ͹"!*sj4r̚h2ѐZ^oq6([ <aw ؑDL-:'Q&w$.I_("}WL M\m=gؗꔷ̪Uʊ!P9^ WX#4\m#19,fpGWhx霝>6z qqNiglc8-E<.en>y@3:Ͱm0@ۼB.ؙe|@AJ=9һL)fV100'0J0A.PgDzA4Ϲ a+ y*Gؖ cUY}7tQlAKLSY:< Í W~k_P0Q}x$/vO >hU u[m;cZ2&"ma_d~D AΝ:_pT+JÚl @[櫰)"0 kM*e_2PSSCQOXuۻ{o3v; p.wvv;;R?OGdu*0~^&ª4Η\|DiR0ch(3 b'3e*eUtSJ>$*/[%Z{O6^_r$V7+(#vv]\W$;ɿ!#AdAq[gb}rf#@ͲyM} y>.Jdgڃᐃ6a<ZN&})0ELXS]mhKA2>(|Ӱj^ `#8I)h.|rrf+<V U4@|.|Y5=۶ Pq#Wn)\c^34l87]`/@E;rE\. 81(*"f`iTQU|UNK['oM52M>,H[H 8VP !чx]5:Z sGkwzn/K^r|KKL;LwV.O$}0mQҡv77%d"o=m1_c$̚_oؕkKܭ)qϖWSOğjJl"kN]ow\ww:S坺>nk40nwBuuXXd0x9mc\a$IX>0i73pÃDSB\:mndjMے"HɽՃOO37y A? *'ϊE@dH {u푝 3CޑEQx+7 Jc]򟧣 0A#M].-HХ/HQcTm 4 |NFQ:}wo7"SШdCе饷[9 [}F&ԢgosR`h^NR d]:4BƜEN| xFI}*a+&{o)HCGS]3{gz̄[7npՅ$lN !M HQzIJ9SZ#q 6Pkm^ߟmTPwBQPc)cѝL9i1?<7dWAlȇٖ2P1,P_Y͵ZW_M!pWy:-ǩ.u9"\K̉*{iNƮYNIaC}|Egx+q^K5"_h-BE{>`jذ@< !P38tzJ5fx׶@s۾ 2}NϗË NvsRbT2.Ax)4jgyB#ݙfK3p&_/jȋZjj:-9,l ծejMbZ U蘌#s,Ѕ0R= 9Z"@1xuՓM{PSPITv Ԇ]b`2)/Q6N!PDC3t™u^S2QfttF(Ck4zK[y)l^kC=1=C -Ԛ]Sӑ7sMyS"j+T ҥp̓gώ^&gii@Qgɴ?2cqM:jQ-22%D5M (m R1n8 L/#YP[@jzA =@?/+ 2^aZ,0 \J\5?CyɅFs;$lu 7hpDY$qdci/D׼4/Vd\d"վjE:"Xt#3Ĩ QZgf&5mroP P0mU{5B=(}@nkt|R 2]o͆V7 {myJ y{οg0KrzIiƆ`_(/Ret">VUi[mn^Ci[EQvqa|>Wl(zͺS3Pi|AN3=CJ }C!7|i H_螈!}eULVujH(M(ɿ!KL.a͊jm&R)+ RjSg0*[ؓGQvd/KqMaPa9b"5[fCO~z}N%e)o8"r)rz Dž~ȔD#BVsQ#ؕƐbc#DBDC|"|T:_2oJGYK>9*k O&& e6ϳt^)`Je M"d.@ojwTiel}`x}n$zhptlgʘ Xt灺m)we ܥEBk;^Rvk63P*Ѵ "pA s8L7 Ĺ^?0xgSE<?{=zpq6Gv!_ f`$d Q`Xr f:1w* f#p=EXW?~yƒ>: Iඨ*G>3PSum1`1jVT bpfBm[ȌuJ*V`V$֥&!w/noKNfڀU~Ώi#CY]5lOThnQI/nDi43YJ[Q.qc';,I׆XWk0Nnӫ'zfC3]xw. rv^PQ xĸB@x9lːH*/;pfYW >rL0\lSyО Q%?n-,0% GW`1a6岯֌_2.)(ĺ Gmȼx Q%!a<ub uL@!2X 4꒲5r^)QI(% {^4䊞RP\p5l{k: ?)hɄ 26KCMYZLxa> G7u†$1|Gn,\:BV#1K |bXi<&+!; I\ Zl\A9}h;-mzU߶·:wmuԓh9d'R%9ZO7iH2@4X/GpekTmU/YQ(S0 'tqx -ڝ#O'K"Yָdŏ|l>?M!?>z1d?~>= gY ß됃/}s\t@3~Őʃ%tK.SL=;Lig;B[.'dyun>lBV:VKS5crvkBw *f`Jj3JŇ)tXd̬](=7KvB s3zEI&ݜ숮kYz`fRވylBTLz BܴuSr>IU9>.냎T=%P{pǫ)R&X1x,=3tcp'm+p `FBwBFV6pKS^Cԓgrd=^-I.kX4.i (:IZCuć@XA*܆LUp,\d\\_`ig+[uW)csb72"NԌ۫+bLu=( KT T=) %~^UX}߈{O^|"NTeՕk֫Z}ʠ8~&b*C]9gzU1[ Z2Nn!s"<\Tͷlm}$x\ 5C}0$_3u9(P\a9":֔̚WZኀD%3&bB÷uE6)6+Xc1jlao??yxߞb2;"9 bQ6)nsekGf2vghov>!Xxe7 3A9{#f[gIoNAU ph>Nʀ*3`_#j?b/ VC%GxcGK4?(]}Q_x^Aɾf%,ǚ09v[SQa(:)}Ib8\A2sw?^f` j 枻UbmX'ʑΜ E,T)*UT5i>QUf!QD_quVB2:ŕ@-RM]ʝ4,t쭤}D ^mCD6ԵVaH}+ ݎ%=QA+!xW֑7B]QuG( 7uY(έ: wDtJzgS/P0%CMeCdR T~_m8\jB2E|U$gďuiujq24TZ!eaDaִ5TV1LX9p:sI3aA{ۖLE!1}@OMɶ=OLpkbxciȫ4WÜo%HjR, ;)Dy'H|$ޭ:y|k6񩾻0/O4 WЬ!XtZ7\D55(w񞻴 +jAnΡO5nPhq^ΜȦK)WS >t Ş5L:,nN 9g>M"9%@4eQ| Ԛ"(: EFh<+#j{M F¼xEN:}$Lc?=!pBC?{5'PLv7.;E3ʺSX}8smrA*JEY/5]Um,bz{OJ;9!aFh b}5O1`sﶤ+Bp֥(Jb4;ϧSNLs(rN` =-SDT1OC]niPCWp&|$﫟poy~vvT/:ݼڵgCt]wV(lN}n}SETڛdћMS)ɨt#=oBlOL\G ȎͮI W2Hxyq!p1bH_Va2\q[ݾ2N6H=ij6#q|/KQ$>8d-M9HEVwKD?mQY ^ϟ^6-}]uo!SdU5:|"a)-=\,)aHT}@za&UuTspOeӂ̓|Ojs:[!쩤38gEZN X$އ{6q$5D!z~:*Nr1_PyT9Bs_|Z|tV3}Eq9B{}c "}1p؉x5:F)Td(y_J 1)t% %!TLM!n>T5aɵiaȁ%OWZg}4LB(}GV-dě@|/ڸ /}%!,c9Ԑ\rʍQ^kv^.)yLn߃}u)5z% 7!Η띖|dnD,8+.Y4|J/-,;߿671TJB8q36`I3209ұYalS/*(#q+;ϔ+#)g6 `DU 3513 0[Ej׳ $C؇/VF8 2O p\odahn:G%wb%`5WQ>$Ϻ6Q&Y,1ynF6Dn}9^8}y8h%0@tʧb{`J߂N߹w!AҋƢ(J_uՊ bH^wLxP> Xrx2xɣZ=G5ņhQyuۋvYTTr ɽJ{Y)"|*F>MA- $#%.!)fc7ˣœE!}c(Qd4wj l߸3jeT<_I8/sW:&:>?⫣K:ypjjߵ* }=B.}9aΌSNup!jNܑTAB)m6@Yǒ(̈X{du\:YŴa\SF,XI&HêRSPFmjkGĻa{[:p5lHh}9EKU.O,\ Rvp-0!biGEQv8.(q1K8L X}^HhLTYg1L\H}2TȎ iD mW*uC} BYq`h Rӎb5L{7⎼"Bߛr7;x:ֹ zk$ߛ אqW}Z͇Db&#)uVwZгhZ7w 0y޺6kVd1 -'ZpK o&Xup0Aٖ0Z~}9g}e?΢Dgk0\"ormj1t>ӌoYFRBއçJY`0dA]VP0+ ؁}vMfctFULIxRuD mT@ye4/کO%.b֎߮nbQ[md&-zb WAy_Gj *e0{(+X*W=$HbrCUmR®7UYiSJ+d96!Gg$Nw)n%87Z\?_dǀ5StHMCGki`jZMK)گ$oW$qjNkkɯ#2]GO-Y[L,b{q9e{Ix:#Ib1z E,.Ĕl QV H/'WO&mlu~?!㴱 wof;s(OqKؿlm1g%}_3ن 6c ~"`!V#\O9͌.F)-EUpn;zgmlpg[+VAj*24j=U}^g1dP- wvΟZ<{u|irUs_[|6Vݻ{wƬ?ˏ VζEs;{;GF#<(|<><>f |1k4[9FáTb"I,o48lY <(E/g"IZe/qBDN+;z:ↀ)@Vd6 $U3U`vVT3#X_bf`xOCh&%qqPE[J 6rC &!@䐚őoР{پrѰ@pX4,ߟ_?%tJ1_>G/9|z|TGWs/ false false 127.0.0.1 8080 true threads that post stories false -1 3 3 1236113809000 1236113809000 true stopthread 300 6 true http /drupal6/ GET true false true false false Cache-Control max-age=0 Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http /drupal6/sites/default/files/css/4e73ed892da4b33f96ae41a88df296d8.css GET true false true false false Host 127.0.0.1 Accept-Language en-us,en;q=0.5 Accept text/css,*/*;q=0.1 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Keep-Alive 300 Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 Cache-Control max-age=0 Accept-Encoding gzip,deflate Referer http://127.0.0.1/drupal6/ http /drupal6/sites/default/files/css/217c09558a102a72eafbe111b6ac3ac2.css GET true false true false false Host 127.0.0.1 Accept-Language en-us,en;q=0.5 Accept text/css,*/*;q=0.1 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Keep-Alive 300 Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 Cache-Control max-age=0 Accept-Encoding gzip,deflate Referer http://127.0.0.1/drupal6/ http /drupal6/misc/feed.png GET true false true false false Host 127.0.0.1 Accept-Language en-us,en;q=0.5 Accept image/png,image/*;q=0.8,*/*;q=0.5 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Keep-Alive 300 Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 Cache-Control max-age=0 Accept-Encoding gzip,deflate Referer http://127.0.0.1/drupal6/ http /drupal6/misc/powered-blue-80x15.png GET true false true false false Host 127.0.0.1 Accept-Language en-us,en;q=0.5 Accept image/png,image/*;q=0.8,*/*;q=0.5 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Keep-Alive 300 Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 Cache-Control max-age=0 Accept-Encoding gzip,deflate Referer http://127.0.0.1/drupal6/ http /drupal6/themes/garland/logo.png GET true false true false false Host 127.0.0.1 Accept-Language en-us,en;q=0.5 Accept image/png,image/*;q=0.8,*/*;q=0.5 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Keep-Alive 300 Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 Cache-Control max-age=0 Accept-Encoding gzip,deflate Referer http://127.0.0.1/drupal6/ http /drupal6/themes/garland/images/bg-navigation.png GET true false true false false Host 127.0.0.1 Accept-Language en-us,en;q=0.5 Accept image/png,image/*;q=0.8,*/*;q=0.5 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Keep-Alive 300 Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 Cache-Control max-age=0 Accept-Encoding gzip,deflate Referer http://127.0.0.1/drupal6/sites/default/files/css/4e73ed892da4b33f96ae41a88df296d8.css http /drupal6/themes/garland/images/body.png GET true false true false false Host 127.0.0.1 Accept-Language en-us,en;q=0.5 Accept image/png,image/*;q=0.8,*/*;q=0.5 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Keep-Alive 300 Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 Cache-Control max-age=0 Accept-Encoding gzip,deflate Referer http://127.0.0.1/drupal6/sites/default/files/css/4e73ed892da4b33f96ae41a88df296d8.css http /drupal6/themes/garland/images/menu-leaf.gif GET true false true false false Host 127.0.0.1 Accept-Language en-us,en;q=0.5 Accept image/png,image/*;q=0.8,*/*;q=0.5 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Keep-Alive 300 Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 Cache-Control max-age=0 Accept-Encoding gzip,deflate Referer http://127.0.0.1/drupal6/sites/default/files/css/4e73ed892da4b33f96ae41a88df296d8.css http /drupal6/themes/garland/images/menu-collapsed.gif GET true false true false false Host 127.0.0.1 Accept-Language en-us,en;q=0.5 Accept image/png,image/*;q=0.8,*/*;q=0.5 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Keep-Alive 300 Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 Cache-Control max-age=0 Accept-Encoding gzip,deflate Referer http://127.0.0.1/drupal6/sites/default/files/css/4e73ed892da4b33f96ae41a88df296d8.css http /drupal6/themes/garland/images/bg-content.png GET true false true false false Host 127.0.0.1 Accept-Language en-us,en;q=0.5 Accept image/png,image/*;q=0.8,*/*;q=0.5 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Keep-Alive 300 Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 Cache-Control max-age=0 Accept-Encoding gzip,deflate Referer http://127.0.0.1/drupal6/sites/default/files/css/4e73ed892da4b33f96ae41a88df296d8.css http /drupal6/themes/garland/images/bg-content-right.png GET true false true false false Host 127.0.0.1 Accept-Language en-us,en;q=0.5 Accept image/png,image/*;q=0.8,*/*;q=0.5 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Keep-Alive 300 Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 Cache-Control max-age=0 Accept-Encoding gzip,deflate Referer http://127.0.0.1/drupal6/sites/default/files/css/4e73ed892da4b33f96ae41a88df296d8.css http /drupal6/themes/garland/images/bg-content-left.png GET true false true false false Host 127.0.0.1 Accept-Language en-us,en;q=0.5 Accept image/png,image/*;q=0.8,*/*;q=0.5 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Keep-Alive 300 Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 Cache-Control max-age=0 Accept-Encoding gzip,deflate Referer http://127.0.0.1/drupal6/sites/default/files/css/4e73ed892da4b33f96ae41a88df296d8.css false name test = true false pass testpass = true false op Log+in = true false form_build_id form-e65a20482915592a68be8ccbdb9fba05 = true false form_id user_login_block = true http /drupal6/node?destination=node POST true false true false false Content-Type application/x-www-form-urlencoded Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http utf-8 /drupal6/node GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http /drupal6/node/add GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/node Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http /drupal6/node/add/story GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/node/add Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 true title Fantastic Story = true true taxonomy[tags][1] unbelievable = true false changed = true false teaser_include 1 = true true teaser_js A story, similar in form to a page, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a story entry. By default, a story entry is automatically featured on the site's initial home page, and provides the ability to post comments. = true true body A story, similar in form to a page, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a story entry. By default, a story entry is automatically featured on the site's initial home page, and provides the ability to post comments. = true false form_build_id form-7d25fdb5d0089c638ef2d946e85009ff = true false form_token 5f539d93894b6055bfd79011c0ec4215 = true false form_id story_node_form = true false op Preview = true http utf-8 /drupal6/node/add/story POST true false true false false Content-Type application/x-www-form-urlencoded Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/node/add/story Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 true title Fantastic Story = true true taxonomy[tags][1] unbelievable = true false changed = true false teaser_include 1 = true true teaser_js A story, similar in form to a page, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a story entry. By default, a story entry is automatically featured on the site's initial home page, and provides the ability to post comments. = true true body A story, similar in form to a page, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a story entry. By default, a story entry is automatically featured on the site's initial home page, and provides the ability to post comments. = true false form_build_id form-8a88a95909d46d432e9fe341c3b3328e = true false form_token 5f539d93894b6055bfd79011c0ec4215 = true false form_id story_node_form = true false op Save = true http utf-8 /drupal6/node/add/story POST true false true false false Content-Type application/x-www-form-urlencoded Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/node/add/story Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http /drupal6/node/3 GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/node/add/story Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http /drupal6/logout GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/node/3 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http utf-8 /drupal6/ GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/node/3 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 false saveConfig true true true true true true true false true true false false true false false false false false 0 true false -1 10 10 1236125829000 1236125829000 true stopthread 300 2 true http /drupal6/ GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 false name test = true false pass testpass = true false op Log+in = true false form_build_id form-b2361253017b019934be93aae0d2fc27 = true false form_id user_login_block = true http /drupal6/node?destination=node POST true false true false false Content-Type application/x-www-form-urlencoded Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http utf-8 /drupal6/node GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http /drupal6/node/3 GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/node Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http /drupal6/comment/reply/3 GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/node/3 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 true subject Horrible comment = true true comment This story is sooooo lame, mwa-ha-ha-ha! Quit your job and get a life. Comments rule! = true false form_build_id form-def6cef1c57faaf54c344b6c8a6070dd = true false form_token 2ba1e45bc76391d74b59d94db2c444df = true false form_id comment_form = true false op Preview = true http utf-8 /drupal6/comment/reply/3 POST true false true false false Content-Type application/x-www-form-urlencoded Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/comment/reply/3 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 true subject Horrible comment = true true comment This story is sooooo lame, mwa-ha-ha-ha! Quit your job and get a life. Comments rule! = true false form_build_id form-1c9ec32e3cd967c8fd838ee71c3368da = true false form_token 2ba1e45bc76391d74b59d94db2c444df = true false form_id comment_form = true false op Save = true http utf-8 /drupal6/comment/reply/3 POST true false true false false Content-Type application/x-www-form-urlencoded Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/comment/reply/3 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http utf-8 /drupal6/node/3 GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/comment/reply/3 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http /drupal6/logout GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/node/3 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http utf-8 /drupal6/ GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/node/3 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 false saveConfig true true true true true true true false true true false false true false false false false false 0 true false -1 20 20 1236126574000 1236126574000 true stopthread 300 1 true http /drupal6/ GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 false name test = true false pass testpass = true false op Log+in = true false form_build_id form-08da3427c3411442e541fb4a605cab8a = true false form_id user_login_block = true http /drupal6/node?destination=node POST true false true false false Content-Type application/x-www-form-urlencoded Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http utf-8 /drupal6/node GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http /drupal6/node/1 GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/node Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http utf-8 /drupal6/node GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http /drupal6/node/2 GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/node Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http utf-8 /drupal6/node GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http /drupal6/node/3 GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/node Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http /drupal6/user/3 GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/node Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http utf-8 /drupal6/ GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/user/3 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http /drupal6/logout GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 http utf-8 /drupal6/ GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1/drupal6/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 false saveConfig true true true true true true true false true true false false true false false false false false 0 true false saveConfig true true true true true true true false true true false false true false false false false false 0 true percona-xtradb-cluster-galera/tests/test_drupal/drupal6.sql.gz0000644000000000000000000020203412247075736025060 0ustar rootroot00000000000000.ZIdrupal6.sqlysH7| 3[31j=]16H" @5oe T Km(ԑ?\48>yuǛO7?9ͧϟ_r_ŋ?7_rvl>3[hB 盏7/?|z ğ7 Q|%[?h1y _5v`F_:;Jh{ {I?1!,Im737sFҳV489+Y4NdIB蘊6쇏o|_?_ ϝ?W?jAg:)mۚږ2Aln>r`N}ΓN6~ODa,1Q/%{|돟7?P7o<;)7Xa-sr_ɿL}Og'p f7o'ۃް#ϝE'}r9/4[D/p&K3Z_:($spo=4J H j`Vuo}"R_i5,d5f"Us.n.c } Y oΝ7w<;-D?MUTq o$JxνqޒeLgb;bmH_F!@+sLHo5eA†_޿/3s&VfDrXy.,=EG";ε ര枱?wϭ v48Tz4D ӷ-Z8f%zӤ\Ȼ4ü>q՛L0IheJ??y(W{ FFMDѴ{M M (K8aN7H#EdzOP0ڵq.%\o ֍w>>9m6NOoB4 j'gM޴'ͲOO8fv$$`dYA {zBJ/E,/4F|\K [%u%J+GA65HQ9lBZқ蔰bRdgmM2 ?%UI\ hW,aDa3'.%ۘ,hdJ؟N$y#y%6s6cc4D N x仁O41~ڼQ[bs u;׽N뿿p/YI*F7l0b?pH?q:r4uءCPۑ(wN!L|/mh t8O<>9g y.zΗt;mB(%T(`LOױu{5qA6of *<jDb#֟_8}w S+._O)i$v~e_|=;pbLČ,]"YPVJU*@,mܛWeJ{?nr\ľ Kf.l8b<(Hj@\٥AxAk Audې}j0 (9Q 𰇧N1z.@7;@R E)Mp CQ410{*;$tSނޟ'gޣY7vf.pY]‰a*/X~ΡS헹3|pXOO2bΐiR?߶1ҜhAnwIfDGHwU*G d0I$r'1%lēgHa rBo729D0r#sE―@>ĊP^ݢ? 71L`z.GVTZ(0?Qb.%DMQB@p9☠MH(@Q{6Kў.U^& 'lB0 5Dy_^˟_4Q1ɺG'dVgd6|%a7Y_]RyQZ,V()zkНKֱT3rY!~5Mh/RzIw{zoC?g8B*@x@[u؅-.q Ro'H7&sHPg6mCU6gƬvH1o=)ݓyf!7L{j0wtOGch6a;4:q[ҶX65Ɖ EiIrZfJ)I#hLf*IIr{@@Rfj)zUOfS6-*I5XT֘V7͈φli`i(_Yʨtxy5q*c5եפEWʾo%t00lSV#M8s0?$`GOXϟ߽Dٸj"PyZq//T6PSd@5l Bc b@F%ٖ0)>u|kMHèp.N[EXT)NЅh ሮ3 !e k'J?js&sn11C7f/n]C/Z lorӰ2X1Ƥ]^a~w q7V7JYAd mw ߹wEgK(;U)E( k3;ߑʥY>Kϟ?pاC"aXdcaUa)St:HZGNY97%=k1ƃv :(쯮;4_TȁkId~uȃ3MPq)=b%;'PGӀ$2*ǘ2c]\nc.MGc}?'-iJ0_( ga T=Ȋ]O5^)(Vܔ\jġi' v5i(C-&b98XI‘n2ʕ.78SЊݔ2b>tdpGϮvXPU])x 򁗡?r:ͥM|DLaev;s!D rA)"pG AxuKföݰxtRp#Eμb&#ǧCA, Ec;+/_ޒ# *B/Ee3Vq-ZKvtC\iBcϧ@>~3ĝhKo_N <TwGzNA2 \g&R-+'x|2l/d]͂j%vBY-xE;Rv;[* wTas}vR,Ȃ*Moe`,Ib\nfsO\mh5ܹ(`N4 yL[@0P\qJJJPcA;A]}gHEnH.$#1˱Bœ)i6X}~`U=ZtyYϺL%aܔ&D?ΡXj*_~ xX\lѾK;ڋ'(XT^r;~^5# ɌH{[!^]v#TtU0f$OUn* (*^=jz^OYwg!R Y_6 y)%k 6]tI@^S'i wES*3`p9sֻݑ?.ww^i"S?E|fy.oKUc=lA;߻] 8S& E]&||[eC>@#oB*ZjdG#;.KӋR(?GI9GIZ=; (NԭlmC)0 |̢@xEk<4>*Q\MB1y܌3i4du,;iXs* :%ٝZK{EP)oZ.I_}o*XL;/$οtU,Pz pP ~?7~erj.tFǗ\cGew 8⁑ёayp sRCV_¶9,xiFnwK~Dm? d ]7}ch=%@ o'P7?gΉ+M<{BI$q< cHvv/q7Γl j^M$ !wӠѯrЀ5>,>`q$tjd҃Ч֮\Q % kD,1`%Ú!wFC(@JA֘v`;@,HO DFqm**ѕX"w'XhW, V`V 2ddٽ`jSuP#aS(A=zy{<Ҕ3@R_\/xen I3D>4 >m!XAuD86b@ &liQ5*;c-j)T4[ȒwXOݧ@ *Q}AiݹW*8l˙ڢ*ox HO h ߃ -:,n~ TLSWPA)[KD]pϾY߼%_HocpuN!`E;RCg`) MҩE%̰h\QLsN.)(W@EA&WZ'&鍤xx&'Iڈ]&O Tִ /^Pxb3) fZq5Yv?,)ު傴1xPʛF3Ĵax065 p\m\rs(Zx-v͓P &W&Յ 5W)妫W0]-\rg|pFt0Qg˙b:b904u 9LߦWj fXh &ߵxO}#2,ߨłjd}u-@\vG+EiFax<;@SS}!kvs_b>:[+qۙWJ{F}2z2S$oA*5( d%wEQC9`Ghl w+JU+] 34\I&4 #=iVb󾊖 2?#8c9>1U p(C/'o3 mv0MD?*liw^.j 0 pnYDy6vHLvńunWK){FVcJJ.[D!&hm P9!3C͊"9 :>Dv|jo* _<ʨ5EOc&!\wPG=|72EU1lI9q(છk{AJb"5O@u^@W/6H}]1G> L3Dffc}礍np-Aѿ)(s~r}r;G˳gsX¿h-۔44piA7iRNy.q0V?_Le~%ͻۼa;PMH-%ܯoWW.lM#Z4}1b<;~a4^ipʮ}2Q~r碫?%bzj*,]7% ?nOsC=d ;|O~7Ą)(a|@YoKZMW*s46):%qxcĕ+Bi,& ci)͡% z@2U9Zdy OT_K[S'dYY qx__d55'x? cl@p?o jS-Z%5Ӕ2=jV^@젉]%/E&`F6?| ME%fGW8:dRQEgPɜ0-KtYT,2JhYMuy |sY -z3  Miۢ uNs,;B?O/ E( Ds/$M]銠J VPbzZ"tsd[Q &2nB&ϟ߽nzU%T%㫖@ WFF[Hx,DDYCfG/sdRtjIQ20VKC!=y|ѯ#ڧ2G378uxOa#^ *HTx5N#@oD-nCxpl9M. % k3! EЫ!h㨐3 BхϽ2̖1o NYNx+Clp :RSV DkgP,0\Eb9u V OOh _ԏ~ȩ'{TLC;,$E 3 %׃coQVs E5EF!? N+ [;B%M xk%B9x󘊠׎}i@_4.YR>ۊ-R/]k8[Ɖ32B7= &[=ϋ V6Id%?k>O^`i.$M|CU†`9#ע霟;+)9Ѹ؈mI*Ҳ8cE$Ηu.% ڟnwʒ{.#`=w,'d+}}Ym|uu  Ȇٌ݆bʜ;6aFn9j$l_HPGj)#e#?D=^0YVc.EmV"ۧH"Vd hR[81ǑLuL7XEĥi=IMƼC LAǵs#&FÐhq_0sXoJ"s,QlǮdi6:F?~Ǣ9yyhIL0p6KueK]Sl+|%'J#Z9g!諱4ŀ]Af@/cFg^ jX8V} ?O?}zSLwO$̐CFQz4:Pjm|,ۢ3I( ʐ<>xIon6Rt,aaQZUl, M[rVwSsW=U ȔcTذh[,B ˜fQ)~ZJB{c\xʅ?|iasij4H6 9OW&!_b2"%KDYP0-ҙq۩H2EG~^K{ ;/]H-?&2s9[L݂Ԃ,L17FaR|luTu7D RygLVC@Xaa)na ŞAY.CIy_D1.$1\mg܏7&O\pZhtu$ÂPE4*$R%'p:k> .gD;㐝ޑ5 o%.Qa2^wSLۀZX? PPIH>̂?YCOBDY"7Q1d+S%Vl])XM 6^(aꩪA|އu`_ ozkuJ-O 'qQ?y*f*-TuԆKoxs)&MB 8|ijbB8K'eFRGܢ\ q%5e{ͦk-:1t6fQ0 R]}Dta3Q_pa HV2BHXD嚕-ɢ\gIÝ]t|`X:LPEQ"2Y琀J}lmHE @X, >è[Gg{54 d*N[+Va@[ܬC r#.*XZU!, riaFL-eE)YΤȍ= Ҕ*kT4q"ݎX(C|D:`C(j ݥџܩv=6"]8rK>|?7cdDJraZ56 7fc7hLe 6VdRQ/慵E_Km1>kw(5b\=|JNRZ:Ȱʼn| ;lUeˌe*pKL\)@4$=G4jSA^i:tiCT~HIIA:6}yY߰ϣ>w<䏿"D4a%EyXWIJ.̺2^Zlܬ10xP*67 1>V]/wg K mS1wc; VMW[Ex8K|AW;b]DH[CF9-ҙ˩ Ъ3Luqs7wm-+5r-H\ wzfg FIMZ_h V^MbynU`KU<[q뗾+ٲ =pS=gD-\#I}6/ Y|WN:դ g8fzYߴQ(}`G BuWY3HP ;&$*?~Dd7GELa%k gEGo;َ ^* rgS/K{BJDO^iܐ\Z锿nGFY8%Pa.݅sEK?FNRxi2W8˘^Sv&0<Wy{@5A_MAvқiFH |ֽnNӝ y*Xr )[6:mC `.uB:)F#qooҤ1jМycl6cFM˰1K|acl:VmWw-a,W̱跮bwޫԤբ53ڝ ͦUO@^_^P92+?{!ԹVNydhgn ge]tRj?-vmኮwnHPȿnbDQQ\;#9uȂCOŦ *iE6˃]^8x@X1vVRF[:CQdlTV.Xr^|p_l0vQ]GlTE|g}c] vS^~v) [2RU)Н{l6 sm{+(}m*pWb6ͻٓa^t= g5@g`Iҭf#㙰a Vثct]+6=, j*,7BtA$ĉaPV0ѻTD: NEMțm ?[U` 1ɎTab'dL2ug*LNK+c&EStOYfM^z`LCaig)97 B6oL? n؋% '1an O6"7o _q7C,t!i޲I[R ύՌF="Ѩ6O6?ZSVh-myWDQPVZu'+zhZ`y.LJD;K1lMǬTZ8𫜟tgl2'f}7:i0n֍z6>9=!h!/S vpUbPcflc4=ZX-g:/ |Šo[H6}6CF'ֺ,1Gߙ. vwgpo^.G1%R)ǖ WQ4Ȗ`TJZn))7sԀގi^[J%r#svVk1JUXքB1PyS~TDq~7`6$uPKF5=-8cb"r['!u*ȻZՃq[6qjMQr7晹Uרp ,- .;{dAȞ!%ixO=, o/WŬl$Z^߬f ĞŝA˟y>h4bG 91!#8qy;PMz>l֍fw&)(7#jwu/׫BXxɦvvsӝ|I&,ƥBZtQ h@ eNIe֟F 8zq>3 P _$xw P Gr6`ΪնvNsEJ͝2+f=1ρ́B/?:nLMvnCѩEX_#N-Bz77Lc|X;0B_";Z2@Nvx\qA<K[ۻ"Ai1愿Q)&/Ll! MUa]~7;N|l[R=In NG6;(7dO[ҋXBF'mQ%4.9^hKu^hՋq3\ȟ( 6n)̙}} QX-zI(}x޸T4\A8r_hvv6أ y8͢>uo[nǤqM)f+XCx<T46_ޮ5 Fm# /:{ wzkBBqpƌ?e{\~y^sϮ%7#z'*78nDMxC0wb_NDKbtwG{瑼"9wQv0&03]OECLŜRN0e £4;RׄwCnn[F'"7෕f("D$Z1KM\nwwX=)gVPħ؞4}0PA2( n"n_Ÿ&sM>8, Mx08_#l^tҘRDt8ͣp@a5va3ScCNqoph̔}u@j,v7V-YYEJ3x{F+ifUO*os;SөcG'zϓB_"6*6sԻj(ۮTJBGSo=rKҿ]WC!Wk][#X/C}хZ)1/j)Z)PjL;ݮ/ծ>LثqlZ.lZ:i¶5‘;\nAVRm:sRtx`dVxs7<ğV+mBCycHR֘Jэw7N7eoᮩ֑T#D/ydY8qoJ{<a~l*kb CL ;KebP;L6[*M!w RL9 y[&vU:&R.-(_gҫAZ}>nv)jwLiUYb9[c_{&d/Lww@XYː,HFvv#ĮbCo۫5dS)1W:d%uDke\w_qvKfo-kd -kzxʊXj@pS8on*黦c-0K`cپF Xlp[m:VgD?^0ȟi{#?RqA؛ ʢb}Ӏ͆\ri,`ِNj_[DGw=hq5M^i]]Fzx2zn2r۝QoyImѰv)4%d5NH\2&`UEzCp) "gQJ)[>g;7EX!YeG w>_HBjDQ{-?W?mxҁ?`^ë/(Ӈ*=n«&|l\ط6 K|kM*:Bٸ00)IHٿ1YD^%[pbMCKeSJ P܆0D&pI]O9^ZD,ӓ &;o~ː *bR;%`ů|rC9m{C9Y, jv`+zs94 6cv(1 //7?}y?o>}Jؽdz'_GS7rG d%|L&x׬e=e*yv;O7_~v$@NoC|D$ԯ?O5OQҎK_O}7 3@PG^Ňo|_?_ Ogx~7_|Q盏d̡sz皂'5xM^y4~Oγu}6'IsդtUk4ˍj؎8I`֩ǎ?^8HjwBkInE! ̂ӹΝO yr = ]B%RSlVF'|q{M{'^f.(6D'XHnލELACmvبdJL  FrӒ|U`PQ݂&5: _#D#E  <-2=27s/#*( w4PloB_s+/ /}(j3%!h VICn&4&:7ZE O4Fi\\/?_"8x N;۫zЁ)$dRTw=i$/{3N"9?G]@?_9Oljs lph\AT7bEl6 J&e (,J^iv[㕍LHNH[lP_~p).q 6waq\ VΖ~pXfޫݜݧݓQ7<ꆏ\DKA{܀n9A `sŀܚ^~\$^h"=g~J( *7|aRl4kn/IkF t+-Q)I+#m؀ٯZYMiW׭5c:pjh*V1ڂ5+6O +Vt"a^*>Pf4}o+J$ ;ץ- G>0AE2}AȊ}w^.tBGp,ۂ؀%Ģy;ϫYPрϙ{F D3L(@o[Gf&2f&"*1 1/A=Ȇƒf=֩,d)qs8@Le2--cpB7> y)IV',\%"K,o]s-i;M+ao=#;/7p0 dF~ FrSY|u] :<ք(4O<ƅKY/c+!.ʦ.HmjHy%JR1iDDOvd^h;; pr `0)vBe&BTi%rI6 }I_X!Ҫ:t,|Sɽk^n]z]kIUL›WҩGG7x*6o #9_oQh 2sd:x}Gj'4'H`Ӕl!hK0Zb#y%RiUBegri!gժ2&+JbU"ौXj^alQ9MtZh9ʋ" z&{{ZiQŋp>-3&S8e!OPi 30 u?3y8dw61O!r !]7 2Ɉ+pd<`\[-;w[_SU9ggY Yβg ˖A>}GjlT&+}vY-0v'E'fA h%/ M~ԩN@1ҝfۼޗoIg{@4rؗ3P/%+Iz^v4ie>kx-죏>9 a2~u 5CfF>8ݐVWGG [ݗJ )uW0).Bꨟ}T?l_e杚%=Wj}<:觸.#;ZLnDT Cq%K8Gڈ"oV.b rH}ȸiu^GQ94~9g?(3t3{|3{ُS !|r7"a#);tɚZ Pa7#3;= 9f]"R |[i ,]"06M`'Of9 f/ ^`v>z#Xp_󗴙ZzBwј}8Ȥ NpBTt%qڹs#~k~f.>QF`N=?{¨tT I/~VH(ң~H-K:˶ǔ?[ܓA%i)GmxM߽1\;tsz4 n LCS dpq><\*e П 5|ẓ̤vSo^G>f>A*˿78'˂_SO (Pd"br1!=!:l{< ܑ~Ҽm۩-2L2 =0'n[4:o1%s )p. GAՐ2a'9w^y5?u~yoق>uЦJ՛̼ߌ CVW%AL4tSfՑs /DڷH l074UTqy/Z`h%{M"[ f S2S9CՋ•^F 8 ,tTy4s@~Y4SÖdcz5=^MRe|DTh .l^G>nDdGyz5Rt\X9 @aC 5#D2 XW!d%}"#."D2ѭNpJLElRa,6]ՇMǎM46>Ql^r>m]x {IrfXTZB leMecըZ5VMj6*lӰM\}6,$;Fr_ RsM)kBz1v4)Aq\sTxܠ} " WUeDVL pQo6pgmp} p;8jWɓD!eWPs2:u$1.Θ d4T=S]SzMhJuN+k!ٸ@PEwӈ{ zy zHbh!embB{#0~80 ^K/+biS#nCRcIӶL4bjhZ[_>|  Yhy%c&ǖCO>#B Bf:C嬇UM:uwy# L꧁QH6>Árʴ(5p\x>6%OԜ:||hٶ `,@?>\ZɮiJ4ѩ(ꪤ2gG :lVΪdryp*spiupvRQLgV(PR~%J) z`Ilٖm+*@RUFyd ؼ,|$;(|bɚWG5}Ӫ}v ' QPȘ0 Va xPDT X ,|%butSQėWyt=m~.Lf)]fetWH l~Bp{/_LC$)g?zH6#ut{=?5ugevuwsT(P Wzڊc DBb[+zϑs&atN_(z(_06X8Cg)2&ķMh;L`G3w-fO`8a|x&(Aԋ0Mv0p F:JoyPfJI^C*ouyL탹7miBXRfCP-%~9 !LM_ϖǂO!?5"By׵ 7[Hޟi5wOmew*xsq<W.= ?")Q혝en.%[0ġqƒ䬇B }Kl_33AqM[]@L3g$bLRstөŨi/e5ӀY=Lxah"p0:,'?>9C?Nh VNe~uYC?=}EU} w99PUObQ6.`F~zK(q{ ?cz?-r\vDtk 4GȔ;uXnSuDO)Y٩jVB'2d7ڴX8&`kWٕ:O׮Tʠ-2dn<d=6V:@ !Ρz`_:d %+}\OTi&ߏʜAb Ocm3<4ņǝsKmA-%1dI1RrcQC(l,h7MDoY-h@C Gӑ`+]Ĵ\TDQ,`%5#JY,0AP/a lR&TZ! rqnw@0PCmH#Kd=y:Z%CWi|KP&]Ƃ3*8j™CJRrzt[{I?~cIA믴Cަz{woл퐳rHBxH"/nHcBhz*"b{tJ %xQRBi @ъCRBVGQ; Hӳt_ +`+4wm)ɩA"{|-xEœa`a-8W29d¯ %`n*~+8DLy8{7Qrx `Wj$-\Is˧lJsoFuVBsК0AVF{ ]b,m(ғy0>:)Т'j.XUKe#f4d'㗁 !ޞ!`~|oRW >q~f :?7r9$6I2DPVȏCRh=K Vd> &ЇEQ\XD@!#ŹT}2W`)d];0G7ЬFqbsxBUq*QwiWgI.g y>Si!b fWrxIZb)WiZdYpAqXjCSo~  {n+0U y0U gg%'XGE{?fԂt[( Iʯ(#Ԣݡ b4*{9?}v\'睑חm5Z=ڏ:d ONOZ]~d}ѱ. n2XQ4:"[*b@7#>yr $\d_3: Fl6HLYѲ#2wIm»P>lz֙frʝGM]M|/iULXOLenY iw8t;RghX5VM\>xԲڔOiB)l]ꣴQ̝7%GB2>82Qfl:_܀1Y.MISnJY;q"M_\1KymDMb4%{ݠ^M OViˉn^C8M2#4sζ  GK^ ̙(r-¨8Gu7GыSHw߻p&Q8s` oa2"2VS,iKsJuN+k!ٸ@PEwӈ$?xD5zN1ʍNˈ~qSTVS&*w;K(7/g3~ڞ.\xL!EfBޏ˹rHe0C4jI:d5"b<4$;fU ;z{e0z VSgՋSX5R ]ɺBpLc{7H07dGoF~oscxd8u4z:zײ]}ZYe q_ؼZ,s+%.k[bw;t۬JkvHv7kܫCQB+Iokcaem"a|zspO/h7H* lruzkQ&'Ĥ;4铥4,5?i /[y:aWy(`6mZhp9ToO_u*3 ;#;dAoOM[n2۫~D<|\l@ZO]qW |B^y !1v1a@ 3/k77I9S!>~n :#vZw&g.tn9 CEz9'SoHr]5DE>moF(:nn*@ܑSoᤪy(Cyg°n`ft~n Q(綾7* %oR VNaزzV #8sVvo~栿QHWB2]CS {|ZPEQ2 )8lQ`E` 6Kw+j==ͳօ-#Z0@8 ?XZA{;c$i`aÂtƐ4%,C* 1jO֦uv@x@=<RwfUz1o[G@"I*1=oI2ʤXpFG$=Xg䝚'3r]rzt[O^)8s!Eq|,^`J'?"'jMY QB"3XMHz H# b*n- ~RR z_|Xw ӾM Z)S l o,ny2cpD'ZW?9HT @}sbnl[a Bڄ06M)CȺ@9WOsK 64ӈJPY5fK̋6}Ʉ / -e q-IEQ&)ϡ 6_ޔF.ޓ5gDl6®Y1CJ FVUwgzXu0AV!qRZ Jycz5kJ I@#>6qR)=KOv4t]O|"~ΌuHS[%P2td.j>wqgw6:ʮ4DI LvI9A} X!UU;7'EvF)o2aғ#vR'V(OAYN+U[XV֗kRᬿ0ދC.5YK~!;U-j]I $g^f/mЬD=t8QQ‡qFy:گf3?*s61Qiu@X|ToוU.yw~L>]0 a)]ٹv-.^Bׂ~1s'j*0qZ_$ |g@Bz!4=OBW=:?() Դ_mqhEˡ )! EK|@Jܯ\[x滶 =nHI0br2_Wuy{Pwu0K?C"J&<=䛌u(U9L5Q.ƤSd |HҦf7)iu8?x@?\?N؟ u! LR.!=B~ )z}zL1A ~yy~ϡ*?*<8;br4u\.a_.ވ&P!d9w~|pXƓS7GD^^!S;LB,_y"w!9äW炮S/Xd-R%4ŪD(}֢lp`B)x| }QŅH4\82RKݧ,O|&H֕i~d~j!tUED ɾu\'J.FL%"]'R޾j\]kޓe{d[ayA/!"Y=7^9YF*f=o[+`E=&/"z hBtL^n–c}ǜ'嘼cr r|cДzXu0AVP]ˏ˷M:ow1y1ycĮ'k>&/ sL^9T<&/C;&/?lרccG+嘼|uL^~X1y1y1y1yccrdcci?a1ydEc'vL^W5>Y1y1y1yS'a옼 1y1y1y1yؓUŎˏˏˏ˕u阼&/uoFSsL^)L%/_D̍/g#|M3aĄ+ NN_ɬqovxdk}9z<ꍒ]VbE5u:4i:-֦TjL+|* }8Ww5qIAw Ne8ک|[֩F~+5ټlZ"Կ -S:{ܔ8JQ|p@a-J|9axG`'3X~qC|wV 64/g0L *,5][jz!zU=}mS9s5G) 7ۦeuPxꈏZs_~O3KJ6!YT ݖd3>=9IvQAMFSEP 'qyf|kh9礷u;:CGeF$Fm3>t ч\Y2tӘndK ՍҖ],ZJuD arbڼE(1ʙ ;\AJfI4`.e|V)e'*ؠQ*Ko!Z<1J@5BJT}uk'^jIIkZ1W!\3E }x̲bAP*cjߎ>6-OAFoHBp-ծV5_]dy88voe){w)xuy-(P A*WJR)Wr["RtX KJfIRi!Φ:&EOz(#z58 뗰Qa~VePd[|q!*+@zZRv$fXlԨl#LUD/2+w+Y([OlK8avGv?6U4 PVt?7)r15SS&;_w1٣ҒvNO: MʞZ󷞲ܞI1e̔[?;iIi0;% )|)M3᱉Lg{1X调u"xb'"bQwUex97r$w2GY$Lz^:+aV 8;㥊KlFܳN)8#{ aT(u F8ʹJ5Jh Y> [)'CHQI*wMgeǢǷ5S eQTcY;'I1z/te$"ױͤA&|gMiz`|CՃ#gn1ޮǀrbvP{YɿvJ'K }Bŵ):tYK[y"CBC<31u'uK ^.p'V?(pݍp)Ν(\uGmݡv"v\ "r4u\,O>nX5g[ZW)QXQXwbcTp7%z"UMz}TmvपscUx7%]´S{rr<ؚyʨm[P+QBXBJB%< ݒ2_dJg壄9ڕJy *FӱHUWj&RRݔ^-*.+Q^Q^S4?=VVXþªQ;ҮVXMǻjnpε((jNDQh7$-ٞ$8 ~zqc Ÿ#14hiqv6= 艑㵞E&јik}zУ쵞mn rMA/lWyO1 %: uN1FCݟ2 Jy]G: 5 ;WV󆴞~D2L]#v g?=+"hQZ#K8,_ =de e$?6 ^  +@-NJ6m%˽Oo^"H##ю P%W6&wWNC8dJ4 Kjh(<fi4zߘ*i6={ui [kdD$iq.S}~-k:)e^m+*}/NsaIu[H;tLC_vOyxsK8Qʚ7[Ƿk6kucDT³y/茆ʍ ?_CaG-l _܀ld`ñh_tB}*оd}P.ˎėʗ7^ PY,'l@π o^A8 #L})z&(VK.Ѓ(xD"Kl^<RfhD~yF, [6 sD/aCr#4u|C2A?H_Xf;yʕހ5xDM# Ԝ溭aK 8\t)5M!<:2_,8ISżX4PDx&>PmT0*?{$m|qHǎ^UP. g(6 \%%R%C_R hSrqxS0ta`S=M{~#kGjkZ9uA!HbX<;W-1?u0u?=! P2otӮEnwV!!Fur<*cA+kɞEK`=S-Tsa?!k~a(cz[K k,,rh=J®06ν iVӅb>>hmBi&)l"y8A6/B Oΐ{)?f?A-!o(vU4G6:/ҮH3aMmnjF)h; Q?.`Exnx.1A/(*e%^2#d`w 4aZp6eC^jJ࣏P/%cVnsnvhRV;(JBk(E% Ε빉w[Tl0e-!Ŭ1(Q5|̱v+fY*ŸB啦¯wO]'zU>Sz vEeQ=QiEC >< fxY[Is~}=1,nxžy!+"3ws8Iy]^fLlԝ1?[\cŢb-9AxLީDT1e`F@qӿNQAv9,w>kD$zop29 L9J@U⡆8@a'Cmҕ#l ft.9dLz|VR%>k>粒x9r6#=.Q_&9gMfg)'AHp<@)X&s`&{ȇlCaŎ]GѰFW ^ՅYg^]ekDb,$1ZK2 꽚BFK  y$㜲)v>X&5 Acy:gco{c+Bsp0bNX;p tAPnγN? IId5%kUhF-tyq@;<PҼ'*,2[u[ȆYݨx bg09yB\ @$_8ѓa0dG $8u5ف&J nc8\qÓr>_ O72^<?3.w+lQnϏdbjs܅?+$[:ʭl -qS\SMBH6t$%ee/xL%6wCrC;Jڒ<ؚp%6MI/rb-pwOq_HO9ܸn_S3?p#ǟcQ8u 9`|( B_2   iTiD.vĉOc"ҥax܍u!F s~|S7P'"C`ţe0<4OK c~MG3$.dyʭrfɏv~R.#Orvv`&^_Lk}SWS:jk(oV0Cxh`mpƛ7♂y |H b<2E4^淨E2x`;!H|L$;TsS3"Y|R} &c& LИ.ؽLBcH{= )CGL3v҈ ۃKKKi/ծv/-gMICWl6_'*WjG)7h'FɰlE[tOM-e'vl]d8Of/zVVY](7'ݵ/@[[ltJx6S f3*?Wd9)ec=/ϊ+{:@ͯ#]6@npq2χEJ.W}k`GQW~VX5<&Y/E t̋qqȽ|KIe3!#bBD;j8^ގ>PT-q]C1冀6gyt#w2ygEK$oV}%=/3Nfa#u/%hpaqW"ұP_ӁDFu&*/YhKmxg?U>O\AnVy hfh=X42]i=?rS=9Cu:}{aKGt@M)@jPP~?r`AMP߷i4G'Eri -R.+a@Q(ߵzרG^^sIHxbEt iWjdh&讎9PX٥&"'1JuAyZumaA/A-cG`ҍHؑlhh^#ʄ2d(}TKxYkj4SM, ȪȢ?QJ;:(ܢQΚEr*QT`-@L{GR*~0/%A8 7>?/A6 EAHQ/*5{UcCְK+Nba>wY1Ӕv ܣEhR*°Z4.]簺5H} /ؠqȦdck"{ܗX*_d  S7't7#ښC.RJr#z}|k0J/&V 무:P\YQf¬E.{!1ž-XX FϖQP(%v||(bko$5dqGSn&Xc>CSlpdWxk?k۝ .FS%{ O&f$\]=frǏΖTy6bɘ95\9|3*)EkN(ޛ s^Vl$]=#oX.@i+|6Ʋ 㗢s:! ~ dsUݎyzC1@6)vS(bR%0Ƀ~Y6~zΝ/_qp`nHOd456Ȱ`'VL4 ]ۣүqOb1[]?!w.2SvQ9$ѣtޞlBlΆ郼:gy*VJK %#k>AC1! ZԸT0hhK Ҏu"fo<_ 6B{~%bOOKV~9q9wXIet 0IL漝P2<*?m+ZQȷ ރM'(څDx;+)Ikk]Nvݸop!A{DȆP鋲mfYz{G*gxWӺ;T 5%f@l1o5/9{ hNl@d:f@`grqfxJ2/dC yDfG׊\^l9 X"9OTY(C̉84&|VqSv <=.`*eF}@ 8+,I@a0 NeCf$޺EȀU 97J}Pk?# YJ4 (֯c crcdKiP.ASAEqmC olώ !+zSz.;0B*(q#YoXcQAy]NǞX,WgmHk6y@)> TavdΦ:Ty=UY"^*p#2q%Yg½wF5- WEzK@_H>%sa1ػp+SzVS\cEaW t9; ep%AX心i,6r0q .npɆҤXI6wg^񛦷u/PS|"?GCe$X!\y4Y082b/~sjsk MQ֋IqcF4 NN!8ᄴw 1P~&Gu}w~ұ7/%Jx[C]lvfѡtC\x_?j'cJݦ/Ҋi^ߕ+MfKk*{cemvn6t;7|7! ;bύq7c9ˍ<$)ݱmtnAgO*u4uc:]Ki$x(S(Z1u~'@U"[؉7M]vw>bGG~7rc/Wޒ?.iΝW.JNfur\;7puJdY<猃&^I-3o/g1s7eN97)uD3uw]p*HiALN\U8!,C_x22wjV#fՒJb3bQ0Xz37^Z,Uk|1@m$H.m/ȟ9?FQ늣Q쑎՗SIaS@>%G P+bZ.(^4lĩo)3#^W0^P<%yش2Vl¥}il83XsQVX(Snk>W$_VNe$=;ϰQ>ԉ<4ƒSԙK"펓)3>cauoPhk=dJ;Ǥy׃rQKpz5Xn5  ^Xƕh-?㩰  nb oG*F}UʰUE}Y|bTϠ OT5/GT ja Jh2IsZM߹2ZzBW3/B'E-'ҌeTG[\Nsb [xUln-؄*5PklBr&r(q )֕~XIcACkMRtv_exSWb")pxaC@ryy"R{KO^K# z`WJ62kf|aj(EdtCs+Mu[ak]w:Y}~ǵym>FV%.VWFHZmJFYbCYo5TJ@CXV cMDŽ9BʄEZFaH5#ەun[2i\Qy?*F!}LwU.‰0ՁFiq^vRl7\ʴ,6NeL8#;}y9+~T7i@3Z /`mv :"2L-p(4LSGrRAʎO[E]A6uO E!aHEA,+ a{H9hZaB9ޘtEH۵yؖ3Ӈ":TXTږ֐`G>27s]#MY)4R=P1uŚ]Hʔ[ܩ|f͆9MERrTΝi J\>`U8nP2I3. ((ut}`CG8XW-('kB'G͗t7|b^GlؠU)? &07ʏūmSNX9X,pGrxsѹbިc)"HH p+q|O,~g`߾r8ޠO# "OF FsRwrar2=[c}W- ashJG6V[>*}3`$p㋻p\,p]L+[뉱Dn5YR(1Gf7M11;0|b{ mOӓU, >xjExrB`F?܃iϯ?]޽v&a%MnO\%ˁ"[PC6pQDN?RH/R8Hzy {m>Z놘H:6}6W*uEٌn#,mc^*m_9nPQ^ΔG 0%ǾxXDf_mv^[UZ3p]әG!skOQ?@rkX+EnlO%>F k>ONVotӓx߾v7>r~)^vwx0'cp#H=ސOɤ/?56g߼_9<_yoo޿<|7 qLC4=>\~돟7?o~yyvB Xn"o|rzŽ2{bU9wWRn,R953~LCQĐ{v˃cp9tWiA ]hH]TpD?cQzާZr:!7{iSCy[ `D 2n}E|y0%XyCN.+e꩗Ui0ՓR{eR+"?}FiM:.XoiX*VJ#57S^+TBT^ؠNyOY$@ $G"o-5~$I6l *ȗmaW>".0^YO}.Gǃ0oñݡ.^k7筈D*==8͂GtK,2-:6c6v+t4CP 7 *xsᄵuf-h6]LkjRph-Z-Ol̀G4\VBm2>Ųʓ$((6HiչA.sοuDx<" 1Q]+4MϏŔHw?YI'y@e$_sD{,X\I=-Nsj!w09jI>' ظSމpxsg&N!ᶓ;p/OgX91FC1mT1vHWqlFHgcɶRIy p& E"Z2 &C0F /?q,OVryĻ%=#$G!,(qQ:$}zF fX:8kf!LԝCo#vT\aT6 , >I/;TaJ-TAN=JϩFxKZ I;^DPdKeB eO&Pk{ R e{hVERvҝJJ*T_Ufs^L(wF!g5T~SEOuIWN;q&i:.?WJJ=~ 7fg3[ XOn2TΠ$&:V݂D{#Lگ6O^ғch߁L֡ 6:mt('_?}r&7.1nq3p.qJRw4 +nşN?JT~Y\y-[l p(755{cbA cҟ&^PG*91:DqGYp$k&h_Ob]I@Ϛ|4SK;rFv-;MvȮİJR68N2?vVfKTVp]'?8#ovK`W+9\KNR?~fl(yŹ(`z% #ꋕQ$]p;.g3v\lbuÈpa%5I/]I'"緕GK16Bot|d 2BAptO pP{K`uU~GHd،B{g 2 3]  M jP O8#ʋY*@V3&]0Žq$oW??q`];/)KK S0.ൺ'gZ}>/B;`9eP.J쏽!+lZZ%.=Ӵϝ-ڻ .Xɜlq q}phܳmEB|er.AgpWdKoYb TA3cj!<$P)DSzp]qft)Re%s8|" U: LVe+Ą9ߩs!};\P.8L{A"_վ4]ʒOxK{[^+R Pn{TL4Q .că1i1iǥy$by)zxQ=um+~3Eͮ\@@HLq\RnC)s,yat*qB#bs.WY)NH<}'WS~/w<>1ƪ䎰cT^3>h5h4f1HFS7ye1H>1Q g?)<FfڈE(1X3^|ЋU[aU{A)@n0 8qpx,|˲ v꘷x/F@Xj/(ʼn 3*Eͺn`(ZO>_ O:%N:݌:̚٣*jnS_ [vZ(g [ %0)QEGt iXճ$NEaSox!wן: 4py)/PC;F5~DBˤ/Qՠy8d>½R70χc7C:zcQXjXqDpm+,>(A'CV;O,?7;[L.O bĈtF$0x%;n#E?GW_Q1K*vN*wp!NXs`G{ d?DM!l`f́zj\Ѕީ< w @뢋[:W9,.Ԝr;2q91>J\bt;ٖr lfwRh^{2-XLf5(lί'bFlѽJ7 ;p @H7(l {M0Yl*DT{^,~|1z 'ԎLIVnRaqUTD.41(6):5:k{+Yfa]C'^'kY 3q?F^2/`3>E>ҿhNF /MGoށ-~Dpٵj"Ţeq9wxѲWޯKէz[#`~y7}Lʘ)E9@5mL<\2^J5X0x*neQ?-Ew޹0<^?իzXp:h:Ƥ=`)?3iKtK$ d$G4%0*s_߲f9g^#1ŸUT4مOY!b;So4먹o=q8w-݃SBs؛Q9#BHJ-f}C^/s{iuz^*}]ܾ!ZtOVҽB=e҂lQ;tj{\5z}SthR}fQm z ~S[++2aٞ(?EƛrU,Μa&ʟ~FF~8_œ _7<8CϛG])8*_uLn 5m/>t\N\t aUdOl}oaz|ӯ'̋`^d?[γN?/&}?Kl&g}5>:.@H2|{r:(8Yʜ=4\\eW2~Z^e XZ;;<8Dodj%n9Co􌋧.D&R':c#=9?>9\'H\7 b>Eӕ6SЏeQb|z҆;\rF;GqTKzsMFjbւR3Wvͱ8Fʺ=7wPկ }tK\^pu#M N1gpu;@rkٍ7 㔀Ќ F9`ZVZ4q$'= #ݮb%$<;i{oHcw}NYDQ&C24Ӏ`QH^`c.܎yEd` fRQ'oEݏo2fUea S).4yE8o~$qC'AAfv|s,x-@j l1j86(PWETS5"m-*U4 ~V&@KU^ld \%:F z!'-6JdU/U1U6V7͉&2$"ώ\xXX*G }EdAXj2uFٗKV:3\mPd\\gJg-e b"Lm#FWyxDz2b4b'4c\w"c[ӑ#Xc}PA (3x Ձ EH F'|*čZ+ r R0-^i;=l$Ըk U+."°pb3Ɓbc:w̄3=&g՗""TѡH4dJTzHQ( `mu;Ev͠C5g>J_?̍b&f/9N/tC2$<'5Y*EiF`%y}~@{vB J"!jSg,}8 y3ޠ%UV-dJ(o7J=(GN.&,^[W]Q$ j+rWilE4^9ZLB..Ǣ;TL2'25{o9FVvvr޲Z'ʭe%S$20~۲ V-F^V<{A௱naXV>+-RmxtgcJ]zǸt+;=m߹nӴLD9-B{exk<V5C*rˮrvg;R$â#Z2SK\j^6x:\&Zo>%<Yꉯ:}Kg/S? ٯ0uF07ϋ7m=bO{wBAHڪy_‹$zhZA]qXX"(^1Wt gxixcx*S~tøSSj#Y#ӊ5w}ސ?s,mY\&^1^Qͪ7W!s+ *[%dcQ>W1Z,&"mbWeXѪa5 iV2R6x&yGu4!+V(k2mHuU:`i򲘁 Rx"薩'$aW((1=i-+yl-@AiVIpV\7bXnd7 ڭ8p` / QV7m~f0|_Z#}t20JA k=5iB-M>U*-Dlm%@tbU6Q+m+g SNS&D7I}'(N{gSjƸUsG '}HFd(oǫ{yL{<=o&oY22@v6T_qqqqKuWRaJ db Ե:888wv *ݠ.%b\v3^tpn@:pN9֏קW:Amc2111 b\bS2KL&AE`{xo]_D(– k cxcxq;GxGO(UPb{8Kw1ldÇ^K3O?>8aLcJܸDAFzz K 3*){%jջl湔`3s}^)x/sյrKlŴhNYxuFB v42ɶU2^i%.ZÅy?2{Dže*F  W!ܻ6a%z,8911W|\uY԰ȡJ}%^P1 gnLLZ*j>:2z)C^;CoJ0:uh q%rȞ6K?ȃ{1tU 8͝pb'=3H\Aȫ7GsL(wyX,mD [zhI]< WaF[ indh]'7/Pl Qwb8 V'%DH-BCO0D3V 䤎  0+w. eAbGD\\t(M!ס(IJ'Џ$r$.dyT0]^@7@^*ԔzN~a8~HO\(ڡV}-iL3OYƟ8V7兴;0!WPY^\JK'^[[`7zEx*@lBY~|*wT.+= <@O>WE}"U\ ="y.(BN.lsϝ ^9*ce@۳桨ҙC ]0>N &HA|[!ʣ-b,Om(t3_jSfU?}F0r #ojA9Br4!E\:L4DC{T,ܑLg zTK$se &7S:ͧ~X-O=R>x.1`Jh|A2UA1$j1xpjDtEL6UycxQb2I.9?>kg* WPcr|ny^b,`WcK o^`xx52Rh]pamOU4[y RɎ73̪@]@X l%Bh]p,73 }\] I.Xw8Y} ݥ+9 <`_~]§l+}CqJ"C<@{-mS^V9.`m]qɭOIH}Ֆ_іL.}Nj02Vyi(v7T%R$Rt~mwNx>um,xp7oq6}VY `&z`:1y%` VJ ?Ɩt9C)9il<֜@!)`ts7s#i #ٗmzIz=GPqH,Jk.7)-m,xHp!2AA\{VxYG/7j__ m~W!~o)D$vUqSg8_MD/@ZHj@)xY@43-0](z~'N> bzQK[Ѿ4:~@ʦB~V̷vct夎)?\ PAfHゲ#'+hiy9mlYġg}pl}>~1|p`<1\RAyɼ|ܹ >Va(NVgHOJ>}ٗԢC^uO6b˕qǖ*J3B-mmҩ>3mR\n:*uOu%R,M+lw0Vk-zrL_cr,r/t_Y@6!\ecF&h/Af26.yesŪRꔤA8-$a|+MJ؁@9573ߚ5 #z?:ZX2N G=ғY= <X^JQ/˺i[ SϞ;w#?\ȝ;Q$]3/9:c2!.фfyCR4y 2:StSlvʯu3p`:N+FGcbo}3$!)d!̈~!0pzo TՏ~%Gtփ/R!S疬 `z+jAzN"2tS>.P E>7c +(^yHRr1م0*R#2\ inZW:ZRw#7lT5iP6r_l#R Ā5M2{C4fh47[^<d%vTЀVl2h42pzdM8 z0L^*} ߦc&%G&ܯM26}m8c*.}x8kZ>.ϢVϝ*kt֜*i중{Wip@CyYJs(ԗ P ec_Nc~v~Ȉ緆ip~ҮƾTzς6\ǏZ@E_Ҳ)^ oW&j"3ۑ!-3erfC_-c KfLuT( -6GfֻFąQ+qج&?hƘ!e˜=rR/og)/6%Gٴ5qYLW!* S&8[waU5pX8 nti1]e /@t݃2lA(&$V3QhG)`l700%M!R  )0=}ihkfB,@?; J6h-L'vC_Hg*40 Zܤ)M֐t˔lJ:lQkvR?[YJ[`?ROBؤ. 7!Odp .[`fN*95iOSv:͇ka$u}Ł^BOlosd *PL0u>q@6MoǸCz~e'<ë|p>'+çן7(aSg%[^2_7_&iCγ?8O+YYcb۷͗Ͽ ޼'{)|qū?|y9i`qHsFпgsCQZ2pKB%YS'Y.EB4\J^&-6%R%龲DI+Q0!-S4&{o% & )8(Չ߼_2sxR{;|cAg|֞ Lz&i*è?L)g3^ Ʃ&FPFgu bKJg_屜gʏx~7_|1I|GCxHih{7?y/~O3A*y:!hڽUPl7 d}=R͜F*l6VWS g̀]}O"T ̳z>jbfUTk[U ŅWjICmDYɅpvNv>lOfڧY@ 9c /䑏&Cp+" Q,9㍺A"_P:%<:ѣ&cJHPbF#*4Τ JDj$ŊLj<M|ۋMsв­MS\JO=/w X;LK:T#|;N<Ʃ',FǠ}xVG︱Mˁ=2#7$fzznp:[ ZQ0sOR2$ĥa ~Y ̣QHFF,\[4iD]:r;l +D!ѝr +*E~;EVٷ m =02sK>oF̶+3B<{I(rH}jP`KG7 '<[ *,[.B2 I[Z#+4C7BY=o_9 1X=;C*z ʸ9eBػ&Vye,7WٯSG`1UۅrУ$(}g1E)J-7p"KF9g1DoA+w`5|(J( laV Fl e'2Fs;dklj{2#o *c;`HKS;^9}G*8ȐrqWo^D$3,?n&60ZZ% 1=e9cBOdx{ 6`F*`C~ne,&xP@ b Դ5<˲[~wNpFX8QEѲ(hJ<'-4CპBWj#6<tXl1P 0s}Kc( <(^W/II9=[DR/n ꘏g~A^a \Hs4HĔJ 1s>g*G"APU*8w"W;6| (Oq98KFElOgN^b.ov9$a F1TC*  4OXaB\1(gqF*?(rmnL0BC:ם9-Q;z/kX^V/]vrt.jKi/6:Ym23OA[d v}&>E߀⢌,tEjSw;'_l&r\Z1yO(^Zͷ ~?1h1y[˹Mps qbH]fÕa/.b!& Gju M)UAA ݊*L@cs\yy3!i\*0wH3< thBG70>J׀U>XZl* bY,XVOA{MV? @tf\(vQ9ޖ7UJ4󌗊 K, w&Cn,9C؄H7:;6D *!պ6ţQNRJt6M4)12,jmMEg#470z.Tv h.[¹+U%9IK 8 C0(@P>A_I0&Aj,3xFlje\4Va_}1tp&\a3vZmXz)v cT}21'⃝s# Xv#I5ͳV/8Sn")#Ř3hT?e+YPϘw  SG?",J %eH6n/ө-ju맼^C3cbohlkJ~sA)PnF%gy:5ģ6 N9D[𙠤\a^_kh7[ξ)k2- hRUM(v$YLj:V0d4ڼ?B1@@]M)OUA܉iWG,/ak5jZ"غՆq}_tzfp)Tմ(J5ԤJ@IUmD-\:된Y0&֭zjBM.{M,͗d\,U]jT=9uYD\/bF4O qNNY՝i,"|Ӥ{0M3Msvs)1oaz4G q<0u ˢQVP\ 6CC빔 24e QնVQqR5wyĩaUs˪ ~јZBh6 `M]³tc:gEr-!K0!;(7+SloI;O;h8C^y@v:t$# *:{zq/pБPnc3PtSTy>@wKF+&hi94 #ny.V'q@=Il'Ib1+$Y6+ EqB al`t:KtH$i uRhX~wE_'[⮬ p@vNxL+{`,0 ׆t9,n|u/*.KzQ\XWS[7%?qaᒉx∸ppID\\E!.?Pت4Li^7vh.) XCrsy .2:<Jȓb_^Qm%`el]w%!Xu0O'X@|8BN0 _ @pYJ]H$$&.*-l<܂d/E*HatO++%7-Phi WjYF8fy:9mn[;ƌ^lܷax[GV7!PVVѶΤ؈ dԳ}揘NGڗXkėkݨ_rg 6^9m-{r%{&q6> xee]Ƅ$44C1v'_%(8WRvtAAJV-]g% B1VG +p9B^$"<[d_{a {Z4p[}Q榊7l(FMߥ3/JCkդ:!ωJAtD$T8dh -V@Dx5xwL>{w1`h4gzc3ek`x9 "d"Pܤ)IN:Q{:Q;.wŽǓt5=< xZ>"bp .Фt> 1P8QrMo"i5G@`AWR9!2'F3l65s+z!=YY:9{x^yAup`rGr>36]W|`"f_f{st > a_mn1ޜʊ:<_py+л6`U<-"3TTjeGh :ox¾c;i,ܺwWvs*m:u~r2 丩*!=IVTD@>ЃY*B$, R{ y] SNV1 :]+}1]*q[;zr0e x ~qqle=}*b<;*P18ƃA4eE! Jwi#l6')a IA|93|Z(+(J3k뇻xhY)25 vit{K?>'J>>f!>Nf.a=_>/V`uxs&8er vvh348(cyx1pA4GIo-O+#.=YPK`Y!NzhӶOK'>9Ќ Tq0]!OF-Ϛlm *ZGoG h铰M`3--~hNQc>Kjd8:̢TH׬scYS#\7%\>ՔxiW:5\ [Od=DQ8dGu囷+26~ÞݚCxߨ5r %>!0?§1K":kyڡxb h?A}p՜if,eƴ(ѸOUWֽ%o˗{ԄXAjMD ߂KYXd !#E6>DO'CvPEF7cl%G(∐"c0vaTH-P= ئu6x ?k"?atG&=(lgFr,XP^T ~4oZ,O[cj露eV{$N$IqJ( >5 -4 E/Is'+GVŖd,JX< k/B_ヾ:h⏻f-_݉F percona-xtradb-cluster-galera/tests/test_drupal/drupal6_8080.jmx0000644000000000000000000064412012247075736025125 0ustar rootroot00000000000000 false false 127.0.0.1 true 7000 3000.0 threads that post stories false -1 4 30 1236113809000 1236113809000 true continue 9000 0 true 127.0.0.1 8080 http /drupal6/ GET true false true false false Cache-Control max-age=0 Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 15000 7000.0 127.0.0.1 8080 http /drupal6/sites/default/files/css/4e73ed892da4b33f96ae41a88df296d8.css GET true false true false false Host 127.0.0.1:8080 Accept-Language en-us,en;q=0.5 Accept text/css,*/*;q=0.1 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Keep-Alive 300 Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 Cache-Control max-age=0 Accept-Encoding gzip,deflate Referer http://127.0.0.1:8080/drupal6/ 127.0.0.1 8080 http /drupal6/sites/default/files/css/217c09558a102a72eafbe111b6ac3ac2.css GET true false true false false Host 127.0.0.1:8080 Accept-Language en-us,en;q=0.5 Accept text/css,*/*;q=0.1 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Keep-Alive 300 Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 Cache-Control max-age=0 Accept-Encoding gzip,deflate Referer http://127.0.0.1:8080/drupal6/ 127.0.0.1 8080 http /drupal6/misc/feed.png GET true false true false false Host 127.0.0.1:8080 Accept-Language en-us,en;q=0.5 Accept image/png,image/*;q=0.8,*/*;q=0.5 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Keep-Alive 300 Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 Cache-Control max-age=0 Accept-Encoding gzip,deflate Referer http://127.0.0.1:8080/drupal6/ 127.0.0.1 8080 http /drupal6/misc/powered-blue-80x15.png GET true false true false false Host 127.0.0.1:8080 Accept-Language en-us,en;q=0.5 Accept image/png,image/*;q=0.8,*/*;q=0.5 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Keep-Alive 300 Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 Cache-Control max-age=0 Accept-Encoding gzip,deflate Referer http://127.0.0.1:8080/drupal6/ 127.0.0.1 8080 http /drupal6/themes/garland/logo.png GET true false true false false Host 127.0.0.1:8080 Accept-Language en-us,en;q=0.5 Accept image/png,image/*;q=0.8,*/*;q=0.5 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Keep-Alive 300 Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 Cache-Control max-age=0 Accept-Encoding gzip,deflate Referer http://127.0.0.1:8080/drupal6/ 127.0.0.1 8080 http /drupal6/themes/garland/images/bg-navigation.png GET true false true false false Host 127.0.0.1:8080 Accept-Language en-us,en;q=0.5 Accept image/png,image/*;q=0.8,*/*;q=0.5 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Keep-Alive 300 Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 Cache-Control max-age=0 Accept-Encoding gzip,deflate Referer http://127.0.0.1:8080/drupal6/sites/default/files/css/4e73ed892da4b33f96ae41a88df296d8.css 127.0.0.1 8080 http /drupal6/themes/garland/images/body.png GET true false true false false Host 127.0.0.1:8080 Accept-Language en-us,en;q=0.5 Accept image/png,image/*;q=0.8,*/*;q=0.5 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Keep-Alive 300 Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 Cache-Control max-age=0 Accept-Encoding gzip,deflate Referer http://127.0.0.1:8080/drupal6/sites/default/files/css/4e73ed892da4b33f96ae41a88df296d8.css 127.0.0.1 8080 http /drupal6/themes/garland/images/menu-leaf.gif GET true false true false false Host 127.0.0.1:8080 Accept-Language en-us,en;q=0.5 Accept image/png,image/*;q=0.8,*/*;q=0.5 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Keep-Alive 300 Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 Cache-Control max-age=0 Accept-Encoding gzip,deflate Referer http://127.0.0.1:8080/drupal6/sites/default/files/css/4e73ed892da4b33f96ae41a88df296d8.css 127.0.0.1 8080 http /drupal6/themes/garland/images/menu-collapsed.gif GET true false true false false Host 127.0.0.1:8080 Accept-Language en-us,en;q=0.5 Accept image/png,image/*;q=0.8,*/*;q=0.5 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Keep-Alive 300 Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 Cache-Control max-age=0 Accept-Encoding gzip,deflate Referer http://127.0.0.1:8080/drupal6/sites/default/files/css/4e73ed892da4b33f96ae41a88df296d8.css 127.0.0.1 8080 http /drupal6/themes/garland/images/bg-content.png GET true false true false false Host 127.0.0.1:8080 Accept-Language en-us,en;q=0.5 Accept image/png,image/*;q=0.8,*/*;q=0.5 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Keep-Alive 300 Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 Cache-Control max-age=0 Accept-Encoding gzip,deflate Referer http://127.0.0.1:8080/drupal6/sites/default/files/css/4e73ed892da4b33f96ae41a88df296d8.css 127.0.0.1 8080 http /drupal6/themes/garland/images/bg-content-right.png GET true false true false false Host 127.0.0.1:8080 Accept-Language en-us,en;q=0.5 Accept image/png,image/*;q=0.8,*/*;q=0.5 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Keep-Alive 300 Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 Cache-Control max-age=0 Accept-Encoding gzip,deflate Referer http://127.0.0.1:8080/drupal6/sites/default/files/css/4e73ed892da4b33f96ae41a88df296d8.css 127.0.0.1 8080 http /drupal6/themes/garland/images/bg-content-left.png GET true false true false false Host 127.0.0.1:8080 Accept-Language en-us,en;q=0.5 Accept image/png,image/*;q=0.8,*/*;q=0.5 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Keep-Alive 300 Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 Cache-Control max-age=0 Accept-Encoding gzip,deflate Referer http://127.0.0.1:8080/drupal6/sites/default/files/css/4e73ed892da4b33f96ae41a88df296d8.css false name test = true false pass testpass = true false op Log+in = true false form_build_id form-e65a20482915592a68be8ccbdb9fba05 = true false form_id user_login_block = true 127.0.0.1 8080 http /drupal6/node?destination=node POST true false true false false Content-Type application/x-www-form-urlencoded Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 15000 7000.0 127.0.0.1 8080 http utf-8 /drupal6/node GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 10000 7000.0 127.0.0.1 8080 http /drupal6/node/add GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/node Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 15000 7000.0 127.0.0.1 8080 http /drupal6/node/add/story GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/node/add Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 15000 7000.0 true title Fantastic Story = true true taxonomy[tags][1] unbelievable = true false changed = true false teaser_include 1 = true true teaser_js A story, similar in form to a page, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a story entry. By default, a story entry is automatically featured on the site's initial home page, and provides the ability to post comments. = true true body A story, similar in form to a page, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a story entry. By default, a story entry is automatically featured on the site's initial home page, and provides the ability to post comments. = true false form_build_id form-7d25fdb5d0089c638ef2d946e85009ff = true false form_token 5f539d93894b6055bfd79011c0ec4215 = true false form_id story_node_form = true false op Preview = true 127.0.0.1 8080 http utf-8 /drupal6/node/add/story POST true false true false false Content-Type application/x-www-form-urlencoded Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/node/add/story Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 30000 10000.0 true title Fantastic Story = true true taxonomy[tags][1] unbelievable = true false changed = true false teaser_include 1 = true true teaser_js A story, similar in form to a page, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a story entry. By default, a story entry is automatically featured on the site's initial home page, and provides the ability to post comments. = true true body A story, similar in form to a page, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a story entry. By default, a story entry is automatically featured on the site's initial home page, and provides the ability to post comments. = true false form_build_id form-8a88a95909d46d432e9fe341c3b3328e = true false form_token 5f539d93894b6055bfd79011c0ec4215 = true false form_id story_node_form = true false op Save = true 127.0.0.1 8080 http utf-8 /drupal6/node/add/story POST true false true false false Content-Type application/x-www-form-urlencoded Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/node/add/story Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 10000 7000.0 127.0.0.1 8080 http /drupal6/node/3 GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/node/add/story Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 7000 3000.0 127.0.0.1 8080 http /drupal6/logout GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/node/3 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 10000 7000.0 127.0.0.1 8080 http utf-8 /drupal6/ GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/node/3 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 7000 1000.0 false saveConfig true true true true true true true false true true false false true false false false false false 0 true false -1 12 30 1236125829000 1236125829000 true continue 9000 0 true 15000 7000.0 127.0.0.1 8080 http /drupal6/ GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 false name test = true false pass testpass = true false op Log+in = true false form_build_id form-b2361253017b019934be93aae0d2fc27 = true false form_id user_login_block = true 127.0.0.1 8080 http /drupal6/node?destination=node POST true false true false false Content-Type application/x-www-form-urlencoded Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 127.0.0.1 8080 http utf-8 /drupal6/node GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 127.0.0.1 8080 http /drupal6/node/3 GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/node Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 127.0.0.1 8080 http /drupal6/comment/reply/3 GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/node/3 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 true subject Horrible comment = true true comment This story is sooooo lame, mwa-ha-ha-ha! Quit your job and get a life. Comments rule! = true false form_build_id form-def6cef1c57faaf54c344b6c8a6070dd = true false form_token 2ba1e45bc76391d74b59d94db2c444df = true false form_id comment_form = true false op Preview = true 127.0.0.1 8080 http utf-8 /drupal6/comment/reply/3 POST true false true false false Content-Type application/x-www-form-urlencoded Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/comment/reply/3 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 true subject Horrible comment = true true comment This story is sooooo lame, mwa-ha-ha-ha! Quit your job and get a life. Comments rule! = true false form_build_id form-1c9ec32e3cd967c8fd838ee71c3368da = true false form_token 2ba1e45bc76391d74b59d94db2c444df = true false form_id comment_form = true false op Save = true 127.0.0.1 8080 http utf-8 /drupal6/comment/reply/3 POST true false true false false Content-Type application/x-www-form-urlencoded Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/comment/reply/3 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 127.0.0.1 8080 http utf-8 /drupal6/node/3 GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/comment/reply/3 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 127.0.0.1 8080 http /drupal6/logout GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/node/3 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 127.0.0.1 8080 http utf-8 /drupal6/ GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/node/3 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 false saveConfig true true true true true true true false true true false false true false false false false false 0 true false -1 24 30 1236126574000 1236126574000 true continue 9000 0 true 15000 7000.0 127.0.0.1 8080 http /drupal6/ GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 false name test = true false pass testpass = true false op Log+in = true false form_build_id form-08da3427c3411442e541fb4a605cab8a = true false form_id user_login_block = true 127.0.0.1 8080 http /drupal6/node?destination=node POST true false true false false Content-Type application/x-www-form-urlencoded Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 127.0.0.1 8080 http utf-8 /drupal6/node GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 127.0.0.1 8080 http /drupal6/node/1 GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/node Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 127.0.0.1 8080 http utf-8 /drupal6/node GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 127.0.0.1 8080 http /drupal6/node/2 GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/node Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 127.0.0.1 8080 http utf-8 /drupal6/node GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 127.0.0.1 8080 http /drupal6/node/3 GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/node Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 127.0.0.1 8080 http /drupal6/user/3 GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/node Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 127.0.0.1 8080 http utf-8 /drupal6/ GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/user/3 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 127.0.0.1 8080 http /drupal6/logout GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 127.0.0.1 8080 http utf-8 /drupal6/ GET true false true false false Accept-Language en-us,en;q=0.5 Host 127.0.0.1:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Keep-Alive 300 User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 Referer http://127.0.0.1:8080/drupal6/ Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 false saveConfig true true true true true true true false true true false false true false false false false false 0 true false saveConfig true true true true true true true false true true false false true false false false false false 0 true false saveConfig true true true true true true true false true true false false true false false false false false 0 true false saveConfig true true true true true true true false true true false false true false false false false false 0 true percona-xtradb-cluster-galera/tests/test_drupal/drupaldb.sql.gz0000644000000000000000000023064712247075736025313 0ustar rootroot00000000000000ǷIdrupaldb.sql]ms8|~q2gG$9[=3ullJH"5$e;~x[3TrW5IHDxn4 xx˹nzvp@?yqh k$A~s|hv|/Zg83O+^]VP˛k[9!,IQR?P4N.'G7G?]g?svMg_ݜmȢ5HwJ=C0q6C VQ8wɲƍ6(6Vif?p1`s _qD9C"Dž(UR kީx _% ^2\;?7L]zoSµ`rɲ=FZ{i^tF,IғjsG7>49&(5B":zO=%zO{{PNN/I(=#]6HȊJwȚAMQ/mG FqDU "< 1ZN΢J7Nmq72/HċN [PC@{$Q$45]!8o",չe;FA5Et9A]/qᗤ!'^`uۻbmos7 Nl"]k҂-C;y׮`{[d?]Uͅg`_4e`टz|Aa=qYe!N[GQJkw볾p 7r&=;5kL;O{P 4t.=w"KD_ 2xΎ-l BPS [HAebk.8 #Z#'΃h[ b'm'h!w 'OC󵨕"=Em;.r\Vr1]lp~?F=|y.6\׳߶brZNMf|5]tVEA "$4{\Ѕ,גxaФ_(Ӈ$N0ortu@%dӁ3וՙR~ }@}g=.֎%w=lPSGzn DkRI REJbN=좆$K'%Lvwl;?(0A&S$],u~(^,ZEO*[$b4~]Tz1i9 'ȣ:"'`=sCO ?-Dak5b|D6jINq@I yf$K >VtsQ֐ 8O%ALDPwQnQ ȃtH|QAk1e2KH M,4hL`4@Z 3$#+WD?h[1E582T$ H2%MbVBNAW`ͦpIW*Pv4A?JF7DS皰ݐ|dvi8&'a#gBFDr3 "2?8#$dW%a>*K6>?|ɥx;?H4LJ9SzT֎܇ 9HBk%/˥KV yAabϙ Ȕt*[穔4,(abExQNd&##,a9{"i!km֐ ՞ߖɻ›0u }4mZ1qEX?˪ AU`sU:ѬM6M/A%Y&4)Wi6Y3Wƃx9)MM˷(DV5J \P*u9nN1]xS3 =, f;߼F$W  v;c:i#?@gYN8Y|2ɞㅢYb"> .ZuM{6Gܙګ04;r:BۙM-N+`WNmޮ;Glw %2go1 ] 4ثxBZ#t߯&OS3y[iysh8_*CO<⹬Fl#0Yn8%!\fZT8e&DV-\NiSJ$%.ZDd~rz٤q"4hG2!ӵ&D`J!Hh0\N% S)G=l($nDtKS"(S>2B7)DX]X͉䖛Y"MhZgD9Ifw.WTwwPHR6eRW%B:Ң/yǩlN-R![em ,TzRD϶H.oKt?yReKhs `0uX4N3Qn-BH)5{(P`ä.P(3~y&@)|"LYDNPwBHkAN*)M d|t톞t#=ă_wgZnt6SITQ'g.7-"\+Y׻&a.Ò-3%i l+:aaa*ݳjO׭;ݚ-Ve?͚R\ϚJ6I1Yᩣ:&M< C`F{)ѓgX*Dٴ% LWfϓNM7N"嵣kQVdyZSSΞ12(A*#$VsyҤ'9픨"SΟ QU|i]*Dә`[uͦ`S7iU=mumpSofP#/5Z\#kHVt g՜9*MJD6('F6AD*q,D.*Jsݦ1k\qnj&0I b2YVuY,b=Ղ9|Ds5znҊ7~ bްǃυ7o8zM݌rXZQ +QJA0a7'K:Vm.U4c9pdк(2vmNntv%&x{ r^M:Gӊ|:bPGTp& ft8m Bd;%pWR J♌ASZ\՘9&_~&vb8IN%?gsMMh?+kIz, !)f NZ|fGo 3\ +!VupJQ>$wa>lb.)ںۇ~N0dD> x\WgX 9Վbv[D32aЃε[ixѓvaIC*R;oRP^0̦<0s hkW'ӻ!Tn v$5IO͹fK;,4oy8?s ϼh! r-UGzTGG1eu7p! |Pn>s ',nW5;^R,c`@4Yzd_n,ڟS%v{A x=I}(l-Ѓ}AE)3aUjbuz&F'ӥM<)HLU4FHұi̐&A ղlllONxW-l(qMxJ`˃tB 6@*k!dhldV\ !SEc#Ӭ LHK#s4k.J37(ƆY&t ±i }-PTh!%OXRn؂iOL`S!ɫpD:wϑ\ f{WٵQB#eSXc{3۸? Xs؟HF؛(v Ys7$ ܟ]J\-b3cКItIەi%,Iy&z>GKP]v^5 OۅC0)YfOD:7h5s ߃n섧5Au_цo!0}Q𦰧R"Qkt`FWdЏ#TQsM5FPWj$NtDIKζ`4f~*qα0~\Mp|i/m=J#Id9&Bwz>o>w֥cAŎ[t6+s=X𼼩kT8_Ш7'@;wd.6TRNJ!΃,ڨ]qolը% &ÏTD[] ?tԫc^l)̓@8aGC/]HG |cZxJyrhuqA% KšfK 0Ѹ%X'ZhމI>˜1sZ&"^ d&QfyJG/n61h@O  t{ !X"Ҹu,M$*5YB֑]xxCpa Ywr,&OςgDJUs7lC?g·/"Co~;MX3ذ4Nion!!C&+!oRN{7$ w ;9:ڷ4:ˤR[)ڋ{ Yo!O-)[kyZ֝ΔW.)M7qE / ڇ ؇pDጦ7`!Mo>S!NV+)68ڦ(YB ~{}9"rb2u_-É5Ovۉ3 eMUᭇ3wSEz>L+ôr>L ^J#n"B00>~m,;PKQEMl0 caRd듳\/Ǜ+;G'x+x̔/Rl ~)%ثlVp`GGC#a_KY deNi^ ٲ1[V4f˚lل1NA*_Ta+ܘK*,?כ{|%x}Dt C#Lr^r^ ^ ^ʔ^[Eb8;u57Pr6>A~!>CU~EǛM +.>̢Sx~"M9X$H9Saԭ]ا."Le ZΣjZzdX' R[J*@~-a}`" 7f8sl.?%{M_S Knw?['$&=[_Y.;nw[(+|M!oYYn~]Y֝Rܟ$\[1l-MG}4p/. g7}tjf{*?{/n!~.!XC ` 2e0`,!)9okҴcEQrQ^y = ;7$,H~v_?<_'t?#/){SȄN% A;gL~+oQGc͏(7>lsG <}e$ρhӿHSF̻UEnln88 ȇp;x9jQ8)UzPV D8Ɓ)ѥ,x%ht{qO욞f /'7nĦD7f׿3Cb쟇XgbqKu0Ep}W>}ջ`>u/.5<|?aJŕ*|U>#iԢN?nOl/OCΰr գnatύDSY0,T~}K^gZ )垱f ,Se?s7O9";9N }Gw n+s6g:dD:deBO`{i'H%N)ۯ(CAt ?(=scv<T{lNzOz0YlO}((IJmv`U g c?b'GU0,OAZ5"ڟx]ՐRCwaZvAa}~2 1Ƥ#-ͣhxt%3 8#o~uq&4ˆH\~4ޥO}P!IfQFFtA^aCko v͜͞6vDE/G}L5)7'] n}9xA'Լ'6/ $Ih)Q-{ѹkdYR~= [/.7//u-U-_NSx񞧣K_t~DnG $_&t']kV//|LG'N Uhh{0ORWcco ,p5 *@"d=N1/{<a9/ -V(N=wn6wz0i{ǁ=dl?(!7ww=\m􀻣xͺ˧G n{c-Ď}..8>ӪhzH%Fҿv삃?TtOʣ+zӂ߫kb4D8ce1Oo%zJLj߆ޤEFgsyFT)cc(>5-F­ϩ7*[aOέc'gUw'J?{‡җ{-:A>.߻`)ߡ9 d$WqKn&Z>K(3@iq2?t[/B{Lqx=?k;g xb.&N^mUF/K9֤6n#M͏WaU3?X4|ȁ^Eo~]) G%mƞ@gR,}FM wXJ:DpO^!S5=(l-+{b'f|1pJE5&Sr5ԝ(^}{v 2:pEoNIQS$yDpe7K`xr8'U璺 p N \w8 /z􎽯 W]ӻ'3ѻ'~ß]΋[P|҅RW*Co'G}uGV .|î:Kp{C16;pvaiNv&?ǡ6:L?BSb\}m\{DW<"ϛcÓjKgѹB>%Fj͵C%PV۞S\sgRzoZt8{Sڼm.1_=起ox@K)lל;`Bb3: 5 YrG]Nf#V5FOpw"4w;O|[IK{/ލzx{sp~r*^>tyn* E!\S;';CpZ)ou{Z3N# Nmp^!^ Z'u=\+8J/xcsP6kuN-TW0q2.Ap(savx_']HlJu(wž% s6|Ko nSO`&*91<);hon̄(5M`wֽ>O#\LJN:$$0Z"(Pư_A94' i~@wRB aTn4̚4*܏^үw^"Sܡ@prMѭ'Wq1 ^tj ? BxK~#l1Fd~7ſYz_|T'ft4-d.οg#cg]N=ua$a=׭t85_;HO}敤`}tvV`%xy-l流\xpz^]_p\i맧{2st KxVt"X3ANҼ8 ?v Ij vR.#w.c/  h >sZ/;qtC:dG toLiD^Z{RGҸ3\D/W*iY[_ȓ&jKQV/ 3,Vd&|}F?c' ژ3_T6s&*jmmԛ]`?+^ V>7|[.&4bJpUͩw3T> `V_4 >hoH-홐)ΤPg4J àQzt1Y&>+tV6OTqLe*`$ct/ V|:XBةzܞ9U6WUʏTՑB\tx,loF[è(j"t^6^b| EjŅ&'7 5mΑ;3ۍo-ա=4t0MZ#H?U/ӺW엠dr K7_W,oY|ݰ}>ot4/("]%Vܺ :*19^`KУw(pwܛ)؛)>`XKm͍ vfto32T/>1oU{+U{ a 7d"gq/@-\/,N;|Olǜŗm063"Ϟ}zqvW\^Eqc8? araL`Ċoŋo^f-&y N٨(Hdjְ&$.26'>Gg 1([|agrj"D*;,׍;@{#b%Q1ERRzi2Y6Jdᣗ&MHLD>ziȺ fi+"kC,T_N^&#YE_芗ƌ,XZ_.Py%{dKFh4I4_dE1KK-[0 4YdGoK#oKKd2.<&*e<.fy_ar8"gEC^#1ṑ#{KrDoscG|% D'gM%$\S/-Mؐv';OF 2FNB: }Y+W^NqU5rea.y?#Etxs[u_ZjҧP 5uśi'ղ2?*P/蘯Fi~D[fCVGtTY##p*,s#]LdZBqw0LՊB[2!K dwabtqasCs2QJWbN]㦦KEDBɻNzz|+uLqar'*ݺk&/7 sm ԍy7 nz_NЙO_"DIBq~FR*8Ï7y:S"Pv&0Mt~""|)-<rM$M%ęqwP yٔ%¬ɊR7~1$]XRAcI P K"cv?/ x1iDgeԊj* ܳps)i*τ E&n4./c^@A=K%瑱p/#ޤ$y?>-Z#iƢn>}.Ko6kŸ4KUr%?M.kI ȫI!xNmFWzÏg|â×wۖG7W/O E'CD$n2/wf򂰲췷rpdhq /Z YgerxN& JL?#: &T9X~yE]x ӢԠ2 qV>s']HQ8-z; kB*ӛ$;-D -.N=??>ڗOUA(K'PEUrt(&b!rb:q{z2KTǢ(/VXԩ/]\"e(gGON󇧅]|{ÔZ;Mw `HH;W32᧏:#k<<.$z[>;??ݼ/+Yώ49p;3',p^M3 ?=̱WWjE@:,>'w8:s{ާ/$(Հӝ0=+I .XHb&^ϗƂ?6v=Lk)ӆڶ JȐɊiȄC>9TO!YԽNߑ7SxMO/W|b"Eƅo|6/F/ mAz-ザC(|SC17IHۻ}fwpՑc# J7"EGidB6+bSݔ{EXEW;*TCM_5xm@] ȣURa >lwä@'gT_n7W0NcdxLC& =q KE68! 9`P^,*<6}#-.n >|X|-9Owgm~pquo?D.KSS>/cw`unVp`ZGAdȰ#am;KY de)j^ w䅭ue,h̖%ٲ1[v.Y~4_o>PO>Yf`)X<6^ ^ ^ʔ^[EǛ8ל;7P«6>⑌!;b{>i0|EǛM>+.>̢Sx=cPGnu#gʮz|{],7>qX©[]勉%,$J8_MX~;Nh*lͯYp=m~yܾuB}nï_n=f_;\S;A:u'^!߽}|ڍϿ?Dמo:Nz@M뇃pZn8[QzSфn8M)i NHs'Qw~*ۇW7ζUJpFNZ_;5-Bq.)S.vcXt J~4lLvOKjG_ xcatw0ȜZtwmJ o <.4 / xVL+Yuj}(w(;woD$ܮ;p uD> K//=5aFL's.}*/ /7vo_:|s[ΉyuzVMroQv(|Fgf_|U[θwr0XHޭϰ33./ /{%wyx޺t{wh[]$H {#h?l%gy|[Rq^jl0wOW6G9KtJ$TCq:S ?wi'i QhX#ͱ~O9;wY=s//Tqպwk/7b2[\΄69h?DK@^%ϏehφD`9]GOoxĮi֑dxkfJIlZIIdzs~8nvKOXp:S:Ⱥy{u&A4dl]_'KLh۾[ton} Wxy sZ_]9J#Ob7p?RB3]_z<n$͓qy(&桚ˊ;87ytKd&z ګ믻MPg ]'NP#l͜zZTSWGTNDŽ 蘄-#[B/noz{&kh:R$n^ 3/9N.oz"q^h;y) EđZ +z:I~8LI{oPKƵ~Yn|giga,2H65ԳI8 atf*9)a,ׯvٌ:l/ukg=Q)Cv\E8~iJQr~oȥ%#ө †П7Wsc }Mqj85J9N=z`lΏ݇/&GggJ3DۼXU5?kObcI8B0e'ho(SVHVRj=Lˮ<(}^F_5՘tyNzxdFp/.Vքy@vˏ?Lػ4੣*$,=Hz‚>ѫ=y|-= Z}nXANO:He/iF|Q8妃A t|m0G<;Wq'^ ヌ>u~M[7rjKD5)FNW.p*[CYV<͸ѥ^sE? pGT ?i4 ~,;؈x no8"h+4 £\<9M.wZy}MY!C&]U<"DHig)8e" ;8>E %9@=>G}N&m8*E^?X:ys55gpZs1EދY8bg w|$O*mRX =?8^ p:W :QA *-R 3;\Pq6P*(89z%Wuӳ'Gxr/zXԋ>6]7?z^tƨ2-{kD\CG'D M&ӑ_/ egp3gW?80h&T0:/図Jp_x+~WP`c%ix zKu֧4U]{Km5/a3=?U|nN貋ˇ/S1=w+5YL pa&zA0eq+.xٺgL pSwfyty(dHUa;TyFM@u<,~F,,2JE.Qo'zWK/[åsW89{7tOsF;6\qN"w-NCVG tj vm:/b@o]'{`BIH;r}rsh"=㨯rʱEߑotP7~V.·xq(f..CC83թs$h8TFǧIVhJZ>YgL?#|>wRp9pf@yLcE6 9ܻg]Z@~4q,C[b `\*ܟ1Tbuo91x&u F/zE7͋lxZՃ^{Kx)`3 xA-N Fϼ;]=sҿ^9W DzEV|]? o4ŠՑMp]c%b/8w:6ۚ iC+&`\14p^BOu: OC5O+≗瀁18D :>|WPj T-x@1pɑgD?f謎hFn\:pYK@F\6M _ %&BE@d8-?2_>(SrZ/~@|=9N/:|' a#7i0jܘufp [?cFxxGPJ *6IHM(Q, ;,*ND PW\:"j9?[sM>)U_(4)ՑUJ=}r(lYD>Diρx=:1zR3P^x` HEhK\BJ:\=CP`ASβ Etʶ1&>M: 3jxŪGĪ}VĂy ,f_ʢUK?pJKYmAybDot4 2fN_?{VoJ?gĺ.Zr[3bSaDN!0夙\+#/H3*(CKA%50k`&{xdp?JN>Mܫ!i'b YLU4 2o9O*|NDƮO7)*W']/=53 y0bK\^2fV ~F}39 t,؋]$ vT;AjsxqUĆ'TϋiEO,2r"Q8K4E[V.fEj}g^V 0fGƪ~ק6b^t@lhi( İLlլ-+?)rM6${" EͣbC|DiD I'a5d}zyzaeAUY^d\E02dOZh>MZ~;wSVQ)l&sCq 91w؟?߼zu~U+jꤷ/ 2no>lnom8IoaiZZ)N!B 5Tid{G?zfy^YYcQUի_UWDNz|(*8msc @&渤n2*ZbwYLqwM3j!ca_󛅕F|opW,G2 |e\AF@VFߌJ]܀RS+{)$rA5t~9}u&J; qMO$OSֱFgY^e蝪3K&Ы,c8~b#nx[]Xo{._ ]XZ  $ z I=X TX8S;vr$y9_J1LZfӱŰzC)EzgG coWV(khCZ6F qYsPvF4ɂÑ?(L ʡm$>O](ě$.#7z|c𥴮0ׂ9tGWy#Fgv8xNDosz2ΥGdG8 sڂoC8 Oyi| q\D/G崬-_ȓ& %GBbՋcW;xŢjɂs;MN j(Fk 4pU όb-rIov}c[P,8 A,*iŎJoN흕LA=I!!E Qzt6g T8.Kiy#v"~&HƚiVJ,E*j_$k{U"z.zuW(]C$Ϧ*dsj :/:/Hp s}Nʽ)vݙnxmmվn2i;/cç sb NnˎSMބ|]=;M;oL|> zq(m"CHJwRTl B lBh}&}xLu%nGND_ܜ /oi6 so8w Ͻi71X|\~m71;`mFJ2 vD?17YKujRv{$D۠b$Nw0g~ќƺHpCV <{AFKi{y -!KA^E.f_gDN=<8# N d$|DCb%~7Љ3Ed!5pES8̇4{K]Fj|dzW׿V=˃8$|agrj"Dʩqf8n>.˱,bՏlŋ'[&kR,}6Eb (9"_(*~u68H\5zK=K*?GZa)& iz[[')?8 CY6EBa:8%"B(%O_/""f[>{i6Y[ǹI}dA}ttIs]?=%d.G/M&kF,|d)G/MY,|EdmJKd$ ]Ҙe"K+v,@^|҄"G,M/Y,bQ}ty{*G/MY0|҄ ??JY njnnXܮC`xѩn|Lxnޒq_ɂ, A#~t It;ԋxKf60ӫ0P8+Zuοr;/&b-Y撧3^D}hnǛݲmeJTs>e w¡8W˚0Hn_TzFi~D[!CVGtTY##p*,s#]LdZBqw0LՊ^o',&&Y]vr86WXUK/4"d/շŜ MMBʼn9=n 'c}PƏq& 9N~ ޮ[ۈ_JEw=B@qJ$$1R /S$>RD/?lz/$N)NI]B)pf>>bieS&&+VJ} @Ŵ,|w1cI %7| WdI$P~.U'|"#/#=HZQ]BQe{6n.W4O_8M0"'B uZk$X=iZ/#(!9P eM b BtvdzI^,eԗW..2 '®\=ansoa`HH;W32᧏:#k<<.$z[>; }^zvهi?9)f![jПAab\HGGg5so4[bIP;az$]0Dc!x9]0z(xn: geE%(v'ԧ*ä@'gT_n7W0NcdxLCq2;c*,L ,Ah+X<Ace8ϟM_Hu _Kyw_)=vcO{)/>RĔ3@ev(y,^!߽}|ڍϿ?Dמo:Nz@M뇃pZn8[QzSфn8M)i NHs'Qw~*ۇW7ζUJpFNZ_;5-f] KʔX|-|;]~ 4lLvOKjG_ xcatw0ȜZtwmJ S.\ً.4 / xVL+Yuj}(w(;woD$ܮ;p uD> K//=5aFL's.}*/ /7vo_:|s[ΉyuzVMroQv(|Fgf_|U[θwr0XHޭϰ33./ /{%wyx޺t{wh[]$H {#h?l%gy|[Rq^jl0wOW6G9KtJ$TCq:S ?wi'i QhX#ͱ~O9;wY=s//Tqպwk/7b2[\΄69h?DK@^%ϏehφD`9]GOoxĮi֑dxkfJIlZIIdzs~8nvKOXp:S:Ⱥy{u&A4dl]_'KLh۾[ton} Wxy sZ_]9J#Ob7p?RB3]_z<n$͓qy(&桚ˊ;87ytKd&z ګ믻MPg ]'NP#l͜zZTSWGTNDŽ 蘄-#[B/noz{&kh:R$n^ 3/9N.oz"q^h;y) EđZ +z:I~8LI{oPKƵ~Yn|giga,2H65e&wxhٙL,;6oV`ixpvZ;X/vP5  U#RăzwӲ+ {s_@)e5&inEcǣ.Q1!| 5q&FD򣦱G'|. x I2m26rjF;F?/_K|V>hm9xЇ't/}Y-?c g_%N`p!]&_w;Q&9j?g<>U\q #Z4ܭy9m5r¥`xFut"fxY_#'pq{+p{Kcke-ʬS+f\R{_9ܢqp#*e4CR?l^?^NSK7ۇOm^.a& ~u;-<\>\&!g*`]qZq $´3sN`قactۣzfp o}Cv"W{sw7QJ;QZwxܬ:|z|0 i"pэ^Pq貋s^3*YɎic!k.82Q*EFW\98>ҭ>-8ɺ&FiL3vQƝVHĤmpMPtmdpv gTy^)26b9}hh- hq. d2^;h|‰SqVuw^ Q8gJ K.|M~ '6x),˞Qw\Vq`Gj+a(ɁtZLKwɠMv@EUc.8U(V~Fbk]L䫌:ٓ_\ s<9=,EnEF=x:c=5"DJȯ 3v@3 C jE*YїY%|<A?mg(hp1A4<|x%bS |K ڌ=JwMߥ6Y[Dqcﰂu 蟪Nz7 'yt㩘EEֻ~jzPX8ذ[V= O2btḕjk 9L94vMqWw}n7:^ٿ[~x[8co١!D9pgg}*h$+4%z-Br3~jy_Y>;~{L83<"gbcC3.- ?^8 -10R~mE*1 jK<:|FOТS ٛEhs 6<-A%xsΏZHaco|ޙZ'gޝHIpOf;?o·v25z҇s ܉~R s7bMZܓ|PnݛÔW򡓭wS!\E, 9 c٢Na+OwkpCƇ b&81^Q};mMM!^0MgwEu/m:1tYʠC@W .z<Bb-Rmඳ\CJ<+nK _^hh+nbGɹu<}M]2oGrZF-Rꂳ/1N=8FY3b.&J|6?xx9ZJ2>&[f;C|n.r<:rt$"TY!Gԣ`5[:P5~]xxR='R\+Mz O0sz^W.1L%D:8xlbWNdͅC_S>^*. ѐ$3 `$ox9`` ι<6g 00PgJNÃ;& J8}/A8,2_>b4ף'ᨙ~fϼI<?cYT#LI/!do7(BsLx EEOrPӿg<_m](Be&Nx7pD5 ,l6̔0QNWuyYYÏEU5W~Ϳ˛7o~$Opn /2h%y^+U桕Ll[X / 5I5ۨIrA$Kϻw:adŋwk>O ׀Mq,NiP,J#hy2wr˽6<ǛcwS"|Q[un\i8qS}# |  ]F&D?oi_3N`Nx?#7w~TfrE=,A-_/\GCuv ƛ.w[o.ug. _$7* (ÿz !SHqRw{,nr @h x ؄{ _ƻ-s ,s2<Æ.UO]k03:QPg%<\uB#oYpV"QD|`p$N~˗^x-gv⟬W|<#jYӴ.pW~k3;TӾ}.ǰuT/$grB.+%g=A8-u#2tƅ^~M |,5ةDיm:rCu>6tя7WXdpWo %riwwH{[/-zN.f/9roK~U[0 jJLQ^F%S <4~pxƺ\KpЖqK`X¯8Xڊ | n$Cn?WhI8G BMkz)q=WXiks`4`|Vc@?nk2Ť.bNu-0ͷ^vc4Mø]^O^H~L!_sVOD\Cbw~ӓe߹/ƿa.zs|EofT_{޽˽{0_8l\?_?|zÙ`=ʿ{֯ec\e^ߵǫFdAT3*\ow].MD].yOU̎O;G8X3@8,|ܓn7N>^D{c-+Og&7~ ntLE7~﹇I}rRo "F`<ћ^0LILe~u _]o`gt?\_q9̽ UkN;z %eIx>I ٬;S#,6 Twi6_H@g8ܳ_Qpl_h*異*S>"|cgS"%qEW֚%w24+}^2]#ݣߣ뀕0}hPcO#i'>T8Aqss52%vɫh\RwƉWyPuB("7 xqBbe`|F<l5ÿ{~?Fkq}A6 WH:I//n5MJfA^7y5 9EK2`^JqUq@0=s\>h$oQ:$ͷ~lŰIx37nнƻn0Sy@M)2y6M0LzўhޕFWپS/V/!ƇNdiM_L \} 5ɯF9hZ^TG/O-LinSU;,0Nbo(ݺ$őU!ֽ_k(ox*;/䉔.gƍZS77nS . m]ɳ[7 C)7~a +X*]!ҳWjI?yz\Sۇn CC.O_kwӶ5Կ]O <Cc|/]wM уӆ@QTVڮɪF J+ 66 {b7+|~vٳ-*vr}Uv{~_IjնIߣjjj$֯ٯ®WӬc+3_m?v_M7r chN|rj/;ewf\Y}Ȫ\:c<emޗ]Y^}=ʊeL|ZHow[}n/Ny{_|B _Žɱ@-Y#dН,~G+Zm.ܝ~?;΅6慶?Ղ7QC~OCwOmKwYwZl>-]:)9z|{VѭiwO|=2#{л_'#w .Obs+7\ڢMM&rOϐd)j)t{eCөGwGEag??&X@,EuYrOYzpL=P}i%;UHXy%߾Ÿf|@ w vB'}C)pO[8;rm:8_{8WpoW c(p }D_R~+UsX~=Xak17W _onoZjBiMx2rE(#xq1_!VӇH?8 q7>zĴ1"X; "Pҡ=~D_n~rN_v޵Ia< xotɽ{R;O17%֙.rj]Jkgʺ{|:z@A: P-ܾ^/-Ϳ[1F5#4Hm=Am$~7@1W*<9 Ω`<Kd d F85DFW1XFM9~NpqZ ]w_Nov]&Z!c+V| ^W?/?yWw#oǓO'Y.W1 Wqq )^8;N/)Jz=m$iUhĭѦr/(BV!xk70Pz]*ٽ8WPPŝ=ma(.s7{att^]XB8YgXA1b8% `4=.?F#, hVGNwNf'eQes]nrOuX) M[R".o/˶mlIM|-h.Us]\yc22o^|p$YnwWyn&unZlK&YVǩ/ˤ}^J.Jn{Y6M{l6̚mFm澔nmVyQr)w>8u#IݬjvY^\׭Hd[eu푤e.2.$IKb!^nhÛҍ˦gERe]~)d+#$y1x:ݻ*,\v*N*kIjJ:4ʢ|VחUSϏro2ikի+>}7.iO[L1讋zFCs#,d[mh;%1N;攽mcr)luy}֠t`DF숤Ir ֩Ω&n"eB7GIڝɕbmz~ҸiL~򪾼r)n]-A:xY'N2eҼ$. #Y[&|/ںvʙNWقqISqN7w;3j*Tۦuȶȯ}(p$}i"EҶav[ mV_Ńaߖe9vU:ߗ[IJlߴWe[q?ڪ*Ⲻ.Ӻi&q#tsfT%/=Mym}Y"U m|uI:~mn8kNMu뾃(s_Յ7#_zg\nܛ,>3) IQNNr"w)btKvͯI^N 'eM=Nwbe{7N3l 3xםN:kZnMW:˫K'^˴q{ږsB -Er}lm:U%_UxM6L'tw^̝^Xn~ױu[;]).RJXT3U5x;nUN%]]5!-Muړ_vu3J3ĩYMܓ;nx*m=83.]| uJWS᪗ã?zYw !3&z^>uxK uzHc,lSW۷ooޏ_nus~K9>( )|7OKvms:H3f}Z3gQpOmo?[ϠvXo=~{@upƙ\p/K:ןOgFCszst6w sg2STa Wt3!}Ԑۆ?Hc?]E|U_nvTSَnA k8w1nnAuϔ!wާ7ۻQE+4jL5GСzKPG~(tuew)~J]y/岥2X‹hr?]{>qmA>OoC9Q@ow˥gt{z{lxwt݉ _z{tnxB ɚNNm~{(t(Trm,'zu$0 } Ӡ[]?]:n5&8G<;4JҊ]8>]lڦbQH|]d~zwu"_?>n|yZuo-K^k[C{0~>]Gn5g&e}O7ͻÍe7e=P !G\Ï]qKa *n uF‰'[KR[rျޏK Ĕ*A),鉊 -khH{BP5ʝdٰa ~ SӌnR%7t_ts|3hVL0Bɂ'Ыg ?][+0K*PՃAI.k8|Ճ|;*k{~F +7\J+AOvZu|x{0*BX:6_+] nx?yЗz9d?ܬ SodiG[F:3I 菧_O_Q{'dxBch ^t"p|2?:g$/1rZdyHjbt2W_ZݍSK0;h+W]+[2r#M:kzBOGYS kyR?[Om>)p>K⯜&ʊ"9lwVigF@Y9O&O;tV;[̿D'onOSo0\ I(fؗagZݡ/݅Sfo_7p84c}6pw I<ebPN_m\۴}riYVy^bU%eTܝ5iܵU^_LGnW e?~]6VB0 =W~J࣮wo/R|lB3.M׵n}}9:Kn( quI;tȄy׳9TB|0WM7cGw_$xa;uHhއ)AYl^97{R=f /j\?_r-kw7Ϳ>+8pͿ?'8H  Ϳ  b?1l5zo@כ+`6nӖ9nnoC$ |Ǹ0ĂXˇ/7^/pjw_m o1 5as{4tw[S8"r_9Fq}σo<4O><~Ƿwaa7#W&=nog{ WO~T@r?]7Bx`~}7j?g%;&y ?k(*n.^K/`C: _6Y{r$9 pAC(0䠸F?..n?|r߄T(N;<-d\gvw˫vn)(Qv{j C޿o "I-[JyGX,S3JРLfX7?R?QC8O :yCӭ?JgG]Py%|Iùjӹk癀ك?1 - oYxqp3vZ)co`5ƽM☲5<"y4s|:{US*cEθ &=֝M'NG*2d=\-j*-ƱS#%\K+-,)}rF~t_s<ޘ\+ -^\(FFW~i'<,?Σc\T 2F5$p&[7z!O_zo7+>~3>~Cƫn޹Wo!!ʫozxW_ug)o4g],8U߹6O>A+Xa׫{ϩ _>֢8W|-~pZ[:,{;A:?+3_I÷>PZr+^I'wMVֿEcMuts2h X VB(S^> Ư__϶¢wNٜϦ+?jtmZ` s1k_ ur@3E4;MjWKJ1j_:pNv-6͗_TůV}e%2Lv^dO+IsxYxerf Y 2k~%g},d1KCѷ,,OjDf,ĂC,r5"l)eD@CIԜF, 4XSܟ9LdXt0tj91a0ucř=WX&ƈdFNf֤0ai1\fN&29%Os!-HcM?3!{0<f.b\ ufeXc,J1T (ȼci"rK3T`D&,#R|r.Z tiɌ`o)x+)(͛ɉwllAl&oe41!XP8@S4{<Ld62eDDPF/hjP;S`Kʹxz B)LngrZւ#K 1*Y)AU6Sou)P_Bt@2f0\0519XunykLBX6ܠ}&.Dv@WDχO.3²VM-gԩT9X\ 3t/Y.:²%#ba֊P :#k_:"| ͫ*8X6cNnk3O 1@`-e&E gqjr`4=L[s.3@X+bÆ%sr7,qe0\e.7l%ؾrNZRcIJL0Z[u%FrƜDF,#a4%Ke6%K 9ׄXڮn9;nтI5_%-g?X/lESY~K΋[ K[N[iRm8FX62*pIqkHa0mUc(/€%ƭ[Cy[KX6< V3tJ+0ĸhdT`ɚ+HXr2 a-z̓!-'­!f1!L|?0K3\@X{9N~k%`Yq+.P],FsuKS%Uy Fj[ Fj!ha×K06| 5Xn`З@d,bYSrkeV!g)eDmFUEMCfp/!Xs2 IF3T&oK*\,cҔM eC%&A M0^B%>9q٦9Xs+3cMn*v"a*ƈ`#ڌ`ce he7PFF0LU2'W"g5vn gH&'MSG3aY;y1ˆnGD4agt\F!$91'KqтDN#yZB2{-x{^>nykAւ#etKl.˜Sk #j1k!gihz. L dFf-(tZC@fcI0ǢH#i b)fF0TfEHo#ӝZM.pEYtQ+c,zp;Sfn%T." v+ v4ca9&KU,F(EkwEqͫV/i0͌`M)`٫adq` Mf`"al`)PJqmXBKưaHKQ hci gɭP W 4"8R`o1'iD`o -g­4fX8/cC#("YΈ[AS.ai LZoko X+d`U[X]B0"ƚ!^Nv2\d}Og+F`4bibb[kF"ΦcN'_զ'On5ga9D`YXFer+0QeaM w1 $羭$ANr@ 7/fy5/aiFVS\_cz51bɉoJ}d~\B~#XBY~kBu'39o cЬ$GrRqd<eD-F\"rfP6ƒ U3 e I΃[qJ΅[k ŷZ0aiJ^B~( t?1'{KT gD&gũ)5EP>aBPQ54o#Z`45ZD‹pE l9 ٳb͗ 4oC%kJ-ai*`M%,[eC-Sl9]nIr%rܺ1bPvK0bϞ 7KX,~,~K`tzlԆcklec3{&ING@2Uv4o-ҡ2p cN#wJΘ[kFD=;0`{FXּ8ܹ1!MTɎSxMe7AdƒKΓ[PDΘ[k!jD}a &k ܨe$S_)snOEy$,NHfeAXȄeMs#έ?FU_Fk~ 2"T9I~?9F( T 9b hp)ua1@ =_rZ꣥qBL[S ˆ:\[Hh(u"g*2DfC'ZN|{`yD]K֔_Ҋm(VW5`ZEnWlDQ-XDЪAPm`4U[B&,M"Yt` h2@FXGΤ[QO! -핚р%ʭ5:FX 7kPM#U .T8`0fVhe-zlD/y`Nj qQ 0TG rB,U4sZ0OM5ALX /!he#\i8:mՒf04,!X #,< F2FU"5.aP-FfkۓeMmZjAs+`QMm#1'˚GAވ\#rZ0/a%ԉ<6È^?G03sK~2vc)!Qf`Quu ,^ L~9NKUml),5K_Χ[gAK6vZ|C9oNOA(Mo-tki1 'DNk)teC/&MW!(/9xDc,pcN$)tsk!D0"w0'0J˚383CM0!t 42'm"FRƈ(9fKc@N[Cm8 FLp 9!nM31I[IJX*)&,MO1!XS ˆj\cC2 \dcM/&sǪ4"",0J_D0'} t % h ÈEa CQ1,*1u0CH+j:4²1AXFq0 lQrk֔c̈́li9!nA0k@`X WA΀[F\2d q%FƈEwJp,okJP`0ցȬtK5K1,j99n}0.؈rYFP<%mOo vVO .{>{k̷ D9m]jmFjf0yKS%^ ,T9M5!T F9m0rC,U Lڊ/Ain9 f8FTz [j.ˍ֒3z H9nAd`#`I\iaTe/"S6FXD0V@NkSS7 JG4Lk \n[ 2²ᥨ gibi 4۴ߎ㲖Ve]4jڷ3 .FSCKdT6|Ɔ{h̥rSh΄`.>FsB4`5]Q՝nHh9L\Bښlx:FS:3ùh)o͕\#.ZqՔ:f46<L3-^˜J;A_"-r!.MIcs0tì7aD<f43я#-XDeD4C*3X `.+r. C0tYc.4F 1HLeÝ413hb8[ԧS-50n,_rL4-Alanߚ+TKеhA4`F3'zKUFR5-lFU5C M 2 ]}^ck̥|FEd\1 R%hifE} [%hxk 1UFr˘J΅u*AF]QAb\Jm 4Ʋ!99L\:0q(F@"7gLUI4d2rwk&bP |I]2ODc,J%JiS.oM29ImM kqHm ilHbNs 3Thƶ誴@.sT'Xseb^hmrs]9%qX̄fË41ri֭R7(\h3˚?Z6FϘf`Λ^9nF4k9)uU#CLe`~ }-r݊294.2t`t1"F]``.{U^9n׆^xkLeD 59CX3.0heh٨4rI?UFL".UL orb!.9vAS257qLwD@)g5Akێ۷&sTsAU&ܭ?cF1ÙΞi7׾7GQέ[T5_/s+QhT[]m *#/w`HfN#LN[5`5\Uy */1؂cs 0qi*E Ǝ\:9J"5L\)y`#ilE42t&UF&m1׹5mx,FcQ 3+2"چsiR6Ds2T 4=`-̩e$i( tvJ!Ƚckfh.&g% FG#IyLcNH#LJFɉz+\ܾA1h6tn9'o% 4g`.il=crnNTrH".͸ fxfkq%1=SHN[kH=BN.%e$uhTSJtoȩ{ќ;*c}sBadcU 9Os53s(8Ic#>r"JSB/z0B;\si\kUh {+*aC%ś|3hI$)RhVof iI[T+4hϞ7׾fztx%g?hScUIK rޚz q̹mUhyxj{LL*uԭ>USi4n"ô]{1/4$3d!sƚk*+9oN"`Fn4R̥*wW.k:Me4ݻu /3pLz@&. 3f33XK`B rhzw&L\6’F3ǹoHn$i1,gho#խ>F[.7/QMcYLc-䌹4r>9in7E42si*ޘd\d46< v9wF\ڱi :r̹;2eנBΎS1`4sb̉itg(Q*h˙sO91DM?3j9uQt$t h\&.#3X;~Eez/1_N<:7\FZt(Itk)b![4$:]l%e*k̥cעAαⲱ'46B4F5[4VڷעSEμ[kܢBβ[GV>SYs^h#˛d@NνSV͈%EEil^24:AWN[GƘ\BN/2} jwQ4̻8h&R44 &9nek&ES@N[50q.\nʹwk DeQ%4`#*0ZֲH&sgw̵=+gԭ̠)t34<:y\6 ֲNL oY,CCI3.0XS}Kγ35}ℊh66M3hz'2Ix+3$n2{$<\5ri&56-Siz{i4S7Z.U]'Fy&CV]h4;r.oiWqX9stF 1~nr)5S2 46y4:M*f-價l)Lehf#i4rpH5r&.sHpQ:+l_U&M23Xk䲑4>*k.f27e4F1Xu-rpA3A4YE\`mHKS&9o 46D4(8 0gۛk߆@80& ;·~/Fἦ@(&w=fP:-o#Vihks# 6jkbRW|Üƶf`D%$h*v$bpZ]` Pckq e0VB_ί2ⲡ%yzk o^9nek15}BOrwˆwi4}z&V˺h|UQ.hU%r_b0'[U!92"sƜK\xZ#ƚ?jKU'&QfU43qӎFDDcM> ri!έi4}u ֎d.kc2xPFsSΑV%t\0@F9?Ns gF sE˜Fǂ9##rh9k4${h)ĥѵ`si s]Fƈ&kx:&iEqNb*DsxSbAiz05ulOF5.`óܠ *۩>401k8W $1F1f023<{4=` 2";!' b@KSN[Vݥ9Osm=FUÔkhS=ooj1 !4rK=w9l*Zt{d%#aALePf4sl[4Uk0WcM0dxe-c47&ʈL4w̠9"%c:s$e]24mm"$RM^`P*h6ɐ˄øMrQakAL4LOI4JuBB'd6)Kяa1We-A/pDc±hY zr ڵ Z6M&4G qK \&2?FN[wV.ƑB3.0XeWܦh[K \te2ݦ躐3hi)6c;{XvH:wL\dfP;ڦ蹐sv]"EO3DeC3&9'OƜ%.36xiieD3&6EJym9OƆǘi }3x*V)a-ci4U ҵʐ>_#}>F`ľ@k;|̥)hT &1VsUa6|Lcm45ުFM4`G ǁTg3r".-[5`$9i\>894.3hG9AOs5qK74^ fmݽFʧw )4ֶ3p$qY Qi ]-&w#:sB4&:k2'ˠw:C/ͧee_4v>B|SriFAÈ&s'̵o;Hc;4Z] F9oA"ݙh,thl 2l0 q4:Y~۷2m`&u4!̡IrdP`+\̠W1`# iT,rk h)z`| DžV=JhDc\-U9Qo7E4kshʹxz켵 1%hc.M=@Lο[mc%:.M 6$oLA<G38f3qiEq4:k. ə<=. v Ϟ77 '?ƚTE41h$}sBrrZZx }C\ks^`f'L4r Z=t;fP`D%(Ú[oHN[iĔ2QĮ4֢KNSI3 p> -\F]`#ʌi%:fHl1'oZ(Z:#h4Je19 :͈:M4:[yQ*t!Ci.7C9S0 ӘeCG4i沶WeM&.#uLXW2=׾* $r#L4F$2: UiePFiTE/1i0 %ƜH&.#"hi4cݘgilT.PckneD&I.iq4VsM34C4j T[0f(3hkdMjs9И{ 4wY&诐sK9ИЌ&6*cq2 zvl$4A~rGP蟘ɼز hF1ќe<׾9K\6%iwAUN>GV1qRCsGG?u6@fL !Z1o2 4r6EUά[GF16"Mfɣ[WT6 )Zr&.MOHS4䜺i)%rZ}ڭ57eCem9^Lӥ)r>޺IT""&M0339!M\6t[rb/1M8CcuK%˚9.ۭΈi4̠A*1Ԛ s26~[StVSpg%T͈%cqD͎@T')VȨ 4˜K3 F3ˣKf1u [He=A\6<3h:)`Y])rLcMT31i!5噹l(Lc-8Gjiv3D4֔j2U)P8&(1SN9M\:)JZJ.#"h̉tsR !g譸E7QiB9gOF+K$G!-Af0@FcN]&.LU8h !\/F@(пb$]ilL G rҞ.3˷Tv˚4c t=o}UAL r:l k&soui9ooh4C U3 ehi| :͈~L4l+ /夾5Tj_3D;RN[љ6vƚ.Ѣ, \FԃK4%1U.rJ!.#hl~ƚ[ʹZRĠ.mT=C\vl1W4=̠6Ƞ훓ĥ*i[d\ 9OML ^fДU 6\IҒU[e#܁i2S~dHcMe.m#*ʈl&kҌ~ d0'ˈ&k> #;̨32R`0謐P[}UL q`( &F KrA;&{vLc-hFpK}=sDc$iy6jl㲖QnsHvVMcgJH#٭Of̺09nQC 6M#UƜL\6Nd 5[rƝf[{r=46FS7h5u+0 jrK3y%I )Iu0$mX8{d5Ws d4]d1Z*qM(,;ft94h0t3Uͳ͵o-:%D*um0>0 fm&9nB4nQ(~U-Ƞ`m-V1SYEr^be#F6e hS 2XF0^ ւ$ll1:RXdI āJQi s ',IKQi 70YT@cnt@SĠ%hȼ 4ʹYvi8 p1\&\Y> kZQQw-16Rf&OG 4U}r/hieN0oF@c,-KtEШF'-JdMQ th没C3͹OԌVaˆ%gݭ5Y֖5SNˆiL~R mk4seNFV3eDFQ3/K43Vf8=׾d6 ͈x&rފgиI[_2 46D#;-B]dLG 4FD2ZΚ|?iv keh{2̤),h@[D3M/0Xc.k^ ql<,Ǡ۳g͵)msoff^`0u.[H\9pXy\d-siZf2K4z.3[Fۯ xK<Ҍ`1'{kR9 21āCSu̥1A.m?m8tƆCi>0ۡȑA3TJhaZ\3Yyz\6՘FOQM%5ɈAS/fMSg/{7׾>L -rYvj:]t:Q`DE91'iV2Aky̥}qqL-LeCB3 hj%6*e%r֜".ke#ii%ZXUrnf$si4cӘ<&#TYrk'b0'˜\&.#rhUVJ4}TYYL@ R8l .N`.k*5qYvlDgm:54̠T3ôdVQ*ЦF+}79nbxƜL\6Lccgi(h!vj4F/hT[]T53414}u :JZh 4af`.k.:G.@ͤhb0T6dQ2h1MصAsb9KoC ֜e6sVzS r^=4(f*M 6| rZ =n-/fM3&ܠE `-A \AD.U!L ּ̥S4JxLcU4s\|skzA=ѠksecAS[kE{J[Qc.M ֔cT[4TsAs{lLcCmqH^&LKhJڵVP¹}ILcMn`sPƈFK9Tc4s-!kNd2"7\Z}ck Z3r"ڣ'OPyz2vDӬVnC?'1 }y3\nX8q2uHe4;5czr2'˜&.ce±'h}9zZJyjzs' =o}y E1uEԖlHZ;3]@c{fHcCO&9)OƈLΑƈL&ΨPĠg2(Fi ?R<}c1f]`\:wÙ#AkRĠ8hhIx+ -6d;fL nzd)mDsY.0hj`C2Z9> U9SN{Td09uyڭf=nq;wa}6w_6?Oۇۿ7WWimy7nt{sqf_oo_mc!"=E0G]fu\BJ4O`Mw 57siew7`UAS!od3MaZή-ϘJ3t͑46@ME}9hKآ,BeT11V/ʁJL d".MeER\Fes&GPG:g *A]T300 4&4*ɐFQc~褮\ \*A|49sy׹XSĠ*wDJQ g̠*YA1ӤJЈDcN*AF`1 4v=DGZsT[y7%PH·Q=/Xy˄#8n߆#Cu(ZAsDN[t FV"iK4:ivQ ilƚ\1`#iL4r4 D`Mt^)2(kr4n!ZrwCSs`ckkz9rZo5T/1PFjTeWifJ.ȹuӠZYuQ4R\]R 7uO (VLcp_@@άSZjr SjrV DoLFڢŵ蹗 pi*`\ iYx  h 2Ɔ4Z'^69 ּe-t&Tx+̡*uQo֗1DcmAV5ɮAvsT23)v *9nM9C| -g*qief![;5;n~`k&K3TEJN[Ļu@Nh$)pZ 9MT.1؈o`s'IBShiYu2s+3`DE[\;沖R\Z~an߈Ejss6ؘƜ=Gr~9cNƜK\&|u~[su&ܹ@# ƜuF'gD p ٨ghu:)A1ZNJd0).qX^\TeML>Q'5Ҙĵ|̉LUV T:M99"1G2N9M\Tl&lDc-.praI]hȷ 4;Aq00mB9nQA4TF.̤թ()arfE4F/XSKU e9ETS1"aQ <<9拜IaҔMg[ 6`1b\`P 3f ?ˆ4KS[Tz Ѩ ^b(x ͆ޛ9B*"xuTr@\"rBHPȨˉx5ᙔ<ah-+/p)ԌsV+7ǡ քnL< rbf8TsV/pЙ&hk2si-"G+T[g$&.k!n9Z66 98\u%g̠T|4)gA @M[Te./ 4ւˆ[1ck/sوYc!E4s훓eS\fDP DQ"v:^S.ik2'KUm`nFU& ixpF0hz -\rBoh 2A.kzeC()Xc.(9n7j< fV.phެ}7a-ヹg`.--DH^<<#͚xF&Bozys6N|+֒sUj6^{briĵZEd$)ilUw$XӉk̐9k1n% 4`bPc ř; !F zМ={\ MrZM:KhI %9n}i4K5> wrkc儻1h \n_[eM[ f.cE3^[!g̭/QPÝɝ[_WR9 ˚[Vg.wFJΝ3k`k_Q/ъFS>7f15\s:j54{ = rb@"40$h=o L AsM۷2&EQSp)ZN 8]<ڷ즸=4&*J/ӨJ^4JU`NeCIQ-2*Ġ7E9)oeţ(r'ȠtFfؾ2 ilLc#hլkrԬ[{7lD2(_YKrZ]ಱ&iZo3Iv{7G\@h˩u+3Kc3is1/ee.YhDcN #R~F"r ,Vp*pt),ϔSRf÷4t+!gig˜&.kze츊@~Ի9s͵oDZuZme469NY!wEgl;2'3m\6ƈ`F+Pδ|ge˻Ѡ%&g%Q7נ/ߩјeNb9gږ siKsֶ[4Όc*hdh 4ہ̠آ]v̼uM-cFJ1vw=fvq73bФ1\66Ɔ ph0 XshhY~k,Ծ|>1FZ5ůmA3Tu_bP}[dP kC^`&r[ݍc&< L#'(`BmiC'W!*U+d0.`6AMZ]Q^g伻5g5q[m޵ j$,SO \njΤ)#R Z2x 没Q稹ŭK̥\9䜸bL-;G(Pƚ\6vJL B/5\:\KζS3ʆS$Gd\(rZ.1Dc-4[{9e*n߆+:SS4ׂlH"As'͵)mA?ˆe"E 3 L4FRƠkt E2RĠ QE JpQ fv3蜭6= MsÏ4E F im1pVzSтѪhl! pM eB%jwZs)O(QT+QV%8r~ 9#o7B k6a@5FV/pi1a7ڈ˚˘X0Sy믧Dce\>SO&Ugԓ+0Ԍs\֤sfBH9~kڠr> Zܾ˘T,z̼lFpD楜}FcD"LF:nFY`l|05ݗlWhu9zkԾ9}l#xLcpˎ4Fy@VDEl9OqjhJj ̥,hR˶c.HmLVΓVDcx][ex]2'˚\-f$iT5Zص5roh%0׹hrohIiN{fsRHe_QeNRJ$ؾ]"1mdHq6/XM\6iR0@vXP_ :sT6ɜM4F4aFj ̡F rZ hMwk1&gn:0>3hh w sPzFТ)'5R6]ƚsFcF3ϡE\v]]A=V5KUڥj;f֢Z')7$AFΦ[u z o`Ku\^"d-Pw0V x /|:\<KUfή1gtr#w\r&h<Î=3&L Bͫx1h\)wO%;csNt znYag"Puƚ\ r6݊Js ,$jt4:\hT[{&t cHg4dVObphؖ4 qi:"R4%u\03Ia48]XɺR4jʈL4F3јemg/m˚䲡@3*:4MF$;G!-=u,ʑˆgk p2'RB")ږ`V-4$*:Njj8Eeh)fc2\6Lc46iL3NƤ-k"a-6VH ߷␣7A[d]e^3f3hnh*u [`MF1h:[& xMhlxrVP ԯպs\89|+[f&{K@ 3YzkK; )PVyGM[]1ʹ_AE/P47qinS)zk1B"x cEPVɉykajZnsP~ƶL:K zÉiĥ*AN[x&.MDm#{%Z3rf/ Jr0hJ[bSVf8a} %qhhN w4:w[aKt;$iq&ju46ƈ&sn\:>9**9HgQqF*F" r&.k*A.MC"ô4^[1*C.mi6q# _9WrYӜKN[HcGf-4+T$U4ˉsokچ]$:NL4˜zM\ř:{zI#4#b4cF;m9M3hh(hΞL7׾ _35h'˹smT|>9Noˆoil6j4;5k %4 ŝq4w5ֱ Fb$Ґh'ɧi9z6IO=3TUm9x0!vM(\hVW.x܃0a |=fkPn)CoK> lFQ" E I֌3% sWc8ڂÇ'4ϬP_<{W8bаnZHp\O>]F40XW(|#$"yvjZ(-=} {b@&H**w8iE9{m}pXm=aU֣Ns |SDHuTOFa02Eo8&\lU  QE*&h-]K2ވصtT,o|gZR!gLa` vٵ#\88\Bö桍bm.]2+iN`хp6iu5 GH3*e_Xseպ[΃2 eKk0R7XeW83thűAh,;1j16XaVh\ln՜\!4\J_M5"mNNH0;pzw~^ݡ'H鄊Уq?BÑdpő) [.˖E;r\ȜI\s=2\scl=٫#Ȫmpmd#:֦q? nѣd<: (to )pj (FΛ.&Ȁ9;֍\䬼PqB.HUMP;3ǀΈ'\$Иcɺ ϶p4}c]И`T#'M.r-0X 7a!(9sh3*Ⴘr2(4W,^Բ)oz̞P*{#\wĜgn0d#l iP. 0f#mNLCsev0vO  сm p-\ټV5,4Ǖ OZ.Ge/-Lva ۅ˲Q鞽#OGT0gi)\tQ5!ySV IBu;&f0/Qe>\ڐB֏.~DL`X!4v E3X&4lԄ-.u ݭ73Uh,aa 읻ƮOACKguߝm/$9NhBrlH$8RV7M$RY%p鮺R!I ]\l=,_uT0cP!v;;.ߠwStA,G nqQJC+]w^)ԕ.RэyS<-+|r;aЭwӿ5$HpFFɾA;ڕ..,L57Ao *,* I\.4tqpqeZXD2H{\M \_e5nΛj, ›p[e ]~bwhl чZ4`l< FulۮrNkm^fػ{98*[A,o`tPh(w>ٞл=9;7R%'g=H,O lq#bU. odFs.;g5JlU.ίpF`ىأvнwӿ]Ȉ-w4#. t\ΐEn:n2Gˈ+ E^M@4E+hQrgFc9%' Sr-ZΌcа%-le GBh#n;t%;?n$А[UW7M,[#oCbaNwM [wXlPB ⦿w-L [,\cLܡt\U4[GJ;ߡMvS ..C쒕BP-<{&w_ҫh3`9c( MAz-P ecy2ȝ*4ӄNͫ\֫5z@ x\h H\lR Bc뷕pfE\I' +ý+@Bm.˝*mb=|ߩBc@nh`f#'M*LWh,c퀺Ͻ{g[< yzSݛ-\$.IYwkͅPM 1ܣZ O sLAl [ empqzaCn|^;ߎϑ&i~Gf}/VGۏSB/JO7AQil恼Nv>JM(lnBjVs&߈=X+Ͷ{p෢.?0e2 k|8ln̿ϫ }^.J@4Dqh;Z@t'a` %hFruO( ސXT ;%:":āVG")D A*@^!A5!2WGDZIH"mAPsFZ!IH"$ VH8jBeVGVGRd#cd " $&*HUd1IS$& H,󑉵 dYUAbͲBҊM @V!M@V!M@ҖVlHdD*  I @^!&3  C1pUGpqpPv2"2⑬:҆V#`8=38MD({vVvъu U *!:t^2hGK[x`wв-kၝCGۼ@Z@;t6$NG:Z @V >)K*"g-@UCMo 12Z@zG^aU=kiCiWؒ4 |x3+ m!+hgZ{zASGKzX{ɠ'/*H+] FH+l h [ZCz h:9!VMajEg/ǧ &Z#6 x+|-B=G+?"Zb6s\T9>ւ[^߹Ҫv#i'j;'[|uW@RHpGt1=IEH9qbq~Ǻ*aGj8>Chw^id$xFd5'߂UD %x.]Uj<@:G%UY=畏W= "zU sK)ꪾs Ҫlݑw^Y+;a*^Zyd]yd= =UOwd٠'X97%w |Gd=<1U9lDTCG>y%ףc?sz7Bj^,'O;qo5_/>,{Óヿm/prpH UP^V7|+@~+oWi $V?|DAs"Q bRG|:+ґŞUG<"Lw:}UiAl Tc<_1l 2YyyY×WV>Z~̇0̇WC3ъIuS `ᣭA`Oas?#X;Y"Q{X]TV'|DzE|HT'<k|R}L|+QAj|'4'uXl ={ nmѣ2bY+~Gۼ*̇6%w|ǀɠ)+6IVH8 8 V\ ʫ).qz{x*@;$C^2hYVO53 C>π6!3ȏJ6I P Dd74;c[M|U}s2f vcǫW9"y*G*U9 I$dxyI`vӈ H۴J.)w5e H:\HvgG*AHk;w b 6\Hk;w[y$-B*l]tjsĕ :s@S3B jCT-XX Ȫ![G|c 1tNb;S}D|sQDZÞ#+mQDZ+THYajHc8!?e;r~EvEkZ5!W62Z#=du !!!cdeĀF^Yu# s|+Qu# # =e=!*r'Q<2Zxd-72*Z U 9Fgw|!|vⱗl+k\dChlGw1U0u-lώ(^>ڹ mR@V߸K|M*cmR )mUaGk> ,<.|5GB9n0yAéq;,<.uz> DX'}T>|KSqE{R-NSGdoq778NZxl?T8 k+!a=^iDiDi9Zwi9?S5sS}Xf.eB 3%k).`xj^2mܗ{ݠMyamS^XVh^XC! UC,( [9Zx=Ҝ{z[lToе入|2ѕ(6rXn~v,/ l=~^ͽO.־⷇'Ƿkl~g}RMrTju1C4t5"3|w󟬮}|旫8> '׋w;YW+I!{y:)gsc=]dױz;YlIwge)(oDheͥ(t|#F G.^c$c 7_TM=!c=$="~vl X@5_T5s `y9| Dǝb #5g><g@i0˱Z3`yYE䔬V X@VQ>ġ<"헜u+ -#>-3ٙt&i ,|Xlߔcce|tƾi6HM: ~LF245f0|G`5噈V#qtF#\c4xFl'4X5FC3|}H;[y(qwVӖrgʿ|}/^ VW.34Ad}4Wm1vpercona-xtradb-cluster-galera/tests/test_drupal/loaddb.sh0000755000000000000000000000145212247075736024130 0ustar rootroot00000000000000#!/bin/sh TEST_HOME=$(cd $(dirname $0); pwd -P) MYSQL_HOST=${MYSQL_HOST:-"127.0.0.1"} MYSQL_PORT=${MYSQL_PORT:-"3306"} MYSQL_USER=${MYSQL_USER:-"root"} MYSQL_PSWD=${MYSQL_PSWD:-"rootpass"} MYSQL_CMD="mysql -u$MYSQL_USER -p$MYSQL_PSWD -h$MYSQL_HOST -P$MYSQL_PORT " set -e #Prepare drupaldb database $MYSQL_CMD -e \ "GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES ON drupaldb.* TO 'drupal'@'%' IDENTIFIED BY 'password'" zcat $TEST_HOME/drupaldb.sql.gz | $MYSQL_CMD # Prepare drupal6 database $MYSQL_CMD -e \ "GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES ON drupal6.* TO 'drupal'@'%' IDENTIFIED BY 'password'" zcat $TEST_HOME/drupal6.sql.gz | $MYSQL_CMD $MYSQL_CMD -e "FLUSH PRIVILEGES" percona-xtradb-cluster-galera/tests/test_drupal/run.sh0000755000000000000000000000734612247075736023517 0ustar rootroot00000000000000#!/bin/bash # # DESCRIPTION: # # This scripts installs drupal site and runs drupal test suite against # installed site. Drupal package will be fetched automatically if it is # not found from working directory. # # SETUP: # # Simplest way to get test running is to create symbolic link # /var/www/drupal_test which will point to ./workdir under this directory. # # PARAMETERS: # # CONCURRENCY - number of test cases run concurrently (default 1) # WORKDIR - directory where drupal site is extracted and installed, # should reside under www root so that site can be accessed # via web server (default ./workdir) # DRUPAL_VERSION - drupal version to be tested # URL - url pointing to installed site, should be specified only # if default does not work # TESTS - drupal test suites to be run or --all to run all tests # VERBOSE - to run test suite in verbose mode set this to --verbose # # NOTES: # # WARNINGS: # * Drops database drupal before site is installed # set -e BASE_DIR=$(cd $(dirname $0); pwd -P) # Parameters CONCURRENCY=${CONCURRENCY:-"1"} WORKDIR=${WORKDIR:-"$BASE_DIR/workdir"} DRUPAL_VERSION=${DRUPAL_VERSION:-"7.12"} TESTS=${TESTS:-"--all"} VERBOSE=${VERBOSE:-""} # Drupal pkg/dirname DRUPAL_PKG="drupal-${DRUPAL_VERSION}.tar.gz" DRUPAL_DIR="$(basename $DRUPAL_PKG .tar.gz)" # Construct url (note, should be done after setting DRUPAL_DIR # to get it right) URL=${URL:-"http://localhost/drupal_test/$DRUPAL_DIR"} PHP=$(which php) DRUSH=$(which drush) if ! test -x $DRUSH then echo "Program 'drush' is required to run this test" exit 1 fi if ! test -x $PHP then echo "PHP is required to run this test" exit 1 fi # Helpers to manage the cluster declare -r DIST_BASE=$(cd $(dirname $0)/..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" # MySQL management MYSQL="mysql -u${DBMS_ROOT_USER} -p${DBMS_ROOT_PSWD} -h${NODE_INCOMING_HOST[0]} -P${NODE_INCOMING_PORT[0]}" # Create working directory if it does not exist yet if ! test -d $WORKDIR then mkdir -p $WORKDIR fi cd $WORKDIR # Download and extract drupal package if ! test -f $DRUPAL_PKG then echo "-- Downloading drupal package" wget http://ftp.drupal.org/files/projects/${DRUPAL_PKG} fi echo "-- Extracting drupal package" tar zxf $DRUPAL_PKG cd $DRUPAL_DIR # Relax permissions for installer chmod -R a+rw sites/default cp sites/default/default.settings.php sites/default/settings.php chmod a+rw sites/default/settings.php # Restart cluster echo "-- Restarting cluster" $SCRIPTS/command.sh restart # Drop drupal database, set globals $MYSQL -e "DROP DATABASE IF EXISTS drupal; SET GLOBAL auto_increment_increment=1; SET GLOBAL wsrep_drupal_282555_workaround=0;" # Install site echo "-- Installing drupal" $DRUSH si \ --db-url="mysql://${DBMS_TEST_USER}:${DBMS_TEST_PSWD}@${NODE_INCOMING_HOST[0]}:${NODE_INCOMING_PORT[0]}/drupal" \ --account-name=drupal --account-pass=drupass --clean-url=0 -y # Relax permissions so that dir can be cleaned up or overwritten # automatically. chmod -R a+rw . # Append base_url in settings.php echo "\$base_url = '"$URL"';" >> sites/default/settings.php # Enable modules MODULES=$(ls -1 modules | grep -v README.txt) $DRUSH en $MODULES -y # Run tests cd scripts echo "-- Running tests $TESTS" $PHP run-tests.sh --concurrency $CONCURRENCY --php $PHP \ --url $URL $VERBOSE $TESTS >& $WORKDIR/drupal-tests.log tests_failed=$(grep -v '0 fails' $WORKDIR/drupal-tests.log | grep 'passes' | wc -l) if test $tests_failed != 0 then echo "Some tests failed:" grep -v '0 fails' $WORKDIR/drupal-tests.log | grep 'passes' exit 1 else echo "Success" exit 0 fi percona-xtradb-cluster-galera/tests/test_insert/create_big.sql0000644000000000000000000000020312247075736025157 0ustar rootroot00000000000000CREATE TABLE test_insert ( i INT AUTO_INCREMENT PRIMARY KEY, f FLOAT, d DATE, t TIME, c CHAR(32), v VARCHAR(1024) ) ENGINE=InnoDB; percona-xtradb-cluster-galera/tests/test_insert/create_small.sql0000644000000000000000000000020212247075736025525 0ustar rootroot00000000000000CREATE TABLE test_insert ( i INT AUTO_INCREMENT PRIMARY KEY, f FLOAT, d DATE, t TIME, c CHAR(8), v VARCHAR(1024) ) ENGINE=InnoDB; percona-xtradb-cluster-galera/tests/test_insert/insert_big.sql0000644000000000000000000000226712247075736025234 0ustar rootroot00000000000000INSERT INTO test_insert VALUES ( DEFAULT, RAND(), CURRENT_DATE, CURRENT_TIME, 'pokemon forever', 'opwkmefovcm weofvowd fowehvfe wehg ryu ki67 w2 fe4t5h ytryu76j tkutykuj thret h4t5 hjy5tyu6j rt hyj5t jty er w cda swc2trt45yu 65i 76ujt yhjn rhjr tj6yu7kjyt jt regt h4 hjytykiy ukhr4 hthtrjtyu7 tyu jty jtyjutykj 6tyu jt hr egge rgte tr5h rthyj ry5 kt dferht rt hjyurt hregftdefgwfrovppoodmpfpp owenj vfweovwjneokmemfdvcwoem wefvowkej 0o=0943i8t5 34fw=eri=we if jfowem -jfwef =ojk frwe=fok wedfwoerj fwde f23 c2dw xr5ju76 p0- 0= 7u kiki8 rtg erf wefd qw fdehtr j k iuk o .lp 4 t5gvwed45w ty65 7u 987l9oimr tg wef d qews xq d34 t5hg 65kji 87j 5t hg erw f 3wefjfdwpewofvdsf ofvkdspfpvsmvsd wefverfbvegfrbqwertyuiopsadfghjklzxcvbnm,.qwertyuiklopasdfghjkl > $debug_log echo "-- Running test $test" $MYSQL -e "DROP DATABASE IF EXISTS test; CREATE DATABASE test; SET GLOBAL auto_increment_increment=1; SET GLOBAL wsrep_drupal_282555_workaround=0; UPDATE mysql.user SET Password='' where User='root'; FLUSH PRIVILEGES;" ./mysql-test-run \ --extern socket=$SOCKET \ --extern host=${NODE_INCOMING_HOST[0]} \ --extern port=${NODE_INCOMING_PORT[0]} \ --extern user=root \ $test result=$? # attempt to reset password back to original mysql -h${NODE_INCOMING_HOST[0]} -P${NODE_INCOMING_PORT[0]} -uroot \ -e "SET wsrep_on=0; UPDATE mysql.user set Password=PASSWORD('"${DBMS_ROOT_PSWD}"') where User='root'; FLUSH PRIVILEGES;" if test $result != 0 then echo "Test $test failed" exit 1 fi # Iterate over nodes and check that cluster is up and running node_cnt=0 for node in $NODE_LIST do node_cnt=$(($node_cnt + 1)) done for node in $NODE_LIST do cs=$($SCRIPTS/command.sh cluster_status $node) if test $cs != "Primary:$node_cnt" then echo "Invalid cluster status for $node: $cs" echo "Test failed" exit 1 fi done $SCRIPTS/command.sh check if test $? != 0 then echo "Consistency check failed" exit 1 fi done percona-xtradb-cluster-galera/tests/test_overhead/run.sh0000755000000000000000000000476712247075736024031 0ustar rootroot00000000000000#!/bin/bash -eu # This test measures master replication overhead by running a number of # autocommit queries with wsrep_on set to 0 and 1. # # NOTES: # - The load was deliberately chosen to produce maximum replication overhead. # - SQL commands are first dumped into a text file in order to minimize client # overhead when benchmarking. declare -r DIST_BASE=$(cd $(dirname $0)/..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh stop start_node -g gcomm:// --mysql-opt --wsrep-new-cluster 0 declare -r SQL="$(dirname $0)/tmp.sql" declare -r TABLE="overhead" declare -r TABLE_DEFINITION="(pk INT PRIMARY KEY, u VARCHAR(64))" declare -r MAX_ROWS=50000 insert_load() { local WSREP_ON=$1 echo "USE $DBMS_TEST_SCHEMA; SET GLOBAL wsrep_on=$WSREP_ON; " # echo "BEGIN; " for i in $(seq 1 $MAX_ROWS) do echo "INSERT INTO $TABLE VALUES ($i, uuid()); " done # echo "COMMIT;" } update_load() { local WSREP_ON=$1 echo "USE $DBMS_TEST_SCHEMA; SET GLOBAL wsrep_on=$WSREP_ON; " # echo "BEGIN; " for i in $(seq 1 $MAX_ROWS) do local PK=$(( $RANDOM * $RANDOM % $MAX_ROWS)) echo "UPDATE $TABLE SET u = uuid() WHERE pk = $PK; " done # echo "COMMIT;" } MYSQL="mysql -u$DBMS_TEST_USER -p$DBMS_TEST_PSWD" MYSQL="$MYSQL -h${NODE_INCOMING_HOST[0]} -P${NODE_INCOMING_PORT[0]} -B" warm_up() { echo -n "Warming up: " $MYSQL -e "DROP TABLE IF EXISTS $DBMS_TEST_SCHEMA.$TABLE" $MYSQL -e "CREATE TABLE $DBMS_TEST_SCHEMA.$TABLE $TABLE_DEFINITION" insert_load 1 > $SQL # generate sql commands /usr/bin/time -o /dev/stdout -f '%e' $MYSQL < $SQL } warm_up declare -a INSERTS declare -a UPDATES for wsrep in 0 1 do $MYSQL -e "DROP TABLE IF EXISTS $DBMS_TEST_SCHEMA.$TABLE" $MYSQL -e "CREATE TABLE $DBMS_TEST_SCHEMA.$TABLE $TABLE_DEFINITION" echo -n "wsrep_on = $wsrep : " insert_load $wsrep > $SQL # generate sql commands TIMING=$(/usr/bin/time -o /dev/stdout -f '%e' $MYSQL < $SQL) echo -n "${TIMING} / " INSERTS[$wsrep]=$(echo -n ${TIMING} | sed s/\\.//) update_load $wsrep > $SQL # generate sql commands TIMING=$(/usr/bin/time -o /dev/stdout -f '%e' $MYSQL < $SQL) echo "${TIMING}" UPDATES[$wsrep]=$(echo -n ${TIMING} | sed s/\\.//) done INSERT_OVERHEAD=$(( ${INSERTS[1]} * 100 / ${INSERTS[0]} - 100)) UPDATE_OVERHEAD=$(( ${UPDATES[1]} * 100 / ${UPDATES[0]} - 100)) echo "Overhead: $INSERT_OVERHEAD% / $UPDATE_OVERHEAD%" percona-xtradb-cluster-galera/tests/test_seesaw/README0000644000000000000000000000130612247075736023222 0ustar rootroot00000000000000The test runs sqlgen load against the cluster and in round robin order kills and restarts nodes. After each transition consistency check is done. This facilitates testing configration changes under highly concurrent load with lots of certification conflicts. Sometimes the first consistency check fails, perhaps due to some race condition. In that case a second consistency check is attempted and if that fails the script exits with error code. Usage with gcomm backend: ./run.sh [N] Usage with vsbes backend: GCS_TYPE=vsbes VSBES_ADDRESS=192.168.0.1:5678 ./run.sh [N] where N is the number of off/on transitions. I.e. N=1 means that 1 node will be killed and restarted. If N is omitted it goes forever. percona-xtradb-cluster-galera/tests/test_seesaw/run.sh0000755000000000000000000000500512247075736023505 0ustar rootroot00000000000000#!/bin/bash -eu declare -r DIST_BASE=$(cd $(dirname $0)/..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/kill.sh . $SCRIPTS/misc.sh TRIES=${1:-"-1"} # -1 stands for indefinite loop KILL_RATE=${KILL_RATE:-"3"} SST_RATE=${SST_RATE:-"2"} #restart # cluster restart should be triggered by user # Start load SQLGEN=${SQLGEN:-"$DIST_BASE/bin/sqlgen"} LD_PRELOAD=$GLB_PRELOAD \ DYLD_INSERT_LIBRARIES=$GLB_PRELOAD \ DYLD_FORCE_FLAT_NAMESPACE=1 \ $SQLGEN --user $DBMS_TEST_USER --pswd $DBMS_TEST_PSWD --host $DBMS_HOST \ --port $DBMS_PORT --users $DBMS_CLIENTS --duration 999999999 \ --stat-interval 99999999 --sess-min 999999 --sess-max 999999 \ --rollbacks 0.1 \ >/dev/null 2>$BASE_RUN/seesaw.err & declare -r sqlgen_pid=$! disown # forget about the job, disable waiting for it term=0 terminate() { echo "Terminating" term=1 } trap terminate SIGINT SIGTERM SIGHUP SIGPIPE trap "kill $sqlgen_pid" EXIT pause 5 5 consistency_check $sqlgen_pid # kills a node and restarts it after a while cycle() { local -r node=$1 local -r node_id=${NODE_ID[$node]} local pause_var=10 local var_kill=$(( $RANDOM % $KILL_RATE )) if [ $var_kill -eq 0 ] then echo "Killing node $node_id..." kill_node $node pause_var=$((($RANDOM % 8) + 1)) else echo "Stopping node $node_id..." stop_node $node fi pause 0 $pause_var if test $pause_var -gt 5 then consistency_check $sqlgen_pid else echo "skipped consistency check due to fast recycle" fi echo "Restarting node $node_id..." local skip_recovery_opt="" if [ $var_kill -eq 0 ] && [ $(($RANDOM % $SST_RATE)) -eq 0 ] then echo "Enforcing SST" skip_recovery_opt="--skip-recovery --start-position '00000000-0000-0000-0000-000000000000:-2'" fi if test $pause_var -gt 3 then restart_node "-g $(gcs_address $node) $skip_recovery_opt" $node else stop_node $node || : # just in case the process is still lingering restart_node "-g $(gcs_address $node) $skip_recovery_opt" $node fi } node=0 node_num=$(( $NODE_MAX + 1 )) try=0 echo "### Looping over $node_num nodes ###" while [ $try -ne $TRIES ] do ! let try++ # ! - to protect from -e echo -e "\nTry ${try}/${TRIES}\n" cycle $node pause consistency_check $sqlgen_pid node=$(( ( node + 1 ) % node_num )) done exit percona-xtradb-cluster-galera/tests/test_sqlgen/run.sh0000644000000000000000000000161612247075736023510 0ustar rootroot00000000000000#!/bin/sh -eu BASE_DIR=$(cd $(dirname $0); pwd -P) REPORT_DIR="$TEST_REPORT_DIR/sqlgen" if ! test -d $REPORT_DIR then mkdir $REPORT_DIR if test $? != 0 then echo "Failed to create report directory" exit 1 fi fi SQLGEN_LOG=$REPORT_DIR/sqlgen.log echo "Running sqlgen test, args: $@" >> $SQLGEN_LOG . $TEST_BASE_DIR/tap/tap-functions plan_tests 1 if test $CLUSTER_N_NODES -lt 1 then skip "ok" "No nodes available, skipping test" elif ! test -x $BASE_DIR/sqlgen then skip "ok" "sqlgen binary not found, skipping test" else echo "Starting load" >> $SQLGEN_LOG args="" for ii in $CLUSTER_NODES do args="$args --host $ii" done args="$args --port $MYSQL_PORT $@" LD_PRELOAD=$GLB_PRELOAD \ DYLD_INSERT_LIBRARIES=$GLB_PRELOAD \ DYLD_FORCE_FLAT_NAMESPACE=1 \ $BASE_DIR/sqlgen $args >> $SQLGEN_LOG 2>&1 ok $? "sqlgen" fi percona-xtradb-cluster-galera/tests/test_startstop/run.sh0000755000000000000000000000072212247075736024262 0ustar rootroot00000000000000#!/bin/bash -eu declare -r DIST_BASE=$(cd $(dirname $0)/..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/signal.sh . $SCRIPTS/misc.sh TRIES=${1:-"-1"} # -1 stands for indefinite loop stop try=0 while [ $try -ne $TRIES ] do ! let try++ # ! - to protect from -e echo -e "\nTry ${try}/${TRIES}\n" start pause 5 stop done percona-xtradb-cluster-galera/tests/test_stopcont/README0000644000000000000000000000071312247075736023605 0ustar rootroot00000000000000The test runs sqlgen load against the cluster and in round robin order makes nodes stop and continue. After each transition consistency check is done. This facilitates testing crawling nodes. Usage with gcomm backend: ./run.sh [N] Usage with vsbes backend: GCS_TYPE=vsbes VSBES_ADDRESS=192.168.0.1:5678 ./run.sh [N] where N is the number of stop/cont transitions. I.e. N=1 means that 1 node will be killed and restarted. If N is omitted it goes forever. percona-xtradb-cluster-galera/tests/test_stopcont/run.sh0000755000000000000000000000337212247075736024074 0ustar rootroot00000000000000#!/bin/bash -eu declare -r DIST_BASE=$(cd $(dirname $0)/..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/signal.sh . $SCRIPTS/misc.sh TRIES=${1:-"-1"} # -1 stands for indefinite loop MIN_PAUSE=${2:-"3"} PAUSE_RND=${3:-"20"} #restart # cluster restart should be triggered by user # Start load SQLGEN=${SQLGEN:-"$DIST_BASE/bin/sqlgen"} LD_PRELOAD=$GLB_PRELOAD \ DYLD_INSERT_LIBRARIES=$GLB_PRELOAD \ DYLD_FORCE_FLAT_NAMESPACE=1 \ $SQLGEN --user $DBMS_TEST_USER --pswd $DBMS_TEST_PSWD --host $DBMS_HOST \ --port $DBMS_PORT --users $DBMS_CLIENTS --duration 999999999 \ --stat-interval 99999999 >/dev/null 2>$BASE_RUN/seesaw.err & declare -r sqlgen_pid=$! disown # forget about the job, disable waiting for it term=0 terminate() { echo "Terminating" term=1 } trap terminate SIGINT SIGTERM SIGHUP SIGPIPE trap "kill $sqlgen_pid" EXIT pause 10 10 consistency_check $sqlgen_pid # kills a node and restarts it after a while cycle() { local -r node=$1 local -r node_id=${NODE_ID[$node]} echo "Signaling node $node_id with STOP... " signal_node STOP $node pause $MIN_PAUSE $PAUSE_RND echo "Signaling node $node_id with CONT..." signal_node CONT $node sleep 10 # this pause needed to get node to notice that it lost the PC # if it did... wait_node_state $node 4 # 4 - SYNCED } node=0 node_num=$(( $NODE_MAX + 1 )) try=0 echo "### Looping over $node_num nodes ###" while [ $try -ne $TRIES ] do ! let try++ # ! - to protect from -e echo -e "\nTry ${try}/${TRIES}\n" cycle $node consistency_check $sqlgen_pid pause 2 node=$(( ( node + 1 ) % node_num )) done exit percona-xtradb-cluster-galera/tests/test_upgrade/README0000644000000000000000000000040612247075736023362 0ustar rootroot00000000000000The test - installs demo package pointed to by OLD, - starts sqlgen load, - checks consistency, - upgrades the nodes one by one to distribution pointed to by NEW, - checks consistency Usage: OLD=.tgz NEW=.tgz ./run.sh Usage with vsbes backend: percona-xtradb-cluster-galera/tests/test_upgrade/run.sh0000755000000000000000000000344312247075736023651 0ustar rootroot00000000000000#!/bin/bash -eu if [ $# -ne 2 ] then echo "Usage: $0 .tgz .tgz" >&2 exit 1 else OLD=$1 NEW=$2 echo "Upgrading $OLD to $NEW" fi declare -r DIST_BASE=$(cd $(dirname $0)/..; pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/kill.sh . $SCRIPTS/remove.sh . $SCRIPTS/install.sh . $SCRIPTS/misc.sh # Remove previous install kill_all remove # Install old version install $OLD start # Start load SQLGEN=${SQLGEN:-"$DIST_BASE/bin/sqlgen"} LD_PRELOAD=$GLB_PRELOAD \ DYLD_INSERT_LIBRARIES=$GLB_PRELOAD \ DYLD_FORCE_FLAT_NAMESPACE=1 \ $SQLGEN --user $DBMS_TEST_USER --pswd $DBMS_TEST_PSWD --host $DBMS_HOST \ --port $DBMS_PORT --users $DBMS_CLIENTS --duration 999999999 \ --stat-interval 99999999 --sess-min 999999 --sess-max 999999 \ --rollbacks 0.1 --ac-frac 20 --long-keys \ >/dev/null 2>$BASE_RUN/seesaw.err & declare -r sqlgen_pid=$! disown # forget about the job, disable waiting for it term=0 terminate() { echo "Terminating" term=1 } trap terminate SIGINT SIGTERM SIGHUP SIGPIPE trap "kill $sqlgen_pid" EXIT pause 5 1 consistency_check $sqlgen_pid # Stops a node, installs new version and restarts upgrade() { local -r node=$1 local -r node_id=${NODE_ID[$node]} echo "Upgrading node $node_id..." stop_node $node install_node $NEW $node echo "Restarting node $node_id..." # if test $pause_var -gt 3 # then start_node "-g $(gcs_address $node)" $node # else # stop_node $node || : # just in case the process is still lingering # start_node "-g $(gcs_address $node)" $node # fi } for node in $NODE_LIST do upgrade $node pause done consistency_check $sqlgen_pid exit