├── .editorconfig ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── examples └── basic.cr ├── shard.yml ├── spec ├── iemon_spec.cr ├── objects.cr ├── spec_helper.cr └── units │ ├── object.cr │ ├── primitives.cr │ └── property.cr ├── src ├── exceptions.cr ├── exceptions │ └── exceptions.cr ├── iemon.cr ├── lib_shm.cr ├── object.cr ├── object │ └── object.cr ├── primitives.cr ├── primitives │ └── primitives.cr ├── property.cr └── property │ └── property.cr └── tools └── clean.cr /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.cr] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 2 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /docs/ 2 | /lib/ 3 | /bin/ 4 | /.shards/ 5 | *.dwarf 6 | 7 | # Libraries don't need dependency lock 8 | # Dependencies will be locked in application that uses them 9 | /shard.lock 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: crystal 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 taicsuzu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Iemon (伊右衛門) 3 |

4 | 5 |

6 | 7 |

8 | 9 |

10 | Awesome shared object in multiple processes for Crystal 11 |

12 | 13 | ## Abstract 14 | 15 | :star: **This will work!** :star: 16 | 17 | ```crystal 18 | my_object = MyObj.new(x: 1) 19 | 20 | fork do 21 | my_object.x = 2 22 | end 23 | 24 | sleep 0.1 25 | 26 | puts my_object.x 27 | #=> 2 28 | ``` 29 | 30 | ## Installation 31 | 32 | Add this to your application's `shard.yml`: 33 | 34 | ```yaml 35 | dependencies: 36 | iemon: 37 | github: tbrand/iemon 38 | ``` 39 | 40 | ## Usage 41 | 42 | ```crystal 43 | require "iemon" 44 | ``` 45 | 46 | Define your object which inherits `Iemon::Object`. 47 | 48 | Iemon manages shared properties. You can define it by using `assigns` method. 49 | ```crystal 50 | class MyObj < Iemon::Object 51 | assigns(x: Int32) 52 | end 53 | ``` 54 | 55 | Then `x` of `MyObj` will be shared between multiple processes. 56 | 57 | So below code will work correctly. 58 | ```crystal 59 | my_obj = MyObj.new(x: 1) 60 | 61 | fork do 62 | my_obj.x = 2 63 | end 64 | 65 | sleep 0.1 # wait a little for a forked process 66 | 67 | puts my_obj.x 68 | #=> 2 69 | ``` 70 | 71 | Call `Iemon::Object#clean` once if you don't need to share the properties of the object anymore. 72 | 73 | Otherwise the shared memory remains even if the execution is finished. 74 | ```crystal 75 | my_obj.clean 76 | ``` 77 | 78 | If you forget to do that, you can clean all of them by 79 | ``` 80 | crystal lib/iemon/tools/clean.cr 81 | ``` 82 | 83 | ## Development 84 | 85 | The project is still under the work in progress. 86 | 87 | Any contributions are welcome! :tada: 88 | 89 | ## Contributing 90 | 91 | 1. Fork it () 92 | 2. Create your feature branch (`git checkout -b my-new-feature`) 93 | 3. Commit your changes (`git commit -am 'Add some feature'`) 94 | 4. Push to the branch (`git push origin my-new-feature`) 95 | 5. Create a new Pull Request 96 | 97 | ## Contributors 98 | 99 | - [tbrand](https://github.com/tbrand) Taichiro Suzuki - creator, maintainer 100 | -------------------------------------------------------------------------------- /examples/basic.cr: -------------------------------------------------------------------------------- 1 | require "../src/iemon" 2 | 3 | class MyObj < Iemon::Object 4 | assigns(x: Int32) 5 | end 6 | 7 | my_obj = MyObj.new(x: 1) 8 | 9 | fork do 10 | my_obj.x = 2 11 | end 12 | 13 | sleep 0.1 14 | 15 | puts my_obj.x 16 | 17 | my_obj.clean 18 | -------------------------------------------------------------------------------- /shard.yml: -------------------------------------------------------------------------------- 1 | name: iemon 2 | version: 0.1.0 3 | 4 | authors: 5 | - taicsuzu 6 | 7 | crystal: 0.25.0 8 | 9 | license: MIT 10 | -------------------------------------------------------------------------------- /spec/iemon_spec.cr: -------------------------------------------------------------------------------- 1 | require "./spec_helper" 2 | require "./units/*" 3 | -------------------------------------------------------------------------------- /spec/objects.cr: -------------------------------------------------------------------------------- 1 | class X < Iemon::Object 2 | assigns(x: Int32) 3 | end 4 | 5 | class Y 6 | def clean; end 7 | end 8 | 9 | class Z < Iemon::Object 10 | assigns(x: X) 11 | end 12 | -------------------------------------------------------------------------------- /spec/spec_helper.cr: -------------------------------------------------------------------------------- 1 | require "spec" 2 | require "../src/iemon" 3 | require "./objects" 4 | -------------------------------------------------------------------------------- /spec/units/object.cr: -------------------------------------------------------------------------------- 1 | describe Iemon::Object do 2 | context "minimum object" do 3 | it "created" do 4 | x = X.new 5 | x.x = 1 6 | x.x.should eq(1) 7 | 8 | x.clean 9 | end 10 | 11 | it "created with default value" do 12 | x = X.new(x: 2) 13 | x.x.should eq(2) 14 | 15 | x.clean 16 | end 17 | 18 | it "change the value from forked process" do 19 | x = X.new(x: 3) 20 | 21 | fork do 22 | x.x = 4 23 | end 24 | 25 | sleep 0.1 26 | 27 | x.x.should eq(4) 28 | x.clean 29 | end 30 | 31 | it "change the value from master process" do 32 | x = X.new(x: 3) 33 | 34 | fork do 35 | sleep 0.1 36 | 37 | x.x.should eq(4) 38 | end 39 | 40 | x.x = 4 41 | 42 | sleep 0.2 43 | 44 | x.clean 45 | end 46 | 47 | it "change the value from forked process on another forked process" do 48 | x = X.new(x: 3) 49 | 50 | fork do 51 | x.x = 4 52 | end 53 | 54 | fork do 55 | sleep 0.1 56 | 57 | x.x.should eq(4) 58 | end 59 | 60 | sleep 0.2 61 | 62 | x.clean 63 | end 64 | end 65 | 66 | context "nested Iemon::Object" do 67 | it "created" do 68 | x = X.new(x: 1) 69 | z = Z.new(x: x) 70 | z.clean(true) 71 | end 72 | 73 | it "change the nested value from forked process" do 74 | x = X.new(x: 1) 75 | z = Z.new(x: x) 76 | 77 | fork do 78 | z.x.x = 2 79 | end 80 | 81 | sleep 0.1 82 | 83 | z.x.x.should eq(2) 84 | z.clean(true) 85 | end 86 | 87 | it "change the nested value from master process" do 88 | x = X.new(x: 1) 89 | z = Z.new(x: x) 90 | 91 | fork do 92 | sleep 0.1 93 | 94 | z.x.x.should eq(2) 95 | end 96 | 97 | z.x.x = 2 98 | 99 | sleep 0.2 100 | 101 | z.clean(true) 102 | end 103 | 104 | it "change the nested value from forked process on another forked process" do 105 | x = X.new(x: 1) 106 | z = Z.new(x: x) 107 | 108 | fork do 109 | z.x.x = 2 110 | end 111 | 112 | fork do 113 | sleep 0.1 114 | 115 | z.x.x.should eq(2) 116 | end 117 | 118 | sleep 0.2 119 | 120 | z.clean(true) 121 | end 122 | 123 | it "change the nested object from forked process" do 124 | x = X.new(x: 1) 125 | z = Z.new(x: x) 126 | 127 | fork do 128 | _x = X.new(x: 2) 129 | 130 | z.x = _x 131 | end 132 | 133 | sleep 0.1 134 | 135 | z.x.x.should eq(2) 136 | z.clean(true) 137 | end 138 | 139 | it "change the nested object from master process" do 140 | x = X.new(x: 1) 141 | z = Z.new(x: x) 142 | 143 | fork do 144 | sleep 0.1 145 | 146 | z.x.x.should eq(2) 147 | end 148 | 149 | _x = X.new(x: 2) 150 | z.x = _x 151 | 152 | sleep 0.2 153 | 154 | z.clean(true) 155 | end 156 | 157 | it "change the nested value from forked process on another forked process" do 158 | x = X.new(x: 1) 159 | z = Z.new(x: x) 160 | 161 | fork do 162 | _x = X.new(x: 2) 163 | 164 | z.x = _x 165 | end 166 | 167 | fork do 168 | sleep 0.1 169 | 170 | z.x.x.should eq(2) 171 | end 172 | 173 | sleep 0.2 174 | 175 | z.clean(true) 176 | end 177 | end 178 | end 179 | -------------------------------------------------------------------------------- /spec/units/primitives.cr: -------------------------------------------------------------------------------- 1 | macro define_primitive_class 2 | {% for prim in Iemon::Primitives::STRUCT %} 3 | class P_{{ prim.id }} < Iemon::Object 4 | assigns(prim: {{ prim.id }}) 5 | end 6 | {% end %} 7 | 8 | {% for prim in Iemon::Primitives::CLASS %} 9 | class P_{{ prim.id }} < Iemon::Object 10 | assigns(prim: {{ prim.id }}) 11 | end 12 | {% end %} 13 | end 14 | 15 | macro define_primitive_specs 16 | describe "Primitives" do 17 | {% for prim in Iemon::Primitives::STRUCT %} 18 | context "primitive: {{ prim.id }}" do 19 | it "create" do 20 | p = P_{{ prim.id }}.new 21 | p.clean 22 | end 23 | end 24 | {% end %} 25 | 26 | {% for prim in Iemon::Primitives::CLASS %} 27 | context "primitive: {{ prim.id }}" do 28 | it "create" do 29 | p = P_{{ prim.id }}.new 30 | p.clean 31 | end 32 | end 33 | {% end %} 34 | end 35 | end 36 | 37 | define_primitive_class 38 | define_primitive_specs 39 | -------------------------------------------------------------------------------- /spec/units/property.cr: -------------------------------------------------------------------------------- 1 | describe Iemon::Property do 2 | context "value" do 3 | it "set and get correctly" do 4 | prop = Iemon::Property(Int32).new 5 | prop.set_value(1) 6 | prop.value.should eq(1) 7 | prop.clean 8 | end 9 | 10 | it "set value in constructor" do 11 | prop = Iemon::Property(Int32).new(1) 12 | prop.value.should eq(1) 13 | prop.clean 14 | end 15 | 16 | it "set_value raises NotSupportedType if it's not" do 17 | prop = Iemon::Property(Y).new 18 | 19 | expect_raises(Iemon::NotSupportedType, 20 | "Y can not be assigned. "+ 21 | "Only primitive types or Iemon::Object are supported.") do 22 | prop.set_value(Y.new) 23 | end 24 | 25 | prop.clean 26 | end 27 | 28 | it "value raises NotAttached if it's not" do 29 | prop = Iemon::Property(Int32).new 30 | 31 | expect_raises(Iemon::NotAttached, "Property(Int32) is not attached") do 32 | prop.raw_value 33 | end 34 | 35 | prop.clean 36 | end 37 | 38 | it "value raises ValueNotSet if it's not" do 39 | prop = Iemon::Property(Int32).new 40 | prop.attach 41 | 42 | expect_raises(Iemon::ValueNotSet, "Value is not set for Iemon::Property(Int32)") do 43 | prop.raw_value 44 | end 45 | 46 | prop.clean 47 | end 48 | end 49 | 50 | context "attach" do 51 | it "correctly" do 52 | prop = Iemon::Property(Int32).new(1) 53 | prop.attach 54 | prop.attached?.should be_true 55 | prop.detach 56 | prop.clean 57 | end 58 | end 59 | 60 | context "detach" do 61 | it "correctly" do 62 | prop = Iemon::Property(Int32).new(1) 63 | prop.attach 64 | prop.detach 65 | prop.detached?.should be_true 66 | prop.clean 67 | end 68 | 69 | it "after set_value" do 70 | prop = Iemon::Property(Int32).new(1) 71 | prop.detached?.should be_true 72 | prop.clean 73 | end 74 | 75 | it "detached for innter Iemon::Object" do 76 | obj = X.new(x: 1) 77 | 78 | prop = Iemon::Property(X).new(obj) 79 | prop.value.__x_prop.not_nil!.detached?.should eq(true) 80 | prop.clean(true) 81 | end 82 | end 83 | 84 | context "clean" do 85 | it "correctly" do 86 | prop = Iemon::Property(Int32).new(1) 87 | prop.clean 88 | prop.cleaned?.should be_true 89 | end 90 | 91 | it "recursively" do 92 | obj = X.new(x: 1) 93 | 94 | prop = Iemon::Property(X).new(obj) 95 | prop.clean(true) 96 | 97 | obj.__x_prop.not_nil!.cleaned?.should eq(true) 98 | end 99 | 100 | it "not recursively" do 101 | obj = X.new(x: 1) 102 | 103 | prop = Iemon::Property(X).new(obj) 104 | prop.clean(false) 105 | 106 | obj.__x_prop.not_nil!.cleaned?.should eq(false) 107 | obj.__x_prop.not_nil!.clean 108 | end 109 | 110 | it "raises AlreadyCleaned if it's already cleaned" do 111 | prop = Iemon::Property(Int32).new(1) 112 | prop.clean 113 | 114 | expect_raises(Iemon::AlreadyCleaned, "failed to clean the property. "+ 115 | "(property is already cleaned)") do 116 | prop.clean 117 | end 118 | end 119 | end 120 | end 121 | -------------------------------------------------------------------------------- /src/exceptions.cr: -------------------------------------------------------------------------------- 1 | require "./exceptions/*" 2 | -------------------------------------------------------------------------------- /src/exceptions/exceptions.cr: -------------------------------------------------------------------------------- 1 | module Iemon 2 | class NotAttached < Exception; end 3 | class AlreadyCleaned < Exception; end 4 | class NotSupportedType < Exception; end 5 | class ValueNotSet < Exception; end 6 | class LibShmException < Exception; end 7 | end 8 | -------------------------------------------------------------------------------- /src/iemon.cr: -------------------------------------------------------------------------------- 1 | require "./lib_shm" 2 | require "./exceptions" 3 | require "./object" 4 | require "./primitives" 5 | require "./property" 6 | 7 | include Iemon::Primitives 8 | # 9 | # Override primitive types so that they can respond to `detach` and `clean`. 10 | # These method do nothing. 11 | # 12 | define_struct 13 | define_class 14 | -------------------------------------------------------------------------------- /src/lib_shm.cr: -------------------------------------------------------------------------------- 1 | lib LibShm 2 | IPC_PRIVATE = 0 3 | 4 | IPC_CREATE = 1000 5 | IPC_EXCL = 2000 6 | 7 | IPC_RMID = 0 8 | 9 | fun shmget(key : Int32, size : LibC::SizeT, flag : Int32) : Int32 10 | fun shmat(id : Int32, addr : UInt8*, flag : Int32) : UInt8* 11 | fun shmdt(addr : UInt8*) : Int32 12 | fun shmctl(id : Int32, cmd : Int32, shmid_ds : UInt8*) : Int32 13 | end 14 | -------------------------------------------------------------------------------- /src/object.cr: -------------------------------------------------------------------------------- 1 | require "./object/*" 2 | -------------------------------------------------------------------------------- /src/object/object.cr: -------------------------------------------------------------------------------- 1 | module Iemon 2 | class Object 3 | # 4 | # Assign properties for the object. 5 | # ``` 6 | # class You 7 | # assigns(x: Int32, y: Int32) 8 | # end 9 | # ``` 10 | # 11 | # You can access them like `property` macro. 12 | # ``` 13 | # you = You.new 14 | # you.x = 2 15 | # puts you.y 16 | # ``` 17 | # 18 | macro assigns(**props) 19 | assigns({{ props }}) 20 | end 21 | 22 | # 23 | # Assign properties for the object. 24 | # See above comments for the details. 25 | # 26 | macro assigns(props) 27 | {% for n, t in props %} 28 | assign({{ n.id }}, {{ t.id }}) 29 | {% end %} 30 | 31 | # 32 | # Iemon's constructor. 33 | # The object which inherit Iemon have to call this. 34 | # ``` 35 | # class You < Iemon 36 | # def initialize 37 | # super 38 | # end 39 | # end 40 | # ``` 41 | # 42 | def initialize 43 | {% for n, t in props %} 44 | @{{ n.id }} = Iemon::Property({{ t.id }}).new 45 | {% end %} 46 | end 47 | 48 | # 49 | # Iemon's constructor with default values. 50 | # ``` 51 | # class You < Iemon 52 | # assigns(x: Int32, y: Int32) 53 | # end 54 | # 55 | # you = You.new(x: 1, y: 2) 56 | # ``` 57 | # 58 | def initialize( 59 | {% for n, t in props %} 60 | {{ n }} : {{ t }}, 61 | {% end %} 62 | ) 63 | {% for n, t in props %} 64 | @{{ n.id }} = Iemon::Property({{ t.id }}).new({{ n }}) 65 | {% end %} 66 | end 67 | 68 | # 69 | # A method for detach shm which attached to the object. 70 | # ``` 71 | # you.detach 72 | # ``` 73 | # 74 | # Note that this method have to be called in 75 | # every objects in every processes. 76 | # ``` 77 | # you1 = You.new 78 | # you2 = You.new 79 | # 80 | # fork do 81 | # you1.x = 0 82 | # you2.x = 1 83 | # 84 | # # detach on forked process 85 | # you1.detach 86 | # you2.detach 87 | # end 88 | # 89 | # # detach on main process 90 | # you1.detach 91 | # you2.detach 92 | # ``` 93 | # 94 | def detach 95 | {% for n, t in props %} 96 | @{{ n.id }}.not_nil!.detach 97 | {% end %} 98 | end 99 | 100 | # 101 | # A method for cleaning shm 102 | # Call this method once on main process for each object. 103 | # ``` 104 | # you1 = You.new 105 | # you2 = You.new 106 | # 107 | # you1.clean 108 | # you2.clean 109 | # ``` 110 | # 111 | def clean(recursive : Bool = false) 112 | {% for n, t in props %} 113 | @{{ n.id }}.not_nil!.clean(recursive) 114 | {% end %} 115 | end 116 | 117 | def copy(other : self) 118 | {% for n, t in props %} 119 | @{{ n.id }}.not_nil!.set_value(other.{{ n.id }}) 120 | {% end %} 121 | end 122 | end 123 | 124 | # 125 | # Assign a property. 126 | # The first argument is a name of the property. 127 | # The second argument is the type. 128 | # The type have to be primitive or Iemon object. 129 | # 130 | macro assign(n, t) 131 | @{{ n }} : Iemon::Property({{ t }})? 132 | 133 | def {{ n }} : {{ t }} 134 | unless {{ n }} = @{{ n }} 135 | raise "self.{{ n }} is not initialized" 136 | end 137 | 138 | {{ n }}.value 139 | end 140 | 141 | def {{ n }}=(val : {{ t }}) : {{ t }} 142 | unless {{ n }} = @{{ n }} 143 | @{{ n }} = Iemon::Property({{ t }}).new 144 | end 145 | 146 | if val.is_a?(Iemon::Object) 147 | # 148 | # Copy every properties from val 149 | # 150 | @{{ n }}.not_nil!.value.copy(val) 151 | # 152 | # Clean the served object's properties 153 | # 154 | val.clean 155 | else 156 | @{{ n }}.not_nil!.set_value(val) 157 | end 158 | 159 | @{{ n }}.not_nil!.value 160 | end 161 | 162 | # 163 | # To access raw Iemon::Property(T) 164 | # ``` 165 | # prop = you.__x_prop 166 | # ``` 167 | # 168 | def __{{ n }}_prop : Iemon::Property({{ t }})? 169 | @{{ n }} 170 | end 171 | end 172 | end 173 | end 174 | -------------------------------------------------------------------------------- /src/primitives.cr: -------------------------------------------------------------------------------- 1 | require "./primitives/*" 2 | -------------------------------------------------------------------------------- /src/primitives/primitives.cr: -------------------------------------------------------------------------------- 1 | module Iemon 2 | module Primitives 3 | STRUCT = [ 4 | Bool, Char, Symbol, 5 | Int8, Int16, Int32, Int64, Int128, 6 | UInt8, UInt16, UInt32, UInt64, UInt128, 7 | Float32, Float64 8 | ] 9 | 10 | CLASS = [ 11 | String, 12 | ] 13 | 14 | macro define_struct 15 | {% for s in STRUCT %} 16 | struct {{ s.id }} 17 | # 18 | # Override or define some method if it's needed 19 | # 20 | def clean; end 21 | end 22 | {% end %} 23 | end 24 | 25 | macro define_class 26 | {% for c in CLASS %} 27 | class {{ c.id }} 28 | # 29 | # Override or define some method if it's needed 30 | # 31 | def clean; end 32 | end 33 | {% end %} 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /src/property.cr: -------------------------------------------------------------------------------- 1 | require "./property/*" 2 | -------------------------------------------------------------------------------- /src/property/property.cr: -------------------------------------------------------------------------------- 1 | module Iemon 2 | class Property(T) 3 | @shm_addr : Pointer(UInt8) = Pointer(UInt8).null 4 | @shm_id : Int32 = -1 5 | @attached_pid : Int32 = -1 6 | @detached_pid : Int32 = -1 7 | @cleaned : Bool = false 8 | @value_set : Bool = false 9 | 10 | def initialize 11 | setup_shm 12 | end 13 | 14 | def initialize(value : T) 15 | setup_shm 16 | set_value(value) 17 | end 18 | 19 | def check_type(value : T) 20 | if !Iemon::Primitives::STRUCT.includes?(T) && 21 | !Iemon::Primitives::CLASS.includes?(T) && 22 | !value.is_a?(Iemon::Object) 23 | error_message = 24 | "#{T.name} can not be assigned. " + 25 | "Only primitive types or Iemon::Object are supported." 26 | 27 | raise NotSupportedType.new(error_message) 28 | end 29 | end 30 | 31 | def setup_shm 32 | @shm_id = LibShm.shmget( 33 | LibShm::IPC_PRIVATE, sizeof(T), 34 | LibShm::IPC_CREATE | LibShm::IPC_EXCL 35 | ) if @shm_id < 0 36 | end 37 | 38 | def value : T 39 | attach unless attached? 40 | v = raw_value 41 | detach 42 | v 43 | end 44 | 45 | def raw_value : T 46 | raise NotAttached.new("#{self.class.name} is not attached.") unless attached? 47 | raise ValueNotSet.new("Value is not set for #{self.class.name}") unless @value_set 48 | @shm_addr.as(T*).value 49 | end 50 | 51 | def set_value(value : T) 52 | check_type(value) 53 | 54 | attach unless attached? 55 | set_raw_value(value) 56 | @value_set = true 57 | detach 58 | end 59 | 60 | def set_raw_value(value : T) 61 | @shm_addr.as(T*).value = value 62 | end 63 | 64 | def attached? : Bool 65 | @attached_pid == Process.pid 66 | end 67 | 68 | def attach 69 | raise AlreadyCleaned.new("failed to attach the property. " + 70 | "(property is already cleaned)") if @cleaned 71 | 72 | @shm_addr = LibShm.shmat(@shm_id, nil, 0) 73 | 74 | @attached_pid = Process.pid 75 | @detached_pid = -1 76 | end 77 | 78 | def detached? : Bool 79 | @detached_pid == Process.pid 80 | end 81 | 82 | def detach 83 | raise AlreadyCleaned.new("failed to detach the property. " + 84 | "(property is already cleaned)") if @cleaned 85 | 86 | res = LibShm.shmdt(@shm_addr) 87 | raise LibShmException.new("LibShm#shmdt returns error code: #{res}.") if res != 0 88 | 89 | @attached_pid = -1 90 | @detached_pid = Process.pid 91 | end 92 | 93 | def clean(recursive : Bool = false) 94 | raise AlreadyCleaned.new("failed to clean the property. " + 95 | "(property is already cleaned)") if @cleaned 96 | 97 | value.clean if recursive && value.is_a?(Iemon::Object) 98 | 99 | @cleaned = true 100 | res = LibShm.shmctl(@shm_id, 0, nil) 101 | raise LibShmException.new("LibShm#shmctl returns error code: #{res}.") if res != 0 102 | end 103 | 104 | def cleaned? : Bool 105 | @cleaned 106 | end 107 | end 108 | end 109 | -------------------------------------------------------------------------------- /tools/clean.cr: -------------------------------------------------------------------------------- 1 | require "colorize" 2 | 3 | puts "start cleaning shm" 4 | puts 5 | 6 | count = 0 7 | 8 | info = `ipcs -m`.split("\n") 9 | info.each do |i| 10 | if i =~ /^m\s+(\d+)\s.+$/ 11 | print ".".colorize.fore(:green) 12 | count += 1 13 | 14 | shm_id = $1 15 | 16 | `ipcrm -m #{shm_id}` 17 | end 18 | end 19 | 20 | puts 21 | puts 22 | puts "```" 23 | puts `ipcs -m` 24 | puts "```" 25 | puts 26 | puts "finished (#{count} of shm are cleaned)" 27 | --------------------------------------------------------------------------------