isomorfeus-ferret 0.13.1 → 0.13.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 98439b4a9e6ca849246c6e2ddd2ce1bbf117182025b3651d4f6a95593aff0eb6
4
- data.tar.gz: 0e1e90c4bfce1014c9983f4bb5be0f3123ef502788f464e988c4671bdd1758ed
3
+ metadata.gz: 19cfca9e5508c89a931678c23a5b0b32d8759d0d1839ac0ecf0b043d93cd1ddf
4
+ data.tar.gz: 5999edb4c8ce46718264abdfac47cdd2bcc0f8e0ddf46a0af69bfba1ec024a1c
5
5
  SHA512:
6
- metadata.gz: da674772ba34175364d0d4d93023ef380eab06334b4f9c3e1956934289a7a2016387607740fb83cc11753a3bb72de62208390933ff9f4cc341524b7fe6c0c6af
7
- data.tar.gz: 8261029020f33cb9fb52453e007defdfd2c1dd029f9da22a02b64ae0047bcd333e62e6d6a8d15263ee71089e203f393f5e865138a31784edf48f66a483a0ffce
6
+ metadata.gz: e8ab5a9574a3b610b4d554a94ecbdf3252a77d7947f81fdd21bdd0c87ed6bd0940f4842091f0bec793b4e061fe218706d0e16ef64ab19d85ca48d825a1867beb
7
+ data.tar.gz: 474d0d57a321f3eaf47e372f76c7102592b85c7d0bf2e8506e7e3baf6ad3cbba2b5628296b5a3cf50b9bdba3031a2712225234143343cb9dbf80bc3781d00faa
data/LICENSE CHANGED
@@ -139,32 +139,6 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
139
139
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
140
140
 
141
141
 
142
- lib/isomorfeus/ferret/monitor.rb originally taken from the gem 'monitor':
143
-
144
- Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
145
-
146
- Redistribution and use in source and binary forms, with or without
147
- modification, are permitted provided that the following conditions
148
- are met:
149
-
150
- 1. Redistributions of source code must retain the above copyright notice,
151
- this list of conditions and the following disclaimer.
152
- 2. Redistributions in binary form must reproduce the above copyright notice,
153
- this list of conditions and the following disclaimer in the documentation
154
- and/or other materials provided with the distribution.
155
-
156
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
157
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
158
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
159
- DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
160
- ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
161
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
162
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
163
- ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
164
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
165
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
166
-
167
-
168
142
  The following licenses apply to files, which are distributed within the repo
169
143
  but not distributed with the gem and not used at runtime:
170
144
 
data/README.md CHANGED
@@ -26,7 +26,7 @@ It should work on *nixes, *nuxes, *BSDs and also works on Windows.
26
26
  - The :store option no longer accepts :compress, compression must now be specified by the separate :compress options (see below).
27
27
  - The ASCII-specific Tokenizers and Analyzers have been removed
28
28
 
29
- ### Sring Encoding support
29
+ ### String Encoding support
30
30
 
31
31
  #### Input strings and stored fields
32
32
 
@@ -37,12 +37,13 @@ All Ruby string encodings are supported.
37
37
  When fields are stored, they are now stored with the encoding, so that when they are retrieved again, they
38
38
  retain the original encoding with positions matching the string in its original encoding.
39
39
 
40
- #### Tokens and Filters
40
+ #### Tokens, Terms, Filters and Queries
41
41
 
42
42
  Tokens are internally converted to UTF-8, which may change their length compared to their original encoding,
43
- yet they retain position information according to the source in its original encoding.
44
- The benefit is, that Filters, Stemmers or anything else working with Tokens only needs to support UTF-8 encoding,
45
- greatly simplifying things and ensuring consistent query results.
43
+ yet they retain position information according to the source in its original encoding. Terms are likewise stored in UTF-8 encoding.
44
+ Queries are converted to UTF-8 encoding too.
45
+ The benefit is, that Filters, Stemmers or anything else working with Tokens and Terms only needs to support UTF-8 encoding,
46
+ greatly simplifying things and ensuring consistent query results, independent of source encoding.
46
47
 
47
48
  ### Compression
48
49
 
@@ -64,6 +64,7 @@ static ID id_fld_num_map;
64
64
  static ID id_field_num;
65
65
  static ID id_boost;
66
66
 
67
+ extern rb_encoding *utf8_encoding;
67
68
  extern void frb_set_term(VALUE rterm, FrtTerm *t);
68
69
  extern FrtAnalyzer *frb_get_cwrapped_analyzer(VALUE ranalyzer);
69
70
  extern VALUE frb_get_analyzer(FrtAnalyzer *a);
@@ -181,8 +182,9 @@ static VALUE frb_get_field_info(FrtFieldInfo *fi) {
181
182
  fi->rfi = TypedData_Wrap_Struct(cFieldInfo, &frb_field_info_t, fi);
182
183
  FRT_REF(fi);
183
184
  }
185
+ return fi->rfi;
184
186
  }
185
- return fi->rfi;
187
+ return Qnil;
186
188
  }
187
189
 
188
190
  /*
@@ -411,8 +413,9 @@ static VALUE frb_get_field_infos(FrtFieldInfos *fis) {
411
413
  fis->rfis = TypedData_Wrap_Struct(cFieldInfos, &frb_field_infos_t, fis);
412
414
  FRT_REF(fis);
413
415
  }
416
+ return fis->rfis;
414
417
  }
415
- return fis->rfis;
418
+ return Qnil;
416
419
  }
417
420
 
418
421
  /*
@@ -496,11 +499,6 @@ static VALUE frb_fis_get(VALUE self, VALUE ridx) {
496
499
  case T_STRING:
497
500
  rfi = frb_get_field_info(frt_fis_get_field(fis, frb_field(ridx)));
498
501
  break;
499
- /*
500
- case T_STRING:
501
- rfi = frb_get_field_info(frt_fis_get_field(fis, StringValuePtr(ridx)));
502
- break;
503
- */
504
502
  default:
505
503
  rb_raise(rb_eArgError, "Can't index FieldInfos with %s",
506
504
  rs2s(rb_obj_as_string(ridx)));
@@ -1219,6 +1217,7 @@ static VALUE frb_get_tv_term(FrtTVTerm *tv_term) {
1219
1217
  VALUE rtext;
1220
1218
  VALUE rpositions = Qnil;
1221
1219
  rtext = rb_str_new2(tv_term->text);
1220
+ rb_enc_associate(rtext, utf8_encoding);
1222
1221
  if (tv_term->positions) {
1223
1222
  int *positions = tv_term->positions;
1224
1223
  rpositions = rb_ary_new2(freq);
@@ -1697,10 +1696,9 @@ frb_iw_delete(VALUE self, VALUE rfield, VALUE rterm)
1697
1696
  * Get the FieldInfos object for this FrtIndexWriter. This is useful if you need
1698
1697
  * to dynamically add new fields to the index with specific properties.
1699
1698
  */
1700
- static VALUE
1701
- frb_iw_field_infos(VALUE self)
1702
- {
1703
- FrtIndexWriter *iw = (FrtIndexWriter *)DATA_PTR(self);
1699
+ static VALUE frb_iw_field_infos(VALUE self) {
1700
+ FrtIndexWriter *iw;
1701
+ TypedData_Get_Struct(self, FrtIndexWriter, &frb_index_writer_t, iw);
1704
1702
  return frb_get_field_infos(iw->fis);
1705
1703
  }
1706
1704
 
@@ -2715,10 +2713,9 @@ frb_ir_fields(VALUE self)
2715
2713
  *
2716
2714
  * Get the FieldInfos object for this IndexReader.
2717
2715
  */
2718
- static VALUE
2719
- frb_ir_field_infos(VALUE self)
2720
- {
2721
- FrtIndexReader *ir = (FrtIndexReader *)DATA_PTR(self);
2716
+ static VALUE frb_ir_field_infos(VALUE self) {
2717
+ FrtIndexReader *ir;
2718
+ TypedData_Get_Struct(self, FrtIndexReader, &frb_index_reader_t, ir);
2722
2719
  return frb_get_field_infos(ir->fis);
2723
2720
  }
2724
2721
 
@@ -3085,10 +3082,6 @@ static void Init_TermDocEnum(void) {
3085
3082
  rb_define_method(cTermDocEnum, "to_json", frb_tde_to_json, -1);
3086
3083
  }
3087
3084
 
3088
- /* rdochack
3089
- cTermVector = rb_define_class_under(mIndex, "TermVector", rb_cObject);
3090
- */
3091
-
3092
3085
  /*
3093
3086
  * Document-class: Ferret::Index::TermVector::TVOffsets
3094
3087
  *
@@ -3107,9 +3100,6 @@ cTermVector = rb_define_class_under(mIndex, "TermVector", rb_cObject);
3107
3100
  */
3108
3101
  static void Init_TVOffsets(void) {
3109
3102
  const char *tv_offsets_class = "TVOffsets";
3110
- /* rdochack
3111
- cTVOffsets = rb_define_class_under(cTermVector, "TVOffsets", rb_cObject);
3112
- */
3113
3103
  cTVOffsets = rb_struct_define(tv_offsets_class, "start", "end", NULL);
3114
3104
  rb_set_class_path(cTVOffsets, cTermVector, tv_offsets_class);
3115
3105
  rb_const_set(mIndex, rb_intern(tv_offsets_class), cTVOffsets);
@@ -3130,13 +3120,8 @@ static void Init_TVOffsets(void) {
3130
3120
  * tv_term = tv.find {|tvt| tvt.term = "fox"}
3131
3121
  * offsets = tv_term.positions.collect {|pos| tv.offsets[pos]}
3132
3122
  */
3133
- static void
3134
- Init_TVTerm(void)
3135
- {
3123
+ static void Init_TVTerm(void) {
3136
3124
  const char *tv_term_class = "TVTerm";
3137
- /* rdochack
3138
- cTVTerm = rb_define_class_under(cTermVector, "TVTerm", rb_cObject);
3139
- */
3140
3125
  cTVTerm = rb_struct_define(tv_term_class, "text", "freq", "positions", NULL);
3141
3126
  rb_set_class_path(cTVTerm, cTermVector, tv_term_class);
3142
3127
  rb_const_set(mIndex, rb_intern(tv_term_class), cTVTerm);
@@ -3172,15 +3157,9 @@ Init_TVTerm(void)
3172
3157
  * particular that you need to store both positions and offsets if you want
3173
3158
  * to associate offsets with particular terms.
3174
3159
  */
3175
- static void
3176
- Init_TermVector(void)
3177
- {
3160
+ static void Init_TermVector(void) {
3178
3161
  const char *tv_class = "TermVector";
3179
- /* rdochack
3180
- cTermVector = rb_define_class_under(mIndex, "TermVector", rb_cObject);
3181
- */
3182
- cTermVector = rb_struct_define(tv_class,
3183
- "field", "terms", "offsets", NULL);
3162
+ cTermVector = rb_struct_define(tv_class, "field", "terms", "offsets", NULL);
3184
3163
  rb_set_class_path(cTermVector, mIndex, tv_class);
3185
3164
  rb_const_set(mIndex, rb_intern(tv_class), cTermVector);
3186
3165
 
@@ -14,6 +14,7 @@
14
14
  #undef close
15
15
  #undef read
16
16
 
17
+ extern rb_encoding *utf8_encoding;
17
18
  extern void frt_micro_sleep(const int micro_seconds);
18
19
 
19
20
  #define GET_LOCK(lock, name, store, err_msg) do {\
@@ -1710,8 +1711,7 @@ static FrtTermVector *frt_fr_read_term_vector(FrtFieldsReader *fr, int field_num
1710
1711
  total_len = delta_start + delta_len;
1711
1712
  frt_is_read_bytes(fdt_in, buffer + delta_start, delta_len);
1712
1713
  buffer[total_len++] = '\0';
1713
- term->text = (char *)memcpy(FRT_ALLOC_N(char, total_len),
1714
- buffer, total_len);
1714
+ term->text = (char *)memcpy(FRT_ALLOC_N(char, total_len), buffer, total_len);
1715
1715
 
1716
1716
  /* read freq */
1717
1717
  freq = term->freq = frt_is_read_vint(fdt_in);
@@ -1822,8 +1822,7 @@ FrtTermVector *frt_fr_get_field_tv(FrtFieldsReader *fr, int doc_num, int field_n
1822
1822
  *
1823
1823
  ****************************************************************************/
1824
1824
 
1825
- FrtFieldsWriter *frt_fw_open(FrtStore *store, const char *segment, FrtFieldInfos *fis)
1826
- {
1825
+ FrtFieldsWriter *frt_fw_open(FrtStore *store, const char *segment, FrtFieldInfos *fis) {
1827
1826
  FrtFieldsWriter *fw = FRT_ALLOC(FrtFieldsWriter);
1828
1827
  char file_name[FRT_SEGMENT_NAME_MAX_LENGTH];
1829
1828
  size_t segment_len = strlen(segment);
@@ -1844,8 +1843,7 @@ FrtFieldsWriter *frt_fw_open(FrtStore *store, const char *segment, FrtFieldInfos
1844
1843
  return fw;
1845
1844
  }
1846
1845
 
1847
- void frt_fw_close(FrtFieldsWriter *fw)
1848
- {
1846
+ void frt_fw_close(FrtFieldsWriter *fw) {
1849
1847
  frt_os_close(fw->fdt_out);
1850
1848
  frt_os_close(fw->fdx_out);
1851
1849
  frt_ram_destroy_buffer(fw->buffer);
@@ -2046,8 +2044,7 @@ void frt_fw_add_doc(FrtFieldsWriter *fw, FrtDocument *doc) {
2046
2044
  frt_ramo_write_to(fw->buffer, fdt_out);
2047
2045
  }
2048
2046
 
2049
- void frt_fw_write_tv_index(FrtFieldsWriter *fw)
2050
- {
2047
+ void frt_fw_write_tv_index(FrtFieldsWriter *fw) {
2051
2048
  int i;
2052
2049
  const int tv_cnt = frt_ary_size(fw->tv_fields);
2053
2050
  FrtOutStream *fdt_out = fw->fdt_out;
@@ -5548,9 +5545,24 @@ FrtHash *frt_dw_invert_field(FrtDocWriter *dw, FrtFieldInverter *fld_inv, FrtDoc
5548
5545
  for (i = 0; i < df_size; i++) {
5549
5546
  int len = df->lengths[i];
5550
5547
  char *data_ptr = df->data[i];
5551
- if (len > FRT_MAX_WORD_SIZE) {
5552
- len = FRT_MAX_WORD_SIZE - 1;
5553
- data_ptr = (char *)memcpy(buf, df->data[i], len);
5548
+ if (df->encodings[i] == utf8_encoding) {
5549
+ if (len >= FRT_MAX_WORD_SIZE) {
5550
+ len = FRT_MAX_WORD_SIZE - 1; // TODO: this may invalidate mbc's
5551
+ data_ptr = (char *)memcpy(buf, df->data[i], len);
5552
+ buf[len] = '\0';
5553
+ }
5554
+ } else if (df->encodings[i] != utf8_encoding) {
5555
+ if (len >= FRT_MAX_WORD_SIZE)
5556
+ len = FRT_MAX_WORD_SIZE - 1;
5557
+ const unsigned char *sp = (unsigned char *)df->data[i];
5558
+ unsigned char *dp = (unsigned char *)&buf;
5559
+ rb_econv_t *ec = rb_econv_open(rb_enc_name(df->encodings[i]), "UTF-8", RUBY_ECONV_INVALID_REPLACE);
5560
+ assert(ec != NULL);
5561
+ rb_econv_convert(ec, &sp, (unsigned char *)df->data[i] + len, &dp, (unsigned char *)&buf + FRT_MAX_WORD_SIZE - 1, 0);
5562
+ rb_econv_close(ec);
5563
+ len = dp - (unsigned char *)&buf;
5564
+ buf[len] = '\0';
5565
+ data_ptr = buf;
5554
5566
  }
5555
5567
  dw_add_posting(mp, curr_plists, fld_plists, doc_num, data_ptr, len, i);
5556
5568
  if (store_offsets) {
@@ -4,7 +4,7 @@ module Isomorfeus
4
4
  # This is a simplified interface to the index. See the TUTORIAL for more
5
5
  # information on how to use this class.
6
6
  class Index
7
- include Isomorfeus::Ferret::MonitorMixin
7
+ include MonitorMixin
8
8
  include Isomorfeus::Ferret::Store
9
9
  include Isomorfeus::Ferret::Search
10
10
 
@@ -1,5 +1,5 @@
1
1
  module Isomorfeus
2
2
  module Ferret
3
- VERSION = '0.13.1'
3
+ VERSION = '0.13.4'
4
4
  end
5
5
  end
@@ -2,7 +2,7 @@ require 'isomorfeus_ferret_ext'
2
2
  require 'isomorfeus/ferret/version'
3
3
  require 'isomorfeus/ferret/field_symbol'
4
4
  require 'isomorfeus/ferret/stdlib_patches'
5
- require 'isomorfeus/ferret/monitor'
5
+ require 'monitor'
6
6
  require 'isomorfeus/ferret/index/field_infos'
7
7
  require 'isomorfeus/ferret/index/index'
8
8
  require 'isomorfeus/ferret/document'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: isomorfeus-ferret
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.1
4
+ version: 0.13.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jan Biedermann
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-04-16 00:00:00.000000000 Z
11
+ date: 2022-04-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -360,7 +360,6 @@ files:
360
360
  - lib/isomorfeus/ferret/field_symbol.rb
361
361
  - lib/isomorfeus/ferret/index/field_infos.rb
362
362
  - lib/isomorfeus/ferret/index/index.rb
363
- - lib/isomorfeus/ferret/monitor.rb
364
363
  - lib/isomorfeus/ferret/stdlib_patches.rb
365
364
  - lib/isomorfeus/ferret/version.rb
366
365
  homepage: https://isomorfeus.com
@@ -1,323 +0,0 @@
1
- # frozen_string_literal: false
2
- # = monitor.rb
3
- #
4
- # Copyright (C) 2001 Shugo Maeda <shugo@ruby-lang.org>
5
- #
6
- # This library is distributed under the terms of the Ruby license.
7
- # You can freely distribute/modify this library.
8
- #
9
-
10
- #
11
- # In concurrent programming, a monitor is an object or module intended to be
12
- # used safely by more than one thread. The defining characteristic of a
13
- # monitor is that its methods are executed with mutual exclusion. That is, at
14
- # each point in time, at most one thread may be executing any of its methods.
15
- # This mutual exclusion greatly simplifies reasoning about the implementation
16
- # of monitors compared to reasoning about parallel code that updates a data
17
- # structure.
18
- #
19
- # You can read more about the general principles on the Wikipedia page for
20
- # Monitors[http://en.wikipedia.org/wiki/Monitor_%28synchronization%29]
21
- #
22
- # == Examples
23
- #
24
- # === Simple object.extend
25
- #
26
- # require 'monitor.rb'
27
- #
28
- # buf = []
29
- # buf.extend(MonitorMixin)
30
- # empty_cond = buf.new_cond
31
- #
32
- # # consumer
33
- # Thread.start do
34
- # loop do
35
- # buf.synchronize do
36
- # empty_cond.wait_while { buf.empty? }
37
- # print buf.shift
38
- # end
39
- # end
40
- # end
41
- #
42
- # # producer
43
- # while line = ARGF.gets
44
- # buf.synchronize do
45
- # buf.push(line)
46
- # empty_cond.signal
47
- # end
48
- # end
49
- #
50
- # The consumer thread waits for the producer thread to push a line to buf
51
- # while <tt>buf.empty?</tt>. The producer thread (main thread) reads a
52
- # line from ARGF and pushes it into buf then calls <tt>empty_cond.signal</tt>
53
- # to notify the consumer thread of new data.
54
- #
55
- # === Simple Class include
56
- #
57
- # require 'monitor'
58
- #
59
- # class SynchronizedArray < Array
60
- #
61
- # include MonitorMixin
62
- #
63
- # def initialize(*args)
64
- # super(*args)
65
- # end
66
- #
67
- # alias :old_shift :shift
68
- # alias :old_unshift :unshift
69
- #
70
- # def shift(n=1)
71
- # self.synchronize do
72
- # self.old_shift(n)
73
- # end
74
- # end
75
- #
76
- # def unshift(item)
77
- # self.synchronize do
78
- # self.old_unshift(item)
79
- # end
80
- # end
81
- #
82
- # # other methods ...
83
- # end
84
- #
85
- # +SynchronizedArray+ implements an Array with synchronized access to items.
86
- # This Class is implemented as subclass of Array which includes the
87
- # MonitorMixin module.
88
- #
89
- module Isomorfeus
90
- module Ferret
91
- module MonitorMixin
92
- #
93
- # FIXME: This isn't documented in Nutshell.
94
- #
95
- # Since MonitorMixin.new_cond returns a ConditionVariable, and the example
96
- # above calls while_wait and signal, this class should be documented.
97
- #
98
- class ConditionVariable
99
- class Timeout < Exception; end
100
-
101
- #
102
- # Releases the lock held in the associated monitor and waits; reacquires the lock on wakeup.
103
- #
104
- # If +timeout+ is given, this method returns after +timeout+ seconds passed,
105
- # even if no other thread doesn't signal.
106
- #
107
- def wait(timeout = nil)
108
- Thread.handle_interrupt(Exception => :never) do
109
- @monitor.__send__(:mon_check_owner)
110
- count = @monitor.__send__(:mon_exit_for_cond)
111
- begin
112
- Thread.handle_interrupt(Exception => :immediate) do
113
- @cond.wait(@monitor.instance_variable_get(:@mon_mutex), timeout)
114
- end
115
- return true
116
- ensure
117
- @monitor.__send__(:mon_enter_for_cond, count)
118
- end
119
- end
120
- end
121
-
122
- #
123
- # Calls wait repeatedly while the given block yields a truthy value.
124
- #
125
- def wait_while
126
- while yield
127
- wait
128
- end
129
- end
130
-
131
- #
132
- # Calls wait repeatedly until the given block yields a truthy value.
133
- #
134
- def wait_until
135
- until yield
136
- wait
137
- end
138
- end
139
-
140
- #
141
- # Wakes up the first thread in line waiting for this lock.
142
- #
143
- def signal
144
- @monitor.__send__(:mon_check_owner)
145
- @cond.signal
146
- end
147
-
148
- #
149
- # Wakes up all threads waiting for this lock.
150
- #
151
- def broadcast
152
- @monitor.__send__(:mon_check_owner)
153
- @cond.broadcast
154
- end
155
-
156
- private
157
-
158
- def initialize(monitor)
159
- @monitor = monitor
160
- @cond = Thread::ConditionVariable.new
161
- end
162
- end
163
-
164
- def self.extend_object(obj)
165
- super(obj)
166
- obj.__send__(:mon_initialize)
167
- end
168
-
169
- #
170
- # Attempts to enter exclusive section. Returns +false+ if lock fails.
171
- #
172
- def mon_try_enter
173
- if @mon_owner != Thread.current
174
- unless @mon_mutex.try_lock
175
- return false
176
- end
177
- @mon_owner = Thread.current
178
- @mon_count = 0
179
- end
180
- @mon_count += 1
181
- return true
182
- end
183
- # For backward compatibility
184
- alias try_mon_enter mon_try_enter
185
-
186
- #
187
- # Enters exclusive section.
188
- #
189
- def mon_enter
190
- if @mon_owner != Thread.current
191
- @mon_mutex.lock
192
- @mon_owner = Thread.current
193
- @mon_count = 0
194
- end
195
- @mon_count += 1
196
- end
197
-
198
- #
199
- # Leaves exclusive section.
200
- #
201
- def mon_exit
202
- mon_check_owner
203
- @mon_count -=1
204
- if @mon_count == 0
205
- @mon_owner = nil
206
- @mon_mutex.unlock
207
- end
208
- end
209
-
210
- #
211
- # Returns true if this monitor is locked by any thread
212
- #
213
- def mon_locked?
214
- @mon_mutex.locked?
215
- end
216
-
217
- #
218
- # Returns true if this monitor is locked by current thread.
219
- #
220
- def mon_owned?
221
- @mon_mutex.locked? && @mon_owner == Thread.current
222
- end
223
-
224
- #
225
- # Enters exclusive section and executes the block. Leaves the exclusive
226
- # section automatically when the block exits. See example under
227
- # +MonitorMixin+.
228
- #
229
- def mon_synchronize
230
- # Prevent interrupt on handling interrupts; for example timeout errors
231
- # it may break locking state.
232
- Thread.handle_interrupt(Exception => :never){ mon_enter }
233
- begin
234
- yield
235
- ensure
236
- Thread.handle_interrupt(Exception => :never){ mon_exit }
237
- end
238
- end
239
- alias synchronize mon_synchronize
240
-
241
- #
242
- # Creates a new MonitorMixin::ConditionVariable associated with the
243
- # receiver.
244
- #
245
- def new_cond
246
- return ConditionVariable.new(self)
247
- end
248
-
249
- private
250
-
251
- # Use <tt>extend MonitorMixin</tt> or <tt>include MonitorMixin</tt> instead
252
- # of this constructor. Have look at the examples above to understand how to
253
- # use this module.
254
- def initialize(*args)
255
- super
256
- mon_initialize
257
- end
258
-
259
- # Initializes the MonitorMixin after being included in a class or when an
260
- # object has been extended with the MonitorMixin
261
- def mon_initialize
262
- if defined?(@mon_mutex) && @mon_mutex_owner_object_id == object_id
263
- raise ThreadError, "already initialized"
264
- end
265
- @mon_mutex = Thread::Mutex.new
266
- @mon_mutex_owner_object_id = object_id
267
- @mon_owner = nil
268
- @mon_count = 0
269
- end
270
-
271
- def mon_check_owner
272
- if @mon_owner != Thread.current
273
- raise ThreadError, "current thread not owner"
274
- end
275
- end
276
-
277
- def mon_enter_for_cond(count)
278
- @mon_owner = Thread.current
279
- @mon_count = count
280
- end
281
-
282
- def mon_exit_for_cond
283
- count = @mon_count
284
- @mon_owner = nil
285
- @mon_count = 0
286
- return count
287
- end
288
- end
289
-
290
- # Use the Monitor class when you want to have a lock object for blocks with
291
- # mutual exclusion.
292
- #
293
- # require 'monitor'
294
- #
295
- # lock = Monitor.new
296
- # lock.synchronize do
297
- # # exclusive access
298
- # end
299
- #
300
- class Monitor
301
- include MonitorMixin
302
- alias try_enter try_mon_enter
303
- alias enter mon_enter
304
- alias exit mon_exit
305
- end
306
- end
307
- end
308
-
309
- # Documentation comments:
310
- # - All documentation comes from Nutshell.
311
- # - MonitorMixin.new_cond appears in the example, but is not documented in
312
- # Nutshell.
313
- # - All the internals (internal modules Accessible and Initializable, class
314
- # ConditionVariable) appear in RDoc. It might be good to hide them, by
315
- # making them private, or marking them :nodoc:, etc.
316
- # - RDoc doesn't recognise aliases, so we have mon_synchronize documented, but
317
- # not synchronize.
318
- # - mon_owner is in Nutshell, but appears as an accessor in a separate module
319
- # here, so is hard/impossible to RDoc. Some other useful accessors
320
- # (mon_count and some queue stuff) are also in this module, and don't appear
321
- # directly in the RDoc output.
322
- # - in short, it may be worth changing the code layout in this file to make the
323
- # documentation easier