pitchfork 0.13.0 → 0.14.0
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/.github/workflows/ci.yml +6 -2
- data/.ruby-version +1 -0
- data/CHANGELOG.md +6 -0
- data/Dockerfile +1 -1
- data/Gemfile.lock +2 -4
- data/docs/CONFIGURATION.md +38 -0
- data/docs/MIGRATING_FROM_UNICORN.md +34 -0
- data/docs/WHY_MIGRATE.md +5 -0
- data/examples/pitchfork.conf.service.rb +27 -0
- data/exe/pitchfork +4 -4
- data/ext/pitchfork_http/extconf.rb +2 -0
- data/ext/pitchfork_http/memory_page.c +223 -0
- data/ext/pitchfork_http/pitchfork_http.c +213 -211
- data/ext/pitchfork_http/pitchfork_http.rl +3 -1
- data/lib/pitchfork/children.rb +20 -15
- data/lib/pitchfork/configurator.rb +12 -0
- data/lib/pitchfork/http_parser.rb +11 -66
- data/lib/pitchfork/http_response.rb +1 -1
- data/lib/pitchfork/http_server.rb +175 -59
- data/lib/pitchfork/message.rb +10 -6
- data/lib/pitchfork/shared_memory.rb +16 -14
- data/lib/pitchfork/socket_helper.rb +1 -1
- data/lib/pitchfork/version.rb +1 -1
- data/lib/pitchfork/worker.rb +43 -15
- data/lib/pitchfork.rb +0 -20
- data/pitchfork.gemspec +0 -1
- metadata +7 -18
- data/lib/pitchfork/app/old_rails/static.rb +0 -60
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9d3eb42c934de40ea4ae4e25e5d334af536a03f9818dfec8798319744d1629a5
|
4
|
+
data.tar.gz: 5ea480e75dabff8298cb419a9ca1ba159af04985f4e02df8fcb51c6eff65b996
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c934f51445bc2b2904f9ecb84edfb0d454e87494fdb3a58210bf5e9b3e69887a6817be1d38f217696432ab40006448f97d22aa0eca866be4e09c6e80ca0499db
|
7
|
+
data.tar.gz: c2dfdf19c3a08aa91199b6289fef08add17281d60eaa3eefd361fb1de74aae0083ae2f10123293782d92b3c8a152b00c21676143c21b03487fecef2c8994d982
|
data/.github/workflows/ci.yml
CHANGED
@@ -4,13 +4,17 @@ on: [push, pull_request]
|
|
4
4
|
|
5
5
|
jobs:
|
6
6
|
ruby:
|
7
|
-
name: Ruby ${{ matrix.ruby }}
|
7
|
+
name: Ruby ${{ matrix.ruby }} ${{ matrix.rubyopt }}
|
8
8
|
timeout-minutes: 15
|
9
9
|
strategy:
|
10
10
|
fail-fast: false
|
11
11
|
matrix:
|
12
12
|
os: ["ubuntu-latest"]
|
13
13
|
ruby: ["ruby-head", "3.3", "3.2", "3.1", "3.0", "2.7", "2.6"]
|
14
|
+
rubyopt: [""]
|
15
|
+
include:
|
16
|
+
- ruby: "3.3"
|
17
|
+
rubyopt: "--enable-frozen-string-literal"
|
14
18
|
runs-on: ubuntu-latest
|
15
19
|
steps:
|
16
20
|
- name: Check out code
|
@@ -26,4 +30,4 @@ jobs:
|
|
26
30
|
run: sudo apt-get install -y ragel socat netcat
|
27
31
|
|
28
32
|
- name: Tests ${{ matrix.rubyopt }}
|
29
|
-
run: bundle exec rake
|
33
|
+
run: RUBYOPT="${{ matrix.rubyopt }}" bundle exec rake
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.3.1
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
+
# 0.14.0
|
4
|
+
|
5
|
+
- Remove the dependency on `raindrops`.
|
6
|
+
- Add `X-Request-Id` header in the workers proctitle if present.
|
7
|
+
- Added experimental service workers.
|
8
|
+
|
3
9
|
# 0.13.0
|
4
10
|
|
5
11
|
- Fix compatibility with `--enable-frozen-string-literal` in preparation for Ruby 3.4.
|
data/Dockerfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
pitchfork (0.
|
4
|
+
pitchfork (0.14.0)
|
5
5
|
rack (>= 2.0)
|
6
|
-
raindrops (~> 0.7)
|
7
6
|
|
8
7
|
GEM
|
9
8
|
remote: https://rubygems.org/
|
@@ -12,8 +11,7 @@ GEM
|
|
12
11
|
nio4r (2.7.0)
|
13
12
|
puma (6.4.2)
|
14
13
|
nio4r (~> 2.0)
|
15
|
-
rack (3.0.
|
16
|
-
raindrops (0.20.1)
|
14
|
+
rack (3.0.11)
|
17
15
|
rake (13.0.6)
|
18
16
|
rake-compiler (1.2.1)
|
19
17
|
rake
|
data/docs/CONFIGURATION.md
CHANGED
@@ -299,8 +299,13 @@ end
|
|
299
299
|
```ruby
|
300
300
|
after_mold_fork do |server, mold|
|
301
301
|
Database.disconnect!
|
302
|
+
|
303
|
+
# Ruby < 3.3
|
302
304
|
3.times { GC.start } # promote surviving objects to oldgen
|
303
305
|
GC.compact
|
306
|
+
|
307
|
+
# Ruby >= 3.3
|
308
|
+
Process.warmup
|
304
309
|
end
|
305
310
|
```
|
306
311
|
|
@@ -417,6 +422,39 @@ after_request_complete do |server, worker, env|
|
|
417
422
|
end
|
418
423
|
```
|
419
424
|
|
425
|
+
### `before_service_worker_ready` (experimental)
|
426
|
+
|
427
|
+
Experimental and may change at any point.
|
428
|
+
|
429
|
+
If defined, Pitchfork will spawn one extra worker, called a service worker
|
430
|
+
which doesn't accept incoming requests, but allows to perform service tasks
|
431
|
+
such as warming node local caches or emitting metrics.
|
432
|
+
|
433
|
+
Service workers are never promoted to molds, so it is safe to use threads and
|
434
|
+
other fork unsafe APIs.
|
435
|
+
|
436
|
+
This callback MUST not block. It should start one or multiple background threads
|
437
|
+
to perform tasks at regular intervals.
|
438
|
+
|
439
|
+
```ruby
|
440
|
+
before_service_worker_ready do |server, service_worker|
|
441
|
+
Thread.new do
|
442
|
+
loop do
|
443
|
+
MyApp.emit_utilization_metrics
|
444
|
+
sleep 1
|
445
|
+
end
|
446
|
+
end
|
447
|
+
end
|
448
|
+
```
|
449
|
+
|
450
|
+
### `before_service_worker_exit` (experimental)
|
451
|
+
|
452
|
+
Experimental and may change at any point.
|
453
|
+
|
454
|
+
Optional.
|
455
|
+
|
456
|
+
Called whenever the service worker is exiting. This allow to do a clean shutdown.
|
457
|
+
|
420
458
|
## Reforking
|
421
459
|
|
422
460
|
### `refork_after`
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Unicorn Migration Guide
|
2
|
+
|
3
|
+
While Pitchfork started out as a patch on top of Unicorn, many Unicorn features
|
4
|
+
that don't make sense in containerized environments were removed to simplify the codebase.
|
5
|
+
|
6
|
+
This guide is intended to cover the most common changes you need to make to your configuration
|
7
|
+
in order to make the switch.
|
8
|
+
|
9
|
+
> [!NOTE]
|
10
|
+
> This document doesn't contain every incompatibility with Unicorn. If you encounter
|
11
|
+
additional incompatibilities, please open an Issue or a Pull Request to add your findings.
|
12
|
+
|
13
|
+
* The configurations `user`, `working_directory`, `stderr_path`, `stdout_path`, and `pid`
|
14
|
+
have been removed without replacement. Pitchfork is designed for modern deployment strategies
|
15
|
+
like Docker and Systemd, as such the responsibility for this functionality is delegated to
|
16
|
+
these systems.
|
17
|
+
|
18
|
+
* The configuration `preload_app` has been removed without replacement. Pitchfork will always behave
|
19
|
+
as if it is set to `true`.
|
20
|
+
|
21
|
+
* The Signal `USR2` has been repurposed for reforking. Remove `ExecReload` from your Sytemd unit
|
22
|
+
file, if it contains it. Reloading is not a supported feature of Pitchfork.
|
23
|
+
|
24
|
+
* The configuration `after_fork` has been split between `after_worker_fork` and `after_mold_fork`.
|
25
|
+
|
26
|
+
* If you use `unicorn-worker-killer` or similar gems, you will need to implement this functionally yourself since
|
27
|
+
there is no `pitchfork-worker-killer`. Changes to Pitchfork internals make this a pretty painless
|
28
|
+
ordeal, you can check out the following GitHub issue to get started: https://github.com/Shopify/pitchfork/issues/92
|
29
|
+
|
30
|
+
## Reforking
|
31
|
+
|
32
|
+
[Reforking](REFORKING.md) is Pitchfork's main selling point. Give [Refork Safety](FORK_SAFETY.md#refork-safety) a read to understand
|
33
|
+
if your application may be compatible. [Enabling reforking](CONFIGURATION.md#refork_after) will give you memory savings above what Unicorn
|
34
|
+
is able to offer with its forking model.
|
data/docs/WHY_MIGRATE.md
CHANGED
@@ -39,6 +39,11 @@ pid file management, hot reload have been stripped.
|
|
39
39
|
|
40
40
|
Pitchfork only kept features that makes sense in a containerized world.
|
41
41
|
|
42
|
+
### Migration Guide
|
43
|
+
|
44
|
+
If the above points convinced you to make the switch, take a look at the [migration guide](MIGRATING_FROM_UNICORN.md).
|
45
|
+
It will go over the most common changes you will need to make to use Pitchfork.
|
46
|
+
|
42
47
|
## Coming from Puma
|
43
48
|
|
44
49
|
Generally speaking, compared to (threaded) Puma, Pitchfork *may* offer better latency and isolation at the expense of throughput.
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# Minimal sample configuration file for Pitchfork
|
3
|
+
|
4
|
+
# listen 2007 # by default Pitchfork listens on port 8080
|
5
|
+
worker_processes 4 # this should be >= nr_cpus
|
6
|
+
refork_after [50, 100, 1000]
|
7
|
+
|
8
|
+
service_thread = nil
|
9
|
+
service_shutdown = false
|
10
|
+
|
11
|
+
before_service_worker_ready do |server, service|
|
12
|
+
service_thread = Thread.new do
|
13
|
+
server.logger.info "Service: start"
|
14
|
+
count = 1
|
15
|
+
until service_shutdown
|
16
|
+
server.logger.info "Service: ping count=#{count}"
|
17
|
+
count += 1
|
18
|
+
sleep 1
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
before_service_worker_exit do |server, service|
|
24
|
+
server.logger.info "Service: shutting down"
|
25
|
+
service_shutdown = true
|
26
|
+
service_thread&.join(2)
|
27
|
+
end
|
data/exe/pitchfork
CHANGED
@@ -64,7 +64,7 @@ op = OptionParser.new("", 24, ' ') do |opts|
|
|
64
64
|
warn "-s/--server only exists for compatibility with rackup"
|
65
65
|
end
|
66
66
|
|
67
|
-
#
|
67
|
+
# Pitchfork-specific stuff
|
68
68
|
opts.on("-l", "--listen {HOST:PORT|PATH}",
|
69
69
|
"listen on HOST:PORT or PATH",
|
70
70
|
"this may be specified multiple times",
|
@@ -72,11 +72,11 @@ op = OptionParser.new("", 24, ' ') do |opts|
|
|
72
72
|
options[:listeners] << address
|
73
73
|
end
|
74
74
|
|
75
|
-
opts.on("-c", "--config-file FILE", "
|
75
|
+
opts.on("-c", "--config-file FILE", "Pitchfork-specific config file") do |f|
|
76
76
|
options[:config_file] = f
|
77
77
|
end
|
78
78
|
|
79
|
-
# I'm avoiding
|
79
|
+
# I'm avoiding Pitchfork-specific config options on the command-line.
|
80
80
|
# IMNSHO, config options on the command-line are redundant given
|
81
81
|
# config files and make things unnecessarily complicated with multiple
|
82
82
|
# places to look for a config option.
|
@@ -89,7 +89,7 @@ op = OptionParser.new("", 24, ' ') do |opts|
|
|
89
89
|
end
|
90
90
|
|
91
91
|
opts.on_tail("-v", "--version", "Show version") do
|
92
|
-
puts "#{cmd} v#{Pitchfork::Const::UNICORN_VERSION}"
|
92
|
+
puts "#{cmd} v#{Pitchfork::VERSION} (based on Unicorn v#{Pitchfork::Const::UNICORN_VERSION})"
|
93
93
|
exit
|
94
94
|
end
|
95
95
|
|
@@ -2,9 +2,11 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
require 'mkmf'
|
4
4
|
|
5
|
+
append_cflags("-fvisibility=hidden")
|
5
6
|
have_const("PR_SET_CHILD_SUBREAPER", "sys/prctl.h")
|
6
7
|
have_func("rb_enc_interned_str", "ruby.h") # Ruby 3.0+
|
7
8
|
have_func("rb_io_descriptor", "ruby.h") # Ruby 3.1+
|
9
|
+
have_func("getpagesize", "unistd.h")
|
8
10
|
|
9
11
|
if RUBY_VERSION.start_with?('3.0.')
|
10
12
|
# https://bugs.ruby-lang.org/issues/18772
|
@@ -0,0 +1,223 @@
|
|
1
|
+
/* Note: A large part of this code has been borrowed/stolen/adapted from raindrops. */
|
2
|
+
|
3
|
+
#include <ruby.h>
|
4
|
+
#include <unistd.h>
|
5
|
+
#include <sys/mman.h>
|
6
|
+
#include <errno.h>
|
7
|
+
#include <stddef.h>
|
8
|
+
#include <string.h>
|
9
|
+
#include <assert.h>
|
10
|
+
|
11
|
+
#define PAGE_MASK (~(page_size - 1))
|
12
|
+
#define PAGE_ALIGN(addr) (((addr) + page_size - 1) & PAGE_MASK)
|
13
|
+
|
14
|
+
static size_t slot_size = 128;
|
15
|
+
|
16
|
+
static void init_slot_size(void)
|
17
|
+
{
|
18
|
+
long tmp = 2;
|
19
|
+
|
20
|
+
#ifdef _SC_NPROCESSORS_CONF
|
21
|
+
tmp = sysconf(_SC_NPROCESSORS_CONF);
|
22
|
+
#endif
|
23
|
+
/* no point in padding on single CPU machines */
|
24
|
+
if (tmp == 1) {
|
25
|
+
slot_size = sizeof(unsigned long);
|
26
|
+
}
|
27
|
+
#ifdef _SC_LEVEL1_DCACHE_LINESIZE
|
28
|
+
if (tmp != 1) {
|
29
|
+
tmp = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
|
30
|
+
if (tmp > 0) {
|
31
|
+
slot_size = (size_t)tmp;
|
32
|
+
}
|
33
|
+
}
|
34
|
+
#endif
|
35
|
+
}
|
36
|
+
|
37
|
+
static size_t page_size = (size_t)-1;
|
38
|
+
|
39
|
+
static void init_page_size(void)
|
40
|
+
{
|
41
|
+
#if defined(_SC_PAGE_SIZE)
|
42
|
+
page_size = (size_t)sysconf(_SC_PAGE_SIZE);
|
43
|
+
#elif defined(_SC_PAGESIZE)
|
44
|
+
page_size = (size_t)sysconf(_SC_PAGESIZE);
|
45
|
+
#elif defined(HAVE_GETPAGESIZE)
|
46
|
+
page_size = (size_t)getpagesize();
|
47
|
+
#elif defined(PAGE_SIZE)
|
48
|
+
page_size = (size_t)PAGE_SIZE;
|
49
|
+
#elif defined(PAGESIZE)
|
50
|
+
page_size = (size_t)PAGESIZE;
|
51
|
+
#else
|
52
|
+
# error unable to detect page size for mmap()
|
53
|
+
#endif
|
54
|
+
if ((page_size == (size_t)-1) || (page_size < slot_size)) {
|
55
|
+
rb_raise(rb_eRuntimeError, "system page size invalid: %llu", (unsigned long long)page_size);
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
/* each slot is a counter */
|
60
|
+
struct slot {
|
61
|
+
unsigned long counter;
|
62
|
+
} __attribute__((packed));
|
63
|
+
|
64
|
+
/* allow mmap-ed regions to store more than one counter */
|
65
|
+
struct memory_page {
|
66
|
+
size_t size;
|
67
|
+
size_t capa;
|
68
|
+
struct slot *slots;
|
69
|
+
};
|
70
|
+
|
71
|
+
static void memory_page_free(void *ptr)
|
72
|
+
{
|
73
|
+
struct memory_page *page = (struct memory_page *)ptr;
|
74
|
+
|
75
|
+
if (page->slots != MAP_FAILED) {
|
76
|
+
int rv = munmap(page->slots, slot_size * page->capa);
|
77
|
+
if (rv != 0) {
|
78
|
+
rb_bug("Pitchfork::MemoryPage munmap failed in gc: %s", strerror(errno));
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
82
|
+
xfree(ptr);
|
83
|
+
}
|
84
|
+
|
85
|
+
static size_t memory_page_memsize(const void *ptr)
|
86
|
+
{
|
87
|
+
const struct memory_page *page = (const struct memory_page *)ptr;
|
88
|
+
size_t memsize = sizeof(struct memory_page);
|
89
|
+
if (page->slots != MAP_FAILED) {
|
90
|
+
memsize += slot_size * page->capa;
|
91
|
+
}
|
92
|
+
return memsize;
|
93
|
+
}
|
94
|
+
|
95
|
+
static const rb_data_type_t memory_page_type = {
|
96
|
+
.wrap_struct_name = "Pitchfork::MemoryPage",
|
97
|
+
.function = {
|
98
|
+
.dmark = NULL,
|
99
|
+
.dfree = memory_page_free,
|
100
|
+
.dsize = memory_page_memsize,
|
101
|
+
},
|
102
|
+
.flags = RUBY_TYPED_WB_PROTECTED,
|
103
|
+
};
|
104
|
+
|
105
|
+
static VALUE memory_page_alloc(VALUE klass)
|
106
|
+
{
|
107
|
+
struct memory_page *page;
|
108
|
+
VALUE obj = TypedData_Make_Struct(klass, struct memory_page, &memory_page_type, page);
|
109
|
+
|
110
|
+
page->slots = MAP_FAILED;
|
111
|
+
return obj;
|
112
|
+
}
|
113
|
+
|
114
|
+
static struct memory_page *memory_page_get(VALUE self)
|
115
|
+
{
|
116
|
+
struct memory_page *page;
|
117
|
+
|
118
|
+
TypedData_Get_Struct(self, struct memory_page, &memory_page_type, page);
|
119
|
+
|
120
|
+
if (page->slots == MAP_FAILED) {
|
121
|
+
rb_raise(rb_eStandardError, "invalid or freed Pitchfork::MemoryPage");
|
122
|
+
}
|
123
|
+
|
124
|
+
return page;
|
125
|
+
}
|
126
|
+
|
127
|
+
static unsigned long *memory_page_address(VALUE self, VALUE index)
|
128
|
+
{
|
129
|
+
struct memory_page *page = memory_page_get(self);
|
130
|
+
unsigned long off = FIX2ULONG(index) * slot_size;
|
131
|
+
|
132
|
+
if (off >= slot_size * page->size) {
|
133
|
+
rb_raise(rb_eArgError, "offset overrun");
|
134
|
+
}
|
135
|
+
|
136
|
+
return (unsigned long *)((unsigned long)page->slots + off);
|
137
|
+
}
|
138
|
+
|
139
|
+
|
140
|
+
static VALUE memory_page_aref(VALUE self, VALUE index)
|
141
|
+
{
|
142
|
+
return ULONG2NUM(*memory_page_address(self, index));
|
143
|
+
}
|
144
|
+
|
145
|
+
static VALUE memory_page_aset(VALUE self, VALUE index, VALUE value)
|
146
|
+
{
|
147
|
+
unsigned long *addr = memory_page_address(self, index);
|
148
|
+
*addr = NUM2ULONG(value);
|
149
|
+
return value;
|
150
|
+
}
|
151
|
+
|
152
|
+
static VALUE memory_page_initialize(VALUE self, VALUE size)
|
153
|
+
{
|
154
|
+
struct memory_page *page;
|
155
|
+
TypedData_Get_Struct(self, struct memory_page, &memory_page_type, page);
|
156
|
+
|
157
|
+
int tries = 1;
|
158
|
+
|
159
|
+
if (page->slots != MAP_FAILED) {
|
160
|
+
rb_raise(rb_eRuntimeError, "already initialized");
|
161
|
+
}
|
162
|
+
|
163
|
+
page->size = NUM2SIZET(size);
|
164
|
+
if (page->size < 1) {
|
165
|
+
rb_raise(rb_eArgError, "size must be >= 1");
|
166
|
+
}
|
167
|
+
|
168
|
+
size_t tmp = PAGE_ALIGN(slot_size * page->size);
|
169
|
+
page->capa = tmp / slot_size;
|
170
|
+
assert(PAGE_ALIGN(slot_size * page->capa) == tmp && "not aligned");
|
171
|
+
|
172
|
+
retry:
|
173
|
+
page->slots = mmap(NULL, tmp, PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0);
|
174
|
+
|
175
|
+
if (page->slots == MAP_FAILED) {
|
176
|
+
int err = errno;
|
177
|
+
|
178
|
+
if ((err == EAGAIN || err == ENOMEM) && tries-- > 0) {
|
179
|
+
rb_gc();
|
180
|
+
goto retry;
|
181
|
+
}
|
182
|
+
rb_sys_fail("mmap");
|
183
|
+
}
|
184
|
+
|
185
|
+
memset(page->slots, 0, tmp);
|
186
|
+
|
187
|
+
return self;
|
188
|
+
}
|
189
|
+
|
190
|
+
void init_pitchfork_memory_page(VALUE mPitchfork)
|
191
|
+
{
|
192
|
+
init_slot_size();
|
193
|
+
init_page_size();
|
194
|
+
|
195
|
+
VALUE rb_cMemoryPage = rb_define_class_under(mPitchfork, "MemoryPage", rb_cObject);
|
196
|
+
|
197
|
+
/*
|
198
|
+
* The size of one page of memory for a mmap()-ed MemoryPage region.
|
199
|
+
* Typically 4096 bytes under Linux.
|
200
|
+
*/
|
201
|
+
rb_define_const(rb_cMemoryPage, "PAGE_SIZE", SIZET2NUM(page_size));
|
202
|
+
|
203
|
+
/*
|
204
|
+
* The size (in bytes) of a slot in a MemoryPage object.
|
205
|
+
* This is the size of a word on single CPU systems and
|
206
|
+
* the size of the L1 cache line size if detectable.
|
207
|
+
*
|
208
|
+
* Defaults to 128 bytes if undetectable.
|
209
|
+
*/
|
210
|
+
rb_define_const(rb_cMemoryPage, "SLOT_SIZE", SIZET2NUM(slot_size));
|
211
|
+
|
212
|
+
rb_define_const(rb_cMemoryPage, "SLOTS", SIZET2NUM(page_size / slot_size));
|
213
|
+
|
214
|
+
/*
|
215
|
+
* The maximum value a slot counter can hold
|
216
|
+
*/
|
217
|
+
rb_define_const(rb_cMemoryPage, "SLOT_MAX", ULONG2NUM((unsigned long)-1));
|
218
|
+
|
219
|
+
rb_define_alloc_func(rb_cMemoryPage, memory_page_alloc);
|
220
|
+
rb_define_private_method(rb_cMemoryPage, "initialize", memory_page_initialize, 1);
|
221
|
+
rb_define_method(rb_cMemoryPage, "[]", memory_page_aref, 1);
|
222
|
+
rb_define_method(rb_cMemoryPage, "[]=", memory_page_aset, 2);
|
223
|
+
}
|