isomorfeus-ferret 0.13.1 → 0.13.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE +0 -26
- data/README.md +6 -5
- data/ext/isomorfeus_ferret_ext/frb_index.c +15 -36
- data/ext/isomorfeus_ferret_ext/frt_index.c +23 -11
- data/lib/isomorfeus/ferret/index/index.rb +1 -1
- data/lib/isomorfeus/ferret/version.rb +1 -1
- data/lib/isomorfeus-ferret.rb +1 -1
- metadata +2 -3
- data/lib/isomorfeus/ferret/monitor.rb +0 -323
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 19cfca9e5508c89a931678c23a5b0b32d8759d0d1839ac0ecf0b043d93cd1ddf
|
4
|
+
data.tar.gz: 5999edb4c8ce46718264abdfac47cdd2bcc0f8e0ddf46a0af69bfba1ec024a1c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
###
|
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
|
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
|
-
|
45
|
-
|
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
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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 (
|
5552
|
-
len
|
5553
|
-
|
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
|
7
|
+
include MonitorMixin
|
8
8
|
include Isomorfeus::Ferret::Store
|
9
9
|
include Isomorfeus::Ferret::Search
|
10
10
|
|
data/lib/isomorfeus-ferret.rb
CHANGED
@@ -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 '
|
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.
|
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-
|
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
|