├── doc
├── state-0.dia
├── state-0.png
├── state-1.dia
├── state-1.png
├── state-10.dia
├── state-10.png
├── state-11.dia
├── state-11.png
├── state-14.dia
├── state-14.png
├── state-15.dia
├── state-15.png
├── state-26.dia
├── state-26.png
├── state-27.dia
├── state-27.png
├── state-30.dia
├── state-30.png
├── state-31.dia
├── state-31.png
├── rtgc-slides.pdf
├── transitions.dia
├── transitions.png
├── export-pngs
├── states.html
└── gc-states.txt
├── LICENSE
├── atomic-booleans.s
├── rrhunt.lisp
├── wbcheck
├── Makefile
├── wbtest.c
├── wb-libclang.c
└── wb.cpp
├── mem-config.h
├── Makefile
├── allocate.h
├── rtglobals.c
├── info-bits.h
├── sigtime.c
├── HEY!
├── mem-internals.h
├── a.c
├── rtutil.c
├── rtstop.c
├── rtcoalesce.c
├── rtalloc.c
├── rtgc.c
├── creeping.txt
└── redhead.txt
/doc/state-0.dia:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wadehennessey/rtgc/HEAD/doc/state-0.dia
--------------------------------------------------------------------------------
/doc/state-0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wadehennessey/rtgc/HEAD/doc/state-0.png
--------------------------------------------------------------------------------
/doc/state-1.dia:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wadehennessey/rtgc/HEAD/doc/state-1.dia
--------------------------------------------------------------------------------
/doc/state-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wadehennessey/rtgc/HEAD/doc/state-1.png
--------------------------------------------------------------------------------
/doc/state-10.dia:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wadehennessey/rtgc/HEAD/doc/state-10.dia
--------------------------------------------------------------------------------
/doc/state-10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wadehennessey/rtgc/HEAD/doc/state-10.png
--------------------------------------------------------------------------------
/doc/state-11.dia:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wadehennessey/rtgc/HEAD/doc/state-11.dia
--------------------------------------------------------------------------------
/doc/state-11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wadehennessey/rtgc/HEAD/doc/state-11.png
--------------------------------------------------------------------------------
/doc/state-14.dia:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wadehennessey/rtgc/HEAD/doc/state-14.dia
--------------------------------------------------------------------------------
/doc/state-14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wadehennessey/rtgc/HEAD/doc/state-14.png
--------------------------------------------------------------------------------
/doc/state-15.dia:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wadehennessey/rtgc/HEAD/doc/state-15.dia
--------------------------------------------------------------------------------
/doc/state-15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wadehennessey/rtgc/HEAD/doc/state-15.png
--------------------------------------------------------------------------------
/doc/state-26.dia:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wadehennessey/rtgc/HEAD/doc/state-26.dia
--------------------------------------------------------------------------------
/doc/state-26.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wadehennessey/rtgc/HEAD/doc/state-26.png
--------------------------------------------------------------------------------
/doc/state-27.dia:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wadehennessey/rtgc/HEAD/doc/state-27.dia
--------------------------------------------------------------------------------
/doc/state-27.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wadehennessey/rtgc/HEAD/doc/state-27.png
--------------------------------------------------------------------------------
/doc/state-30.dia:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wadehennessey/rtgc/HEAD/doc/state-30.dia
--------------------------------------------------------------------------------
/doc/state-30.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wadehennessey/rtgc/HEAD/doc/state-30.png
--------------------------------------------------------------------------------
/doc/state-31.dia:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wadehennessey/rtgc/HEAD/doc/state-31.dia
--------------------------------------------------------------------------------
/doc/state-31.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wadehennessey/rtgc/HEAD/doc/state-31.png
--------------------------------------------------------------------------------
/doc/rtgc-slides.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wadehennessey/rtgc/HEAD/doc/rtgc-slides.pdf
--------------------------------------------------------------------------------
/doc/transitions.dia:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wadehennessey/rtgc/HEAD/doc/transitions.dia
--------------------------------------------------------------------------------
/doc/transitions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wadehennessey/rtgc/HEAD/doc/transitions.png
--------------------------------------------------------------------------------
/doc/export-pngs:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | dia --version
3 | dia --export=state-0.png state-0.dia
4 | dia --export=state-10.png state-10.dia
5 | dia --export=state-11.png state-11.dia
6 | dia --export=state-14.png state-14.dia
7 | dia --export=state-15.png state-15.dia
8 | dia --export=state-1.png state-1.dia
9 | dia --export=state-26.png state-26.dia
10 | dia --export=state-27.png state-27.dia
11 | dia --export=state-30.png state-30.dia
12 | dia --export=state-31.png state-31.dia
13 | dia --export=transitions.png transitions.dia
14 |
15 |
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | RTGC - Real Time Garbage Collection
2 | Copyright 2017 Wade Lawrence Hennessey
3 |
4 | RTGC is licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use RTGC except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 |
--------------------------------------------------------------------------------
/atomic-booleans.s:
--------------------------------------------------------------------------------
1 | # Copyright 2017 Wade Lawrence Hennessey
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | .text
16 |
17 | .align 8
18 | .global locked_long_or
19 | locked_long_or:
20 | lock orq %rsi, (%rdi)
21 | ret
22 |
23 | .align 8
24 | .global locked_long_and
25 | locked_long_and:
26 | lock andq %rsi, (%rdi)
27 | ret
28 |
29 | .align 8
30 | .global locked_long_inc
31 | locked_long_inc:
32 | lock addq $1, (%rdi)
33 | ret
34 |
35 | .align 8
36 | .globl locked_byte_or
37 | locked_byte_or:
38 | lock orb %sil, (%rdi)
39 | ret
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/rrhunt.lisp:
--------------------------------------------------------------------------------
1 | ;;; Copyright 2017 Wade Lawrence Hennessey
2 | ;;;
3 | ;;; Licensed under the Apache License, Version 2.0 (the "License");
4 | ;;; you may not use this file except in compliance with the License.
5 | ;;; You may obtain a copy of the License at
6 | ;;;
7 | ;;; http://www.apache.org/licenses/LICENSE-2.0
8 | ;;;
9 | ;;; Unless required by applicable law or agreed to in writing, software
10 | ;;; distributed under the License is distributed on an "AS IS" BASIS,
11 | ;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | ;;; See the License for the specific language governing permissions and
13 | ;;; limitations under the License.
14 |
15 | (defvar *proc*)
16 |
17 | (defun hunt ()
18 | (dotimes (run 10000)
19 | (run-program "/usr/bin/rm" `("-rf" "/home/wade/.local/share/rr"))
20 | (let ((*proc* (run-program "/usr/bin/rr"
21 | `("record" "-c" "250" "/home/wade/rtgc/a")
22 | :output t :wait nil)))
23 | (print *proc*)
24 | (terpri)
25 | (dotimes (time 70)
26 | (sleep 60)
27 | (let ((status (process-status *proc*)))
28 | (unless (process-alive-p *proc*)
29 | (format t "~%*******Program died with ~S!*****~%" status)
30 | (return-from hunt 'done))
31 | (format t "~S - ~D~%" status time)))
32 | (process-kill *proc* 9 :pid))))
33 |
--------------------------------------------------------------------------------
/wbcheck/Makefile:
--------------------------------------------------------------------------------
1 | # Copyright 2017 Wade Lawrence Hennessey
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | CC = clang
16 |
17 | all: wb
18 |
19 | wb: wb.cpp
20 | g++ -fno-rtti -O0 -g `/home/wade/build//bin/llvm-config --cxxflags` -I/home/wade/llvm//tools/clang/include -I/home/wade/build//tools/clang/include wb.cpp \
21 | -Wl,--start-group -lclangAST -lclangASTMatchers -lclangAnalysis -lclangBasic -lclangDriver -lclangEdit -lclangFrontend -lclangFrontendTool -lclangLex -lclangParse -lclangSema -lclangEdit -lclangRewrite -lclangRewriteFrontend -lclangStaticAnalyzerFrontend -lclangStaticAnalyzerCheckers -lclangStaticAnalyzerCore -lclangSerialization -lclangToolingCore -lclangTooling -lclangFormat -Wl,--end-group `/home/wade/build//bin/llvm-config --ldflags --libs --system-libs` -o wb
22 |
23 | wb-libclang:
24 | $(CC) -o wb-libclang -g wb-libclang.c -lclang
25 |
26 | clean:
27 | rm -f wb wb-libclang
28 |
29 |
30 |
--------------------------------------------------------------------------------
/doc/states.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | GC States
5 |
6 |
7 |
8 |
20 |
21 |
22 |
23 |  |
24 |
25 |
26 |
27 |
28 |  |
29 |  |
30 |  |
31 |
32 |
33 |  |
34 |  |
35 |  |
36 |
37 |
38 |  |
39 |  |
40 |  |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/mem-config.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 Wade Lawrence Hennessey
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | // Memory management configuration
19 |
20 | // HEY! get rid of max_segments?
21 | #define MAX_HEAP_SEGMENTS 1
22 | #define MAX_STATIC_SEGMENTS 1
23 | #define MAX_SEGMENTS MAX_HEAP_SEGMENTS + MAX_STATIC_SEGMENTS
24 | #define MAX_THREADS 20
25 | #define MAX_GLOBAL_ROOTS 1000
26 |
27 | // The heap is divided into multiple segments
28 | #define DEFAULT_HEAP_SEGMENT_SIZE 1 << 20
29 | #define DEFAULT_STATIC_SEGMENT_SIZE 0 /* only 1 static segment for now */
30 | #define CHECK_BASH 0
31 | #define CHECK_SETFINIT 1
32 | #define GC_POINTER_ALIGNMENT (sizeof(long *))
33 | #define PAGE_POWER 12 /* x86_64 page size is normally 4096 */
34 | //#define INTERIOR_PTR_RETENTION_LIMIT 32
35 | #define INTERIOR_PTR_RETENTION_LIMIT 512
36 |
37 | #define FLIP_SIGNAL SIGUSR1
38 | #define DETECT_INVALID_REFS 0
39 | #define USE_BIT_WRITE_BARRIER 1
40 |
41 | #ifdef NDEBUG
42 | #define DEBUG(x)
43 | #else
44 | #define DEBUG(x) x
45 | #endif
46 |
47 |
48 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # Copyright 2017 Wade Lawrence Hennessey
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | CC = gcc
16 |
17 | a: a.c
18 | $(CC) -o a -g a.c -L./ -lrtgc
19 |
20 | opt-a:
21 | $(CC) -o a -O2 -g -DNDEBUG a.c -L./ -lrtgc
22 |
23 | lib:
24 | $(CC) -shared -fPIC -o librtgc.so -g rtglobals.c rtalloc.c rtgc.c rtstop.c rtutil.c atomic-booleans.s rtcoalesce.c -lpthread
25 |
26 | opt-lib:
27 | $(CC) -shared -fPIC -o librtgc.so -O2 -g -DNDEBUG rtglobals.c rtalloc.c rtgc.c rtstop.c rtutil.c atomic-booleans.s rtcoalesce.c -lpthread
28 |
29 | all:
30 | $(CC) -g -o a a.c rtglobals.c rtalloc.c rtgc.c rtstop.c rtutil.c atomic-booleans.s rtcoalesce.c -lpthread
31 |
32 | debug:
33 | $(CC) -g -o a a.c rtglobals.c rtalloc.c rtgc.c rtstop.c rtutil.c atomic-booleans.s rtcoalesce.c -lpthread
34 |
35 | opt:
36 | $(CC) -O2 -g -o a a.c rtglobals.c rtalloc.c rtgc.c rtstop.c rtutil.c atomic-booleans.s rtcoalesce.c -lpthread
37 |
38 | sigtime:sigtime.c
39 | $(CC) -o sigtime -g sigtime.c -lpthread
40 |
41 | install:
42 | cp allocate.h /usr/local/include
43 | cp librtgc.so /usr/local/lib64
44 | /sbin/ldconfig
45 |
46 | tags:
47 | etags *.[c,h]
48 |
49 | clean:
50 | rm -f a *.o *.so
51 |
52 |
--------------------------------------------------------------------------------
/wbcheck/wbtest.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 Wade Lawrence Hennessey
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | // clang-check wbtest.c --ast-dump --
19 |
20 | #include
21 |
22 | char *global_var;
23 |
24 | typedef struct cons {
25 | void *car;
26 | struct cons *cdr;
27 | } CONS;
28 |
29 | typedef union header {
30 | struct {
31 | union header *ptr;
32 | void *ptr2;
33 | } one;
34 | void *ptr1;
35 | } HEADER;
36 |
37 | void s1(struct cons *lhs, CONS *rhs) {
38 | *lhs = *rhs;
39 | }
40 |
41 | CONS s2(struct cons *rhs) {
42 | CONS foo = *rhs;
43 | foo = *rhs;
44 | return(foo);
45 | }
46 |
47 | void u1(union header *lhs, HEADER*rhs) {
48 | *lhs = *rhs;
49 | }
50 |
51 | void mem1(CONS *lhs, CONS *rhs) {
52 | memset(lhs, 0, sizeof(CONS));
53 | }
54 |
55 | void mem2(CONS *lhs, CONS *rhs) {
56 | memcpy(lhs, rhs, sizeof(CONS));
57 | }
58 |
59 | void b1(CONS *c) {
60 | c->car = 0;
61 | }
62 |
63 | void b2(long *p[], long *x) {
64 | p[7] = x;
65 | }
66 |
67 | void b3(char *x) {
68 | global_var = x;
69 | }
70 |
71 | void nb1(CONS *c) {
72 | CONS *x;
73 | x = c->car;
74 | b1(x);
75 | }
76 |
77 | void nb2(long p[], long x) {
78 | p[7] = x;
79 | }
80 |
81 | void compound_ptr_assign(long *p[], long *x) {
82 | p[7] += 8;
83 | p[7] -= p[6];
84 | }
85 |
86 |
87 |
--------------------------------------------------------------------------------
/allocate.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 Wade Lawrence Hennessey
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | // Interface to rtgc allocator
19 |
20 | #define RTnopointers ((void *) 0)
21 | #define RTpointers ((void *) 1)
22 | #define RTcustom1 ((void *) 2)
23 | #define RTmetadata ((void *) 3)
24 |
25 | #define RTbeerBash(lhs, rhs) ((lhs) = (rhs))
26 | #define setf_init(lhs, rhs) ((lhs) = (rhs))
27 |
28 | #define INVALID_ADDRESS 0xEF
29 |
30 | typedef long RT_METADATA;
31 |
32 | void *RTallocate(void *metadata, int number_of_bytes);
33 |
34 | void *RTstatic_allocate(void *metadata, int number_of_bytes);
35 |
36 | void *RTwrite_barrier(void *lhs_address, void * rhs);
37 |
38 | void *RTsafe_bash(void *lhs_address, void * rhs);
39 |
40 | void *RTsafe_setfInit(void *lhs_address, void * rhs);
41 |
42 | void *ptrcpy(void *p1, void * p2, int num_bytes);
43 |
44 | void *ptrset(void *p1, int data, int num_bytes);
45 |
46 | void RTinit_heap(size_t first_segment_bytes, size_t static_size);
47 |
48 | int RTpthread_create(pthread_t *thread, const pthread_attr_t *attr,
49 | void *(*start_func) (void *), void *args);
50 |
51 | int rtgc_count(void);
52 |
53 | void RTfull_gc();
54 |
55 | void RTregister_root_scanner(void (*root_scanner)());
56 |
57 | int RTregister_custom_scanner(void (*custom_scanner)(void *low, void *high));
58 |
59 | void RTregister_no_write_barrier_state(void *start, int len);
60 |
61 | void RTtrace_pointer(void *ptr);
62 |
63 | void RTtrace_heap_pointer(void *ptr);
64 |
65 | struct timespec RTtime_diff(struct timespec start, struct timespec end);
66 |
67 | int RTtime_cmp(struct timespec x, struct timespec y);
68 |
69 | extern volatile int RTatomic_gc;
70 | extern int RTpage_power;
71 | extern int RTpage_size;
72 |
--------------------------------------------------------------------------------
/rtglobals.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 Wade Lawrence Hennessey
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | #define _USE_GNU
19 | #define _GNU_SOURCE
20 |
21 |
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include "mem-config.h"
31 | #include "info-bits.h"
32 | #include "mem-internals.h"
33 | #include "allocate.h"
34 |
35 | GROUP_INFO *groups;
36 | PAGE_INFO *pages;
37 | HOLE_PTR empty_pages;
38 |
39 | int RTpage_power = PAGE_POWER;
40 | int RTpage_size = BYTES_PER_PAGE;
41 | SEGMENT *segments;
42 | int total_segments;
43 |
44 | THREAD_INFO *threads;
45 | THREAD_INFO *live_threads;
46 | THREAD_INFO *free_threads;
47 | int total_threads = 0;
48 |
49 | THREAD_STATE *saved_threads;
50 | int total_saved_threads = 0;
51 |
52 | char **global_roots;
53 | int total_global_roots;
54 |
55 | // HEY! only 1 static segment while these are global!
56 | BPTR first_static_ptr;
57 | BPTR last_static_ptr;
58 | BPTR static_frontier_ptr;
59 |
60 | BPTR first_partition_ptr;
61 | BPTR last_partition_ptr;
62 |
63 | #if USE_BIT_WRITE_BARRIER
64 | LPTR RTwrite_vector;
65 | #else
66 | BPTR RTwrite_vector;
67 | #endif
68 | size_t RTwrite_vector_length;
69 |
70 | long total_partition_pages;
71 | int unmarked_color;
72 | int marked_color;
73 | int enable_write_barrier;
74 | volatile long gc_count;
75 |
76 | pthread_key_t thread_key;
77 |
78 | pthread_mutex_t threads_lock;
79 | pthread_mutex_t global_roots_lock;
80 | pthread_mutex_t empty_pages_lock;
81 | // Use locked add instruction instead of a mutex?
82 | pthread_mutex_t static_frontier_ptr_lock;
83 |
84 | sem_t gc_semaphore;
85 | volatile int run_gc = 0;
86 | volatile int RTatomic_gc = 0;
87 |
88 | long *RTno_write_barrier_state_ptr = 0;
89 | long saved_no_write_barrier_state = 0;
90 |
91 |
--------------------------------------------------------------------------------
/info-bits.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 Wade Lawrence Hennessey
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | // Links are divided into a pointer and some low order info bits.
19 | typedef struct gc_header {
20 | struct gc_header *prev;
21 | struct gc_header *next;
22 | } GC_HEADER;
23 |
24 | typedef GC_HEADER *GCPTR;
25 |
26 | #define LINK_INFO_BITS 4
27 | #define LINK_INFO_MASK ((1 << LINK_INFO_BITS) - 1)
28 | #define LINK_POINTER_MASK (~LINK_INFO_MASK)
29 |
30 | #define GET_LINK_POINTER(l) ((GCPTR) (((long) (l)) & LINK_POINTER_MASK))
31 | #define SET_LINK_POINTER(l, value) (RTbeerBash (l, \
32 | (GCPTR) (((long) (l) & LINK_INFO_MASK) \
33 | | (long) value)))
34 |
35 | #define GET_LINK_INFO(l,mask) ((long) l & (mask))
36 | #define SET_LINK_INFO(l,mask,bits) (RTbeerBash (l, \
37 | (GCPTR) (((long) l & ~(mask)) | bits)))
38 |
39 | // GC info bits
40 | #define GC_STORAGE_INFO_MASK (0x3)
41 | #define GC_COLOR_INFO_MASK (0x3)
42 |
43 | #define GET_STORAGE_CLASS(p) (GET_LINK_INFO(p->next, GC_STORAGE_INFO_MASK))
44 | #define SET_STORAGE_CLASS(p,RTclass) (SET_LINK_INFO(p->next,GC_STORAGE_INFO_MASK, RTclass))
45 |
46 | #define GET_COLOR(p) (GET_LINK_INFO(p->prev,GC_COLOR_INFO_MASK))
47 | #define SET_COLOR(p,color) (SET_LINK_INFO(p->prev,GC_COLOR_INFO_MASK,color))
48 |
49 | // use enums for these instead?
50 | #define SC_NOPOINTERS 0
51 | #define SC_POINTERS 1
52 | #define SC_CUSTOM1 2
53 | #define SC_METADATA 3
54 |
55 | #define GENERATION0 0
56 | #define GENERATION1 1
57 | #define GRAY 2
58 | #define GREEN 3 // Could use GREEN=GRAY, but we've got room
59 |
60 | #define WHITEP(p) (GET_COLOR(p) == unmarked_color)
61 | #define BLACKP(p) (GET_COLOR(p) == marked_color)
62 | #define GRAYP(p) (GET_COLOR(p) == GRAY)
63 | #define GREENP(p) (GET_COLOR(p) == GREEN)
64 |
--------------------------------------------------------------------------------
/doc/gc-states.txt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 Wade Lawrence Hennessey
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | # Gray Black Free Free_last White Label
19 | ----------------------------------------------------------------------------------
20 | 0 0 0 0 0 0 Start
21 | 1 0 0 0 0 H Start
22 | 2 0 0 0 H 0 Invalid
23 | 3 0 0 0 H H Invalid
24 | 4 0 0 H 0 0 Invalid
25 | 5 0 0 H 0 H Invalid
26 | 6 0 0 H H 0 Invalid
27 | 7 0 0 H H H Invalid
28 | 8 0 H 0 0 0 Invalid
29 | 9 0 H 0 0 H Invalid
30 | 10 0 H 0 H 0 Only black
31 | 11 0 H 0 H H Black, white
32 | 12 0 H H 0 0 Invalid
33 | 13 0 H H 0 H Invalid
34 | 14 0 H H H 0 Only blk/free
35 | 15 0 H H H H Done
36 | 16 H 0 0 0 0 Invalid
37 | 17 H 0 0 0 H Invalid
38 | 18 H 0 0 H 0 Invalid
39 | 19 H 0 0 H H Invalid
40 | 20 H 0 H 0 0 Invalid
41 | 21 H 0 H 0 H Invalid
42 | 22 H 0 H H 0 Invalid
43 | 23 H 0 H H H Invalid
44 | 24 H H 0 0 0 Invalid
45 | 25 H H 0 0 H Invalid
46 | 26 H H 0 H 0 Done
47 | 27 H H 0 H H Done
48 | 28 H H H 0 0 Invalid
49 | 29 H H H 0 H Invalid
50 | 30 H H H H 0 Done
51 | 31 H H H H H Tracing
52 |
--------------------------------------------------------------------------------
/wbcheck/wb-libclang.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 Wade Lawrence Hennessey
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | // Make: clang -o wb -g wb.c -lclang
19 |
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 |
31 | // YOW! What a hack, but
32 | int isAssignment(CXCursor cursor, CXTranslationUnit tu) {
33 | int result = 0;
34 | CXToken *tokens;
35 | unsigned numTokens;
36 | CXSourceRange range = clang_getCursorExtent(cursor);
37 | clang_tokenize(tu, range, &tokens, &numTokens);
38 | for(unsigned i = 0; i
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 |
32 | static volatile long flag = 0;
33 | static volatile long counter = 0;
34 |
35 | void handler_func(int signum, siginfo_t *siginfo, void *context) {
36 | counter = counter + 1;
37 | }
38 |
39 | struct timespec RTtime_diff(struct timespec end, struct timespec start) {
40 | struct timespec diff;
41 | if ((end.tv_nsec - start.tv_nsec) < 0) {
42 | diff.tv_sec = end.tv_sec - start.tv_sec - 1;
43 | diff.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec;
44 | } else {
45 | diff.tv_sec = end.tv_sec - start.tv_sec;
46 | diff.tv_nsec = end.tv_nsec - start.tv_nsec;
47 | }
48 | return(diff);
49 | }
50 |
51 | void *us_busy_loop(void *arg) {
52 | const int count = 6005;
53 | struct timeval buffer[count];
54 | memset(buffer, 0, count * sizeof(struct timeval));
55 |
56 | int err;
57 | while(0 == flag); // spin waiting for sender to start
58 | for (int i = 0; i < count; i++) {
59 | if (0 != (err = gettimeofday(buffer + i, 0))) {
60 | error(0, err, "gettimeofday failed");
61 | }
62 | }
63 |
64 | for (int i = 5; i < (count - 1); i++) {
65 | struct timeval diff;
66 | timersub(buffer + i + 1, buffer + i, &diff);
67 | printf("%ld,\n", diff.tv_usec);
68 | }
69 | flag = 0;
70 | }
71 |
72 | void *ns_busy_loop(void *arg) {
73 | const int count = 6005;
74 | struct timespec buffer[count];
75 | struct timespec res;
76 | if (0 == clock_getres(CLOCK_THREAD_CPUTIME_ID, &res)) {
77 | fprintf(stderr, "clock resolution is %ld ns\n", res.tv_nsec);
78 | }
79 | memset(buffer, 0, count * sizeof(struct timespec));
80 |
81 | int err;
82 | while(0 == flag); // spin waiting for sender to start
83 | for (int i = 0; i < count; i++) {
84 | // Tried CLOCK_THREAD_CPUTIME_ID and CLOCK_REALTIME
85 | if (0 != (err = clock_gettime(CLOCK_REALTIME, buffer + i))) {
86 | error(0, err, "clock_gettime failed");
87 | }
88 | }
89 |
90 | for (int i = 0; i < (count - 1); i++) {
91 | struct timespec diff = RTtime_diff(buffer[i + 1], buffer[i]);
92 | printf("%ld,\n", diff.tv_nsec);
93 | }
94 | flag = 0;
95 | }
96 |
97 | void send_loop(pthread_t thread) {
98 | int err;
99 | counter = 0;
100 | flag = 1;
101 | for (int i = 0; i < 50; i++) {
102 | //for (int t = 0; t < 5000; t++);
103 |
104 | long next_count = counter + 1;
105 | if (0 != (err = pthread_kill(thread, SIGUSR1))) {
106 | error(0, err, "pthread_kill failed");
107 | }
108 | while (counter < next_count);
109 |
110 | }
111 | void **retval;
112 | //pthread_join(thread, retval);
113 | fprintf(stderr, "exiting send loop, counter is %ld\n", counter);
114 | }
115 |
116 | void init_signals() {
117 | struct sigaction signal_action;
118 | sigset_t set;
119 |
120 | sigemptyset(&set);
121 | sigaddset(&set, SIGUSR1);
122 | // sigprocmask seems to do the same thing as this
123 | if (0 != pthread_sigmask(SIG_UNBLOCK, 0, &set)) {
124 | printf("mask failed!");
125 | }
126 |
127 | memset(&signal_action, 0, sizeof(signal_action));
128 | signal_action.sa_sigaction = handler_func;
129 | signal_action.sa_flags = SA_SIGINFO | SA_RESTART;
130 | sigaction(SIGUSR1, &signal_action, 0);
131 | }
132 |
133 | int main(int argc, char *argv[]) {
134 | pthread_t thread;
135 | init_signals();
136 | pthread_create(&thread, NULL, &ns_busy_loop, 0);
137 | send_loop(thread);
138 | }
139 |
--------------------------------------------------------------------------------
/HEY!:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 Wade Lawrence Hennessey
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | very useful to keep debuginfo up to date
19 | dnf --enablerepo=updates-debuginfo update
20 |
21 | m-x gud-gdb to switch *dedicated* window
22 | c-x c-a c-b to set breakpoint at point line. Who changed this and why?
23 |
24 | service sshd start
25 | // to enable sshd at boot time
26 | systemctl enable sshd.service
27 |
28 |
29 | git clone https://github.com/wadehennessey/rtgc.git
30 | or
31 | git checkout
32 |
33 |
34 | git commit -am "blah"
35 | # then must push commit
36 | git push
37 |
38 |
39 |
40 | use #define _GNU_SOURCE for weird compile problems not seeing decls
41 |
42 |
43 | gdb> info signals
44 | use next instead of continue to switch to diff thread break points
45 |
46 | # these go in .gdbinit
47 | handle SIGUSR1 nostop
48 | set non-stop on
49 |
50 | >set non-stop on
51 | >set non-stop off
52 | >show non-stop
53 | >interrupt will stop the running thread
54 | >break a.c:166 if (j == 1535)
55 |
56 |
57 | wmctrl -r "emacs" -e 1,-1,-1,770,1010
58 |
59 |
60 |
61 | TODO:
62 | -----
63 | verify:
64 | double linked lists correct - (obj->next)->prev == obj, (obj->prev)->next == obj
65 | verify color in header and color in list are the same
66 | verify all _count fields are correct.
67 |
68 | # to limit all threads in ./a to the second core:
69 | taskset 0x10 ./a
70 |
71 | # rr traces are stored in /home/wade/.local/share/rr/
72 |
73 | gdb tui mode in xterm: c-x a
74 |
75 | too see event numbers and start at a specific event number:
76 | rr -M replay -g
77 |
78 | rr dump
79 | rr dump /home/wade/.local/share/rr/a-0/ 39000000
80 |
81 | try gdb cmds:
82 | -------------
83 | checkpoint
84 | info checkpoints
85 | restart
86 | delete
87 | watch -l
88 | when - command shows current event-number
89 |
90 | try rr -c and -e options to increase thread interleave
91 |
92 | # task switch after every N retired branches
93 | rr record -c 2500 ./a
94 |
95 | # I think I recorded "the bug" with this (compile "make debug")
96 | rr record -c 5 ./a
97 |
98 | # to see record options
99 | rr help record
100 | rr help replay
101 |
102 | From rr/src/Scheduler.h
103 | -c default: enum { DEFAULT_MAX_TICKS = 250000 }; // 50ms time slice
104 | -e default: enum { DEFAULT_MAX_EVENTS = 10 };
105 |
106 | To run real-time round-robin:
107 | -----------------
108 | chrt --rr 30 ./a
109 | or
110 | chrt --fifo 30 ./a
111 |
112 | ps -Lo pid,tid,class <./a pid #>
113 |
114 |
115 | To add swap
116 | ------------
117 | su
118 | cd /home
119 | dd if=/dev/zero of=bigswapfile bs=1G count=64
120 | chmod 600 /root/bigswapfile
121 | mkswap bigswapfile
122 | swapon bigswapfile
123 |
124 | in /etc/fstab add:
125 | /home/bigswapfile swap swap defaults 0 0
126 |
127 |
128 | flip
129 | -------
130 | 200ns for the "null" signal and return time. Includes double the "system" time.
131 | Mean 5000 "null" signal and returns per 1ms.
132 |
133 | Time for 1,000,000 "null" kill signal and returns
134 | real 0m0.209s
135 | user 0m0.029s
136 | sys 0m0.389s
137 |
138 | ---------------------------------------------
139 |
140 | try gcc -mcmodel=large to avoid dlopen relocation problems in wcl
141 |
142 | change wcl print level to once for lib, once for compiler. Make it easy to change back.
143 |
144 | Add IF_COUNTING/DEBUG wrappers
145 |
146 | switch *debug-library-config* to *delivery-library-config* when doing build
147 | to speed things up
148 | --------------
149 |
150 | add .gdbinit to rtgc, make ~/.gdbinit a link to rtgc version
151 | add .wclinit to wcl, make ~/.wclinit a link
152 |
153 |
154 |
155 | --------------------------------
156 | clang stuff
157 |
158 | run "clang -### -c a.c" to see full command line the driver generates,
159 | then append "-ast-dump" to the end and run it.
160 |
161 | in build dir "make clang" will just rebuild clang
162 |
163 |
164 | AST Matcher
165 | -------------
166 | NOTE: clang-query only reads file passed to it *ONCE* at startup. File changes
167 | after file is read are *NOT* seen.
168 |
169 | /home/wade/build/bin/clang-query /tmp/w.c --
170 | > set output diag
171 |
172 | ----------
173 | export C_INCLUDE_PATH=/home/wade/build/lib/clang/4.0.0/include
174 |
175 | -----------
176 | Try LIBRARY_PATH env variable to allow removal or librtgc.so from wcl tree
177 |
--------------------------------------------------------------------------------
/mem-internals.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 Wade Lawrence Hennessey
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | #define EMPTY_PAGE ((GPTR) 0)
19 | #define FREE_PAGE ((GPTR) 1)
20 | #define SYSTEM_PAGE ((GPTR) 2)
21 | #define EXTERNAL_PAGE ((GPTR) 3)
22 |
23 | #define HEAP_SEGMENT 0
24 | #define STATIC_SEGMENT 1
25 |
26 | typedef unsigned long * LPTR;
27 | typedef unsigned char * BPTR;
28 |
29 | #define BYTES_PER_PAGE (1 << PAGE_POWER)
30 | #define PAGE_ALIGNMENT_MASK (BYTES_PER_PAGE - 1)
31 | #define PTR_TO_PAGE_INDEX(ptr) ((long) (((BPTR) ptr - first_partition_ptr) >> PAGE_POWER))
32 | #define PAGE_INDEX_TO_PTR(page_index) (first_partition_ptr + ((page_index) << PAGE_POWER))
33 | #define PTR_TO_GROUP(ptr) pages[PTR_TO_PAGE_INDEX(ptr)].group
34 | #define IN_PARTITION(ptr) (((BPTR) ptr >= first_partition_ptr) && ((BPTR) ptr < last_partition_ptr))
35 | #define PAGE_GROUP(ptr) (IN_PARTITION(ptr) ? PTR_TO_GROUP(ptr) : EXTERNAL_PAGE)
36 | #define IN_HEAP(ptr) ((long) PAGE_GROUP(ptr) > (long) EXTERNAL_PAGE)
37 | #define ROUND_DOWN_TO_PAGE(ptr) ((BPTR) (((long) ptr & ~PAGE_ALIGNMENT_MASK)))
38 | #define ROUND_UP_TO_PAGE(ptr) (ROUND_DOWN_TO_PAGE(ptr) + BYTES_PER_PAGE)
39 |
40 | #define MIN_GROUP_INDEX 5 // yields min 32 byte objects on x86_64
41 | #define MAX_GROUP_INDEX 24 // yields max 16 megabyte objects
42 | #define MIN_GROUP_SIZE (1 << MIN_GROUP_INDEX)
43 | #define MAX_GROUP_SIZE ( 1 << MAX_GROUP_INDEX)
44 | #define NUMBER_OF_GROUPS (MAX_GROUP_INDEX - MIN_GROUP_INDEX + 1)
45 | #define LONG_ALIGNMENT (sizeof(long) - 1)
46 | #define ROUND_UPTO_LONG_ALIGNMENT(n) (((((n) - 1)) & ~LONG_ALIGNMENT) + \
47 | sizeof(long))
48 | #define BITS_PER_BYTE 8
49 | #define BITS_PER_LONG (BITS_PER_BYTE * sizeof(long))
50 |
51 |
52 | #define METADATAP(ptr) (((void *) ptr) > RTpointers)
53 |
54 | #define MIN(x,y) ((x < y) ? x : y)
55 | #define MAX(x,y) ((x > y) ? x : y)
56 | #define SWAP(x,y) {int tmp = x; x = y; y = tmp;}
57 |
58 | typedef struct group_info {
59 | int size;
60 | int index;
61 |
62 | GCPTR last; // used in rtgc and rtalloc
63 | GCPTR free; // used in rtgc and rtalloc
64 | GCPTR gray; // only used in rtgc
65 | GCPTR black; // only used in rtgc and rtalloc
66 | GCPTR white; // only used in rtgc
67 |
68 | int white_count; // only used in rtgc
69 | int black_scanned_count; // used in rtgc
70 | int black_alloc_count; // used in rtgc and rtalloc
71 |
72 | pthread_mutex_t free_lock; // used in rtgc and rtalloc
73 | pthread_mutex_t black_and_last_lock; // used in rtgc and rtalloc
74 | } GROUP_INFO;
75 |
76 | typedef GROUP_INFO *GPTR;
77 |
78 | typedef struct segment {
79 | BPTR first_segment_ptr;
80 | BPTR last_segment_ptr;
81 | int segment_page_count;
82 | int type;
83 | } SEGMENT;
84 |
85 | typedef struct hole {
86 | long page_count; // only used in rtalloc
87 | struct hole *next; // only used in rtalloc
88 | } HOLE;
89 |
90 | typedef HOLE *HOLE_PTR;
91 |
92 | typedef struct page_info {
93 | GCPTR base;
94 | GPTR group;
95 | } PAGE_INFO;
96 |
97 | typedef PAGE_INFO *PPTR;
98 |
99 | typedef struct thread_state {
100 | gregset_t registers; // NREG is 23 on x86_64
101 | char *saved_stack_base; // This is the LOWEST addressable byte
102 | int saved_stack_size;
103 | } THREAD_STATE;
104 |
105 | typedef struct thread_info {
106 | pthread_t pthread;
107 | long long *stack_base; // This is the LOWEST addressable byte of the stack
108 | int stack_size;
109 | char *stack_bottom; // HIGHEST address seen when thread started
110 |
111 | int saved_thread_index; // copied stack and register states
112 |
113 | struct timeval max_pause_tv, total_pause_tv;
114 | struct thread_info *next;
115 | // These fields are only used at thread startup time
116 | void *(*start_func) (void *);
117 | char *args;
118 | volatile long started;
119 | } THREAD_INFO;
120 |
121 | typedef struct counter {
122 | int count;
123 | pthread_mutex_t lock;
124 | pthread_cond_t cond;
125 | } COUNTER;
126 |
127 | void scan_object(GCPTR ptr, int total_size);
128 | void RTinit_empty_pages(int first_page, int page_count, int type);
129 | void rtgc_loop();
130 | void init_signals_for_rtgc();
131 | void lock_all_free_locks();
132 | void unlock_all_free_locks();
133 | int stop_all_mutators_and_save_state();
134 | void RTroom();
135 | void init_realtime_gc(void);
136 | void Debugger(char *msg);
137 | void *RTbig_malloc(size_t size);
138 | void RTcopy_regs_to_stack(BPTR regptr);
139 | void out_of_memory(char *space_name, int size);
140 | void register_global_root(void *root);
141 | void counter_init(COUNTER *c);
142 | int counter_zero(COUNTER *c);
143 | int counter_increment(COUNTER *c);
144 | void counter_wait_threshold(COUNTER *c, int threshold);
145 | void locked_byte_or(unsigned char *x, unsigned char y);
146 | void locked_long_or(unsigned long *x, unsigned long y);
147 | void locked_long_and(unsigned long *x, unsigned long y);
148 | void locked_long_inc(volatile unsigned long *x);
149 | void coalesce_all_free_pages();
150 |
151 | extern BPTR first_partition_ptr;
152 | extern BPTR last_partition_ptr;
153 | extern BPTR first_static_ptr;
154 | extern BPTR last_static_ptr;
155 | extern BPTR static_frontier_ptr;
156 |
157 | extern GROUP_INFO *groups;
158 | extern PAGE_INFO *pages;
159 | extern HOLE_PTR empty_pages;
160 | extern volatile long gc_count;
161 |
162 | extern SEGMENT *segments;
163 | extern int total_segments;
164 |
165 | extern THREAD_INFO *threads;
166 | extern THREAD_INFO *live_threads;
167 | extern THREAD_INFO *free_threads;
168 | extern int total_threads;
169 |
170 | extern THREAD_STATE *saved_threads;
171 | extern int total_saved_threads;
172 |
173 | extern long total_partition_pages;
174 | extern int unmarked_color;
175 | extern int marked_color;
176 | extern int enable_write_barrier;
177 |
178 | extern pthread_key_t thread_key;
179 | extern char **global_roots;
180 | extern int total_global_roots;
181 | extern pthread_mutex_t threads_lock;
182 | extern pthread_mutex_t empty_pages_lock;
183 | extern pthread_mutex_t global_roots_lock;
184 | extern pthread_mutex_t static_frontier_ptr_lock;
185 |
186 | extern sem_t gc_semaphore;
187 | extern volatile int run_gc;
188 |
189 | #if USE_BIT_WRITE_BARRIER
190 | extern LPTR RTwrite_vector;
191 | #else
192 | extern BPTR RTwrite_vector;
193 | #endif
194 | extern size_t RTwrite_vector_length;
195 |
196 | extern long *RTno_write_barrier_state_ptr;
197 | extern long saved_no_write_barrier_state;
198 |
199 | #define LOCK(lock) pthread_mutex_lock(&lock)
200 | #define UNLOCK(lock) pthread_mutex_unlock(&lock)
201 | #define WITH_LOCK(lock, code) LOCK(lock); \
202 | code \
203 | UNLOCK(lock);
204 |
--------------------------------------------------------------------------------
/a.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 Wade Lawrence Hennessey
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | // A simple multi-mutator test program for rtgc
19 |
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include "info-bits.h"
34 | #include "mem-config.h"
35 | #include "mem-internals.h"
36 | #include "allocate.h"
37 |
38 | // http://www.textfiles.com/etext/AUTHORS/DOYLE/ for text files
39 |
40 | typedef struct node {
41 | char *word;
42 | int count;
43 | struct node *lesser;
44 | struct node *greater;
45 | } NODE;
46 |
47 | RT_METADATA NODE_md[] = {sizeof(NODE),
48 | offsetof(NODE, word),
49 | offsetof(NODE, lesser),
50 | offsetof(NODE, greater),
51 | -1};
52 |
53 | NODE *new_node(char *word, NODE *lesser, NODE *greater) {
54 | //NODE *node = (NODE *) RTallocate(RTpointers, sizeof(NODE));
55 | NODE *node = (NODE *) RTallocate(NODE_md, 1);
56 | node->word = word;
57 | node->count = 1;
58 | setf_init(node->lesser, lesser);
59 | setf_init(node->greater, greater);
60 | return node;
61 | }
62 |
63 | char *new_word(char *buffer, int i) {
64 | char *word = RTallocate(RTnopointers, (i + 1));
65 | buffer[i] = '\0';
66 | strcpy(word, buffer);
67 | return(word);
68 | }
69 |
70 | char *read_word(FILE *f) {
71 | int c;
72 | char buffer[1024];
73 | int i = 0;
74 | do {
75 | c = fgetc(f);
76 | if (EOF == c) {
77 | return(NULL);
78 | }
79 | } while (!isalnum(c));
80 | do {
81 | buffer[i] = c;
82 | i = i + 1;
83 | c = fgetc(f);
84 | if (EOF == c) {
85 | return(new_word(buffer, i));
86 | }
87 | } while (isalnum(c));
88 | return(new_word(buffer, i));
89 | }
90 |
91 | void insert_node(NODE *next, char *word, int level) {
92 | int result = strcmp(word, next->word);
93 | if (level < 1844) {
94 | if (0 == result) {
95 | next->count = next->count + 1;
96 | } else {
97 | if (result < 0) {
98 | if (NULL == next->lesser) {
99 | setf_init(next->lesser, new_node(word, 0, 0));
100 | } else {
101 | result = strcmp(word, (next->lesser)->word);
102 | if (0 == result) {
103 | (next->lesser)->count = (next->lesser)->count +1;
104 | } else {
105 | if (result < 0) {
106 | insert_node(next->lesser, word, level + 1);
107 | } else {
108 | // Insert new node between next and lesser
109 | NODE *new = new_node(word, next->lesser, 0);
110 | RTwrite_barrier(&(next->lesser), new);
111 | }
112 | }
113 | }
114 | } else {
115 | if (NULL == next->greater) {
116 | setf_init(next->greater, new_node(word, 0, 0));
117 | } else {
118 | result = strcmp(word, (next->greater)->word);
119 | if (0 == result) {
120 | (next->greater)->count = (next->greater)->count + 1;
121 | } else {
122 | if (result > 0) {
123 | insert_node(next->greater, word, level + 1);
124 | } else {
125 | // Insert new node between next and greater
126 | NODE *new = new_node(word, 0, next->greater);
127 | RTwrite_barrier(&(next->greater), new);
128 | }
129 | }
130 | }
131 | }
132 | }
133 | } else {
134 | Debugger("Infinite recursion!\n");
135 | }
136 | }
137 |
138 | NODE *build_word_tree(char *filename) {
139 | FILE *f = fopen(filename, "r");
140 | if (f == NULL) {
141 | printf("Cannot open file %s\n", filename);
142 | return(NULL);
143 | } else {
144 | char *word;
145 | NODE *root;
146 | word = read_word(f);
147 | // No write_barrier needed - intialzing write to the stack
148 | root = new_node(word, NULL, NULL);
149 | while (NULL != (word = read_word(f))) {
150 | insert_node(root, word, 0);
151 | }
152 | fclose(f);
153 | return(root);
154 | }
155 | }
156 |
157 | // Walk word tree, print if verbose is not 0. Return total word count.
158 | int walk_word_tree(NODE *n, int verbose) {
159 | int r, count;
160 | if (NULL == n) {
161 | count = 0;
162 | } else {
163 | if (0 != n->lesser) {
164 | r = strcmp(n->word, (n->lesser)->word);
165 | assert(r > 0);
166 | }
167 | if (0 != n->greater) {
168 | r = strcmp(n->word, (n->greater)->word);
169 | assert(r < 0);
170 | }
171 | count = n->count;
172 | count = count + walk_word_tree(n->lesser, verbose);
173 | if (0 != verbose) {
174 | printf("%d %s\n", n->count, n->word);
175 | }
176 | count = count + walk_word_tree(n->greater, verbose);
177 | }
178 | return(count);
179 | }
180 |
181 | void *start_word_count(void *arg) {
182 | long count = (long) arg;
183 | long i = 0;
184 |
185 | pthread_t thread = pthread_self();
186 | while (i < count) {
187 | char top;
188 | NODE *root = build_word_tree("redhead.txt");
189 | assert(9317 == walk_word_tree(root, 0));
190 | if (0 == (i % 25)) {
191 | printf("[%p] %d word counts\n", thread, i);
192 | }
193 | i = i + 1;
194 | }
195 | }
196 |
197 | /*
198 | void *make_threads(void *arg) {
199 | pthread_t thread;
200 | for (long i = 0; i < 3; i++) {
201 | pthread_t thread;
202 | RTpthread_create(&thread, NULL, &start_word_count, (void *) i);
203 | }
204 | }
205 | */
206 |
207 | void *make_threads(void *arg) {
208 | long counts_per_thread = 5000;
209 | long total_threads_created = 0;
210 | while (1) {
211 | // should make total_threads a condition variable
212 | while (total_threads < 4) {
213 | pthread_t thread;
214 | int err;
215 | if (0!= (err = RTpthread_create(&thread,
216 | NULL,
217 | &start_word_count,
218 | (void *) counts_per_thread))) {
219 | printf("Thread create failure, err = %d\n", err);
220 | Debugger("Thread create failure");
221 | } else {
222 | total_threads_created = total_threads_created + 1;
223 | if (0 == (total_threads_created % 25)) {
224 | printf("**************** %d threads created (%d total word counts)\n",
225 | total_threads_created,
226 | total_threads_created * counts_per_thread);
227 | }
228 | }
229 | }
230 | sched_yield();
231 | }
232 | }
233 |
234 | int main(int argc, char *argv[]) {
235 | RTatomic_gc = 0;
236 | // When using RTatomic_gc = 1 we need about 4x them minimum heap size we need
237 | // when using RTatomic_gc = 0, otherwise we'll get "out of memory Heap"
238 | // errors.
239 | RTinit_heap((1L << 23), 1L << 18);
240 | pthread_t thread;
241 | RTpthread_create(&thread, NULL, &make_threads, 0);
242 | rtgc_loop();
243 | }
244 |
--------------------------------------------------------------------------------
/rtutil.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 Wade Lawrence Hennessey
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include "mem-config.h"
31 | #include "info-bits.h"
32 | #include "mem-internals.h"
33 | #include "allocate.h"
34 |
35 | // grows *DOWN*, not up
36 | void *RTbig_malloc(size_t bytes) {
37 | BPTR p = (mmap(0,
38 | bytes,
39 | PROT_EXEC | PROT_READ | PROT_WRITE,
40 | MAP_PRIVATE | MAP_ANONYMOUS,
41 | 0,
42 | 0));
43 | // printf("RTbig_malloc of %ld bytes returning pointer %p\n", bytes, p);
44 | return(p);
45 | }
46 |
47 | void out_of_memory(char *msg, int bytes_needed) {
48 | printf("out of memory %s %d\n", msg, bytes_needed);
49 | Debugger(0);
50 | }
51 |
52 | void Debugger(char *msg) {
53 | if (0 != msg) {
54 | printf(msg);
55 | } else {
56 | printf("Hey! rtgc called the debugger - fix me!\n");
57 | }
58 | fflush(stdout);
59 | raise(SIGSTOP);
60 | }
61 |
62 | void copy_test(size_t len) {
63 | struct timeval start_tv, end_tv;
64 | char *src = malloc(len);
65 | char *dest = malloc(len);
66 |
67 | gettimeofday(&start_tv, 0);
68 | memcpy(dest, src, len);
69 | gettimeofday(&end_tv, 0);
70 | printf("start: %d sec, %d usec\n", start_tv.tv_sec, start_tv.tv_usec);
71 | printf("end: %d sec, %d usec\n", end_tv.tv_sec, end_tv.tv_usec);
72 | printf("elapsed: %d\n", end_tv.tv_usec - start_tv.tv_usec);
73 | free(src);
74 | free(dest);
75 | }
76 |
77 | void counter_init(COUNTER *c) {
78 | c->count = 0;
79 | pthread_mutex_init(&(c->lock), NULL);
80 | pthread_cond_init(&(c->cond), NULL);
81 | }
82 |
83 | int counter_zero(COUNTER *c) {
84 | pthread_mutex_lock(&(c->lock));
85 | c->count = 0;
86 | int err = pthread_cond_broadcast(&(c->cond));
87 | if (0 != err) {
88 | printf("pthread_cond_broadcast failed with %d!\n", err);
89 | }
90 | int val = c->count;
91 | //printf("counter zero\n");
92 | pthread_mutex_unlock(&(c->lock));
93 | return(val);
94 | }
95 |
96 | int counter_increment(COUNTER *c) {
97 | pthread_mutex_lock(&(c->lock));
98 | // why can't we just say c->count = c->count + 1 ?
99 | int val = c->count;
100 | val = val + 1;
101 | c->count = val;
102 | int err = pthread_cond_broadcast(&(c->cond));
103 | if (0 != err) {
104 | printf("pthread_cond_broadcast failed with %d!\n", err);
105 | }
106 | //printf("counter increment\n");
107 | fflush(stdout);
108 | pthread_mutex_unlock(&(c->lock));
109 | return(val);
110 | }
111 |
112 | void counter_wait_threshold(COUNTER *c, int threshold) {
113 | pthread_mutex_lock(&(c->lock));
114 | while (c->count < threshold) {
115 | pthread_cond_wait(&(c->cond), &(c->lock));
116 | }
117 | // now c->count >= threshold
118 | pthread_mutex_unlock(&(c->lock));
119 | }
120 |
121 | // useful with clock_gettime
122 | struct timespec RTtime_diff(struct timespec end, struct timespec start) {
123 | struct timespec diff;
124 | if ((end.tv_nsec - start.tv_nsec) < 0) {
125 | diff.tv_sec = end.tv_sec - start.tv_sec - 1;
126 | diff.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec;
127 | } else {
128 | diff.tv_sec = end.tv_sec - start.tv_sec;
129 | diff.tv_nsec = end.tv_nsec - start.tv_nsec;
130 | }
131 | return(diff);
132 | }
133 |
134 | int RTtime_cmp(struct timespec a, struct timespec b) {
135 | if (a.tv_sec == b.tv_sec) {
136 | return(a.tv_nsec > b.tv_nsec);
137 | } else {
138 | return(a.tv_sec > b.tv_sec);
139 | }
140 | }
141 |
142 |
143 | static
144 | int verify_white_count(GPTR group) {
145 | GCPTR ptr = group->white;
146 | int count = 0;
147 | while (ptr != NULL) {
148 | if (group->size < BYTES_PER_PAGE) {
149 | if ((((long) (GET_LINK_POINTER(ptr->prev)) % group->size) != 0) ||
150 | (((long) (GET_LINK_POINTER(ptr->next)) % group->size) != 0)) {
151 | Debugger("Bad gchdr\n");
152 | }
153 | }
154 | ptr = GET_LINK_POINTER(ptr->next);
155 | count = count + 1;
156 | }
157 | if (group->white_count != count) {
158 | Debugger("incorrect white_count\n");
159 | }
160 | }
161 |
162 | static
163 | void verify_white_counts() {
164 | for (int i = MIN_GROUP_INDEX; i <= MAX_GROUP_INDEX; i++) {
165 | GPTR group = &groups[i];
166 | verify_white_count(group);
167 | }
168 | }
169 |
170 | #define FINALIZE_RING_SIZE (1 << 16)
171 | #define FINALIZE_RING_MASK (FINALIZE_RING_SIZE - 1)
172 |
173 | unsigned long finalize_head;
174 | unsigned long finalize_tail;
175 | void *finalize_ring[FINALIZE_RING_SIZE];
176 | pthread_mutex_t finalize_lock;
177 | pthread_cond_t finalize_cond_empty;
178 | pthread_cond_t finalize_cond_full;
179 |
180 | void finalize_add(void *obj) {
181 | pthread_mutex_lock(&finalize_lock);
182 | while ((finalize_tail + FINALIZE_RING_SIZE) > finalize_head) {
183 | pthread_cond_wait(&finalize_cond_full, &finalize_lock);
184 | }
185 | finalize_ring[finalize_head & FINALIZE_RING_MASK] = obj;
186 | finalize_head = finalize_head + 1;
187 | pthread_cond_signal(&finalize_cond_empty);
188 | pthread_mutex_unlock(&finalize_lock);
189 | }
190 |
191 | void *finalize_remove() {
192 | pthread_mutex_lock(&finalize_lock);
193 | while (finalize_tail == finalize_head) {
194 | pthread_cond_wait(&finalize_cond_empty, &finalize_lock);
195 | }
196 | void *object = finalize_ring[finalize_tail & FINALIZE_RING_MASK];
197 | finalize_tail = finalize_tail + 1;
198 | pthread_cond_signal(&finalize_cond_full);
199 | pthread_mutex_unlock(&finalize_lock);
200 | }
201 |
202 | void finalize_init() {
203 | pthread_mutex_init(&finalize_lock, NULL);
204 | pthread_cond_init(&finalize_cond_empty, NULL);
205 | pthread_cond_init(&finalize_cond_full, NULL);
206 | finalize_head = 0;
207 | finalize_tail = 0;
208 | }
209 |
210 | void timespec_test () {
211 | struct timespec res, start_time, end_time, elapsed;
212 | if (0 == clock_getres(CLOCK_REALTIME, &res)) {
213 | clock_gettime(CLOCK_REALTIME, &start_time);
214 | clock_gettime(CLOCK_REALTIME, &end_time);
215 | elapsed = RTtime_diff(start_time, end_time);
216 | printf("Elapsed: %lld.%.9ld\n", elapsed.tv_sec, elapsed.tv_nsec);
217 | }
218 | }
219 |
220 | void eat_line(FILE *f) {
221 | char c;
222 | do {
223 | c = fgetc(f);
224 | } while (c != '\n');
225 | }
226 |
227 | // read_maps_file(argv[0]);
228 | // need to register general path prefixes to match
229 | // and then have this call register_global_root_section for each prefix match
230 | void read_maps_file(char *executable_path) {
231 | pid_t pid = getpid();
232 | char maps_path[128];
233 |
234 | sprintf(maps_path, "/proc/%d/maps", pid);
235 | FILE *maps = fopen(maps_path, "r");
236 | long low, high;
237 | int offset, inode;
238 | char perms[5], dev[6], path[1024];
239 | int count = 0;
240 | while (EOF != count) {
241 | count = fscanf(maps,
242 | "%lx-%lx %s %x %s %x",
243 | &low, &high, &perms, &offset, &dev, &inode);
244 | if (EOF != count) {
245 | //printf("%lx-%lx %s %08x %s %x\n", low, high, perms, offset, dev, inode);
246 | if (0 == inode) {
247 | eat_line(maps);
248 | } else {
249 | count = fscanf(maps, " %s", &path);
250 | if ((0 == strcmp(&(perms[0]), "rw-p")) &&
251 | (0 == strcmp(&(path[0]), executable_path))) {
252 | printf("roots start at %x, length %x\n", low, high - low);
253 | }
254 | }
255 | }
256 | }
257 | }
258 |
259 |
260 |
--------------------------------------------------------------------------------
/rtstop.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 Wade Lawrence Hennessey
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | #define _GNU_SOURCE
19 |
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include "info-bits.h"
32 | #include "mem-config.h"
33 | #include "mem-internals.h"
34 | #include "allocate.h"
35 |
36 | /*
37 | Tried using condition variables to synch handlers and gc. Always deadlocked after
38 | running a long time. Then discovered this from the pthread_cond_broadcast
39 | man page:
40 |
41 | int pthread_cond_broadcast(pthread_cond_t *cond);
42 | int pthread_cond_signal(pthread_cond_t *cond);
43 |
44 | It is not safe to use the pthread_cond_signal() function in a signal
45 | handler that is invoked asynchronously. Even if it were safe, there
46 | would still be a race between the test of the Boolean
47 | pthread_cond_wait() that could not be efficiently eliminated.
48 |
49 | Mutexes and condition variables are thus not suitable for releasing a
50 | waiting thread by signaling from code running in a signal handler.
51 |
52 | New method uses sig_atomic_t flags and locked increment instructions.
53 | */
54 |
55 | // Integers safe to read and set in signal handler
56 | // static volatile sig_atomic_t volatile is ESSENTIAL,
57 | // or -O2 optimizaions break things
58 | static volatile long entered_handler_count = 0;
59 | static volatile long copied_stack_count = 0;
60 |
61 | // see /usr/include/sys/ucontext.h for more details
62 | void print_registers(gregset_t *gregs) {
63 | printf("REG_R8 %llx\n", (*gregs)[REG_R8]);
64 | printf("REG_R9 %llx\n", (*gregs)[REG_R9]);
65 | printf("REG_R10 %llx\n", (*gregs)[REG_R10]);
66 | printf("REG_R11 %llx\n", (*gregs)[REG_R11]);
67 | printf("REG_R12 %llx\n", (*gregs)[REG_R12]);
68 | printf("REG_R13 %llx\n", (*gregs)[REG_R13]);
69 | printf("REG_R14 %llx\n", (*gregs)[REG_R14]);
70 | printf("REG_R15 %llx\n", (*gregs)[REG_R15]);
71 |
72 | printf("REG_RDI %llx\n", (*gregs)[REG_RDI]);
73 | printf("REG_RSI %llx\n", (*gregs)[REG_RSI]);
74 | printf("REG_RBP %llx\n", (*gregs)[REG_RBP]);
75 | printf("REG_RBX %llx\n", (*gregs)[REG_RBX]);
76 | printf("REG_RDX %llx\n", (*gregs)[REG_RDX]);
77 | printf("REG_RAX %llx\n", (*gregs)[REG_RAX]);
78 | printf("REG_RCX %llx\n", (*gregs)[REG_RCX]);
79 | printf("REG_RSP %llx\n", (*gregs)[REG_RSP]);
80 | printf("REG_RIP %llx\n", (*gregs)[REG_RIP]);
81 |
82 | printf("REG_EFL %llx\n", (*gregs)[REG_EFL]);
83 | // Actually short cs, gs, fs, __pad0.
84 | printf("REG_CSGSFS %llx\n", (*gregs)[REG_CSGSFS]);
85 | printf("REG_ERR %llx\n", (*gregs)[REG_ERR]);
86 | printf("REG_TRAPNO %llx\n", (*gregs)[REG_TRAPNO]);
87 | printf("REG_OLDMASK %llx\n", (*gregs)[REG_OLDMASK]);
88 | printf("REG_CR2 %llx\n", (*gregs)[REG_CR2]);
89 | }
90 |
91 | void gc_flip_action_func(int signum, siginfo_t *siginfo, void *context) {
92 | THREAD_INFO *thread;
93 | struct timeval start_tv, end_tv, pause_tv;
94 | long current_gc_count = gc_count;
95 |
96 | // We cannot be in the middle of an allocation at this point because
97 | // the gc holds all the group free_locks
98 | gettimeofday(&start_tv, 0);
99 | locked_long_inc(&entered_handler_count);
100 | if (0 == (thread = pthread_getspecific(thread_key))) {
101 | printf("pthread_getspecific failed!\n");
102 | } else {
103 | ucontext_t *ucontext = (ucontext_t *) context;
104 | mcontext_t *mcontext = &(ucontext->uc_mcontext);
105 | gregset_t *gregs = &(mcontext->gregs);
106 | memcpy(&(saved_threads[thread->saved_thread_index].registers),
107 | gregs,
108 | sizeof(gregset_t));
109 |
110 | // real interrupted stack pointer is saved in the RSP register
111 | char *stack_top = (char *) (*gregs)[REG_RSP];
112 | long live_stack_size = thread->stack_bottom - stack_top;
113 |
114 | // Be careful here, must copy from lowest to highest address
115 | // in both real stack and saved stack
116 | memcpy(saved_threads[thread->saved_thread_index].saved_stack_base,
117 | stack_top,
118 | live_stack_size);
119 | saved_threads[thread->saved_thread_index].saved_stack_size = live_stack_size;
120 | locked_long_inc(&copied_stack_count);
121 |
122 | gettimeofday(&end_tv, 0);
123 | timersub(&end_tv, &start_tv, &pause_tv);
124 | timeradd(&(thread->total_pause_tv),
125 | &pause_tv,
126 | &(thread->total_pause_tv));
127 | if timercmp(&pause_tv, &(thread->max_pause_tv), >) {
128 | thread->max_pause_tv = pause_tv;
129 | }
130 | }
131 |
132 | if (1 == RTatomic_gc) {
133 | while (gc_count == (current_gc_count + 2)) {
134 | sched_yield();
135 | }
136 | }
137 | }
138 |
139 | void init_signals_for_rtgc() {
140 | struct sigaction signal_action;
141 | sigset_t set;
142 |
143 | sigemptyset(&set);
144 | sigaddset(&set, FLIP_SIGNAL);
145 | // sigprocmask seems to do the same thing as this
146 | if (0 != pthread_sigmask(SIG_UNBLOCK, 0, &set)) {
147 | printf("mask failed!");
148 | }
149 |
150 | memset(&signal_action, 0, sizeof(signal_action));
151 | signal_action.sa_sigaction = gc_flip_action_func;
152 | signal_action.sa_flags = SA_SIGINFO | SA_RESTART;
153 | sigaction(FLIP_SIGNAL, &signal_action, 0);
154 | }
155 |
156 | void lock_all_free_locks() {
157 | for (int i = MIN_GROUP_INDEX; i <= MAX_GROUP_INDEX; i++) {
158 | GPTR group = &groups[i];
159 | pthread_mutex_lock(&(group->free_lock));
160 | }
161 | }
162 |
163 | void unlock_all_free_locks() {
164 | for (int i = MIN_GROUP_INDEX; i <= MAX_GROUP_INDEX; i++) {
165 | GPTR group = &groups[i];
166 | pthread_mutex_unlock(&(group->free_lock));
167 | }
168 | }
169 |
170 | // Return total number of mutators stopped
171 | int stop_all_mutators_and_save_state() {
172 | // stop the world and copy all stack and register state in each live thread
173 | entered_handler_count = 0;
174 | copied_stack_count = 0;
175 | pthread_mutex_lock(&threads_lock);
176 | int total_threads_to_halt = 0;
177 | THREAD_INFO *thread = live_threads;
178 | while (thread != NULL) {
179 | thread->saved_thread_index = total_threads_to_halt;
180 | saved_threads[total_threads_to_halt].saved_stack_size = 0;
181 |
182 | total_threads_to_halt = total_threads_to_halt + 1;
183 | int err = pthread_kill(thread->pthread, FLIP_SIGNAL);
184 | if (0 != err) {
185 | if (ESRCH == err) {
186 | // Try to call free_thread here? Probably not.
187 | // This should have been done correctly on pthread exit, no
188 | // matter how it occurred.
189 | Debugger("Not a valid thread handle\n");
190 | } else {
191 | Debugger("pthread_kill failed!");
192 | }
193 | }
194 | thread = thread->next;
195 | }
196 |
197 | if (0 != RTno_write_barrier_state_ptr) {
198 | saved_no_write_barrier_state = *RTno_write_barrier_state_ptr;
199 | }
200 |
201 | while (entered_handler_count != total_threads_to_halt) {
202 | sched_yield();
203 | }
204 |
205 | enable_write_barrier = 1;
206 | SWAP(marked_color,unmarked_color);
207 | unlock_all_free_locks();
208 |
209 | // Busy wait to start gc cycle until all thread stacks are copied
210 | while (copied_stack_count != total_threads_to_halt) {
211 | sched_yield();
212 | }
213 | // all stacks and registers should be copied at this point
214 | assert(total_threads_to_halt == copied_stack_count);
215 | // Allow creation of new threads now
216 | pthread_mutex_unlock(&threads_lock);
217 |
218 | // We could return this and pass it around, but what's the point.
219 | // Its unique global info used once per gc cycle which saved_threads
220 | // have state to scan.
221 | total_saved_threads = total_threads_to_halt;
222 | }
223 |
224 |
--------------------------------------------------------------------------------
/wbcheck/wb.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 Wade Lawrence Hennessey
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | // Run: ./wb filename.c --
19 |
20 | #include
21 | #include
22 |
23 | #include "clang/AST/AST.h"
24 | #include "clang/AST/ASTConsumer.h"
25 | #include "clang/ASTMatchers/ASTMatchers.h"
26 | #include "clang/ASTMatchers/ASTMatchFinder.h"
27 | #include "clang/Frontend/CompilerInstance.h"
28 | #include "clang/Frontend/FrontendActions.h"
29 | #include "clang/Tooling/CommonOptionsParser.h"
30 | #include "clang/Tooling/Tooling.h"
31 | #include "clang/Rewrite/Core/Rewriter.h"
32 | #include "clang/Lex/Lexer.h"
33 | //#include "llvm/Support/raw_ostream.h"
34 |
35 | using namespace clang;
36 | using namespace clang::ast_matchers;
37 | using namespace clang::driver;
38 | using namespace clang::tooling;
39 |
40 | static llvm::cl::OptionCategory MatcherSampleCategory("warn");
41 |
42 | int binop_strings(Rewriter &Rewrite,
43 | const BinaryOperator *Assign,
44 | std::string *lhs_text,
45 | std::string *rhs_text) {
46 | LangOptions lopt;
47 | SourceManager &sm = Rewrite.getSourceMgr();
48 | SourceLocation lhs_b(Assign->getLHS()->getLocStart());
49 | SourceLocation lhs_e(Lexer::getLocForEndOfToken(Assign->getLHS()->getLocEnd(),
50 | 0,
51 | sm,
52 | lopt));
53 | SourceLocation rhs_b(Assign->getRHS()->getLocStart());
54 | SourceLocation rhs_e(Lexer::getLocForEndOfToken(Assign->getRHS()->getLocEnd(),
55 | 0,
56 | sm,
57 | lopt));
58 |
59 | *lhs_text = std::string(sm.getCharacterData(lhs_b),
60 | sm.getCharacterData(lhs_e) -
61 | sm.getCharacterData(lhs_b));
62 | *rhs_text = std::string(sm.getCharacterData(rhs_b),
63 | sm.getCharacterData(rhs_e) -
64 | sm.getCharacterData(rhs_b));
65 | return(sm.getCharacterData(rhs_e) - sm.getCharacterData(lhs_b));
66 | }
67 |
68 | void warn(Rewriter &Rewrite,
69 | const BinaryOperator *Assign,
70 | std::string warning) {
71 | SourceManager &sm = Rewrite.getSourceMgr();
72 | LangOptions lopt;
73 | SourceLocation b(Assign->getLHS()->getLocStart());
74 |
75 | unsigned int line = sm.getPresumedLineNumber(b, 0);
76 | unsigned int col = sm.getPresumedColumnNumber(b, 0);
77 | StringRef filename = sm.getBufferName(b, 0);
78 |
79 | std::cout << filename.data() << " ";
80 | std::cout << "line:" << line << ",col:" << col << warning << std::endl;
81 | }
82 |
83 | class AssignHandler : public MatchFinder::MatchCallback {
84 | public:
85 | AssignHandler(Rewriter &Rewrite) : Rewrite(Rewrite) {}
86 |
87 | virtual void run(const MatchFinder::MatchResult &Result) {
88 | const BinaryOperator *Assign = Result.Nodes.getNodeAs("assign");
89 | std::string lhs_text, rhs_text;
90 | int assign_length = binop_strings(Rewrite, Assign, &lhs_text, &rhs_text);
91 | std::string rewrite;
92 | if (Assign->isCompoundAssignmentOp(Assign->getOpcode())) {
93 | std::string op = Assign->getOpcodeStr(Assign->getOpcode()).data();
94 | rewrite = "*** " + op + " with LHS of type pointer***";
95 | } else {
96 | rewrite = "write_barrier(&(" + lhs_text + "), " + rhs_text + ")";
97 | }
98 | Rewrite.ReplaceText(Assign->getLHS()->getLocStart(),
99 | assign_length,
100 | rewrite);
101 | }
102 |
103 | private:
104 | Rewriter &Rewrite;
105 | };
106 |
107 | class MemcpyHandler : public MatchFinder::MatchCallback {
108 | public:
109 | MemcpyHandler(Rewriter &Rewrite) : Rewrite(Rewrite) {}
110 |
111 | virtual void run(const MatchFinder::MatchResult &Result) {
112 | const CallExpr *Memcpy = Result.Nodes.getNodeAs("memcpy");
113 | Rewrite.InsertText(Memcpy->getLocStart(), "RT", true, true);
114 | }
115 |
116 | private:
117 | Rewriter &Rewrite;
118 | };
119 |
120 | class MemsetHandler : public MatchFinder::MatchCallback {
121 | public:
122 | MemsetHandler(Rewriter &Rewrite) : Rewrite(Rewrite) {}
123 |
124 | virtual void run(const MatchFinder::MatchResult &Result) {
125 | const CallExpr *Memset = Result.Nodes.getNodeAs("memset");
126 | Rewrite.InsertText(Memset->getLocStart(), "RT", true, true);
127 | }
128 |
129 | private:
130 | Rewriter &Rewrite;
131 | };
132 |
133 | class RecordAssignHandler : public MatchFinder::MatchCallback {
134 | public:
135 | RecordAssignHandler(Rewriter &Rewrite) : Rewrite(Rewrite) {}
136 |
137 | virtual void run(const MatchFinder::MatchResult &Result) {
138 | const BinaryOperator *Assign =
139 | Result.Nodes.getNodeAs("assign");
140 | warn(Rewrite, Assign, " - RecordAssign without write barrier");
141 |
142 | std::string lhs_text, rhs_text;
143 | int assign_length = binop_strings(Rewrite, Assign, &lhs_text, &rhs_text);
144 | Rewrite.ReplaceText(Assign->getLHS()->getLocStart(),
145 | assign_length,
146 | "RTrecordcpy(&(" +
147 | lhs_text + "), &("
148 | + rhs_text + "), " +
149 | "sizeof(" + lhs_text + "))");
150 | }
151 |
152 | private:
153 | Rewriter &Rewrite;
154 | };
155 |
156 | // Implementation of the ASTConsumer interface for reading an AST produced
157 | // by the Clang parser. It registers a couple of matchers and runs them on
158 | // the AST.
159 | class MyASTConsumer : public ASTConsumer {
160 | public:
161 | MyASTConsumer(Rewriter &R) : HandlerForAssign(R),
162 | HandlerForMemcpy(R),
163 | HandlerForMemset(R),
164 | HandlerForRecordAssign(R) {
165 |
166 | // This should be by far the most common match for basic pointer writes we
167 | // we need to intercept.
168 | Matcher.addMatcher(
169 | binaryOperator(hasOperatorName("="),
170 | hasLHS(expr(hasType(isAnyPointer()))),
171 | // skip local variable assignments
172 | unless(hasLHS(declRefExpr(to(varDecl(hasAutomaticStorageDuration())))))).bind("assign"),
173 | &HandlerForAssign);
174 |
175 | // Should be very unusual to find a match for this.
176 | Matcher.addMatcher(
177 | binaryOperator(anyOf(hasOperatorName("+="),
178 | hasOperatorName("-=")),
179 | hasLHS(expr(hasType(isAnyPointer())))).bind("assign"),
180 | &HandlerForAssign);
181 |
182 | Matcher.addMatcher(
183 | callExpr(callee(functionDecl(hasName("memcpy")))).bind("memcpy"),
184 | &HandlerForMemcpy);
185 |
186 | Matcher.addMatcher(
187 | callExpr(callee(functionDecl(hasName("memset")))).bind("memset"),
188 | &HandlerForMemset);
189 |
190 | // Match struct, class, and union assignments
191 | Matcher.addMatcher(
192 | binaryOperator(hasOperatorName("="),
193 | hasType(qualType(hasCanonicalType(recordType()))),
194 | unless(hasLHS(declRefExpr(to(varDecl(hasAutomaticStorageDuration())))))).bind("assign"),
195 | &HandlerForRecordAssign);
196 | }
197 |
198 | void HandleTranslationUnit(ASTContext &Context) override {
199 | // Run the matchers when we have the whole TU parsed.
200 | Matcher.matchAST(Context);
201 | }
202 |
203 | private:
204 | AssignHandler HandlerForAssign;
205 | MemcpyHandler HandlerForMemcpy;
206 | MemsetHandler HandlerForMemset;
207 | RecordAssignHandler HandlerForRecordAssign;
208 | MatchFinder Matcher;
209 | };
210 |
211 | // For each source file provided to the tool, a new FrontendAction is created.
212 | class MyFrontendAction : public ASTFrontendAction {
213 | public:
214 | MyFrontendAction() {}
215 | void EndSourceFileAction() override {
216 | TheRewriter.getEditBuffer(TheRewriter.getSourceMgr().getMainFileID())
217 | .write(llvm::outs());
218 | }
219 |
220 | std::unique_ptr CreateASTConsumer(CompilerInstance &CI,
221 | StringRef file) override {
222 | TheRewriter.setSourceMgr(CI.getSourceManager(), CI.getLangOpts());
223 | return llvm::make_unique(TheRewriter);
224 | }
225 |
226 | private:
227 | Rewriter TheRewriter;
228 | };
229 |
230 | int main(int argc, const char **argv) {
231 | CommonOptionsParser op(argc, argv, MatcherSampleCategory);
232 | ClangTool Tool(op.getCompilations(), op.getSourcePathList());
233 |
234 | return Tool.run(newFrontendActionFactory().get());
235 | }
236 |
--------------------------------------------------------------------------------
/rtcoalesce.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 Wade Lawrence Hennessey
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | // rtgc page coalescing
19 |
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include "mem-config.h"
31 | #include "info-bits.h"
32 | #include "mem-internals.h"
33 | #include "allocate.h"
34 |
35 | static void verify_heap() {
36 | lock_all_free_locks();
37 | int page = 0;
38 | while (page < total_partition_pages) {
39 | GPTR group = pages[page].group;
40 | if (group > EXTERNAL_PAGE) {
41 | if (group->size <= BYTES_PER_PAGE) {
42 | //identify_single_free_page(page, group);
43 | page = page + 1;
44 | } else {
45 | //page = identify_multiple_free_pages(page, group);
46 | //page = page + (group->size / BYTES_PER_PAGE);
47 | BPTR page_base = PAGE_INDEX_TO_PTR(page);
48 | GCPTR gcptr = (GCPTR) page_base;
49 | if (gcptr->prev > (GCPTR) 16) {
50 | assert(gcptr->prev >= (GCPTR) first_partition_ptr);
51 | }
52 | if (gcptr->next > (GCPTR) 16) {
53 | assert(gcptr->next >= (GCPTR) first_partition_ptr);
54 | }
55 | page = page + (group->size / BYTES_PER_PAGE);
56 | }
57 | } else {
58 | page = page + 1;
59 | }
60 | }
61 | unlock_all_free_locks();
62 | }
63 |
64 | static void coalesce_free_pages() {
65 | long next_page = 0;
66 | long hole = -1;
67 | long page_count;
68 | while (next_page < total_partition_pages) {
69 | if (pages[next_page].group == FREE_PAGE) {
70 | if (-1 == hole) {
71 | hole = next_page;
72 | page_count = 1;
73 | } else {
74 | page_count = page_count + 1;
75 | }
76 | } else {
77 | if (-1 != hole) {
78 | RTinit_empty_pages(hole, page_count, HEAP_SEGMENT);
79 | hole = -1;
80 | page_count = 0;
81 | }
82 | }
83 | next_page = next_page + 1;
84 | }
85 | if (-1 != hole) {
86 | RTinit_empty_pages(hole, page_count, HEAP_SEGMENT);
87 | }
88 | }
89 |
90 |
91 | // Caller must hold the group->free_lock to save
92 | // us from repeatedly locking and unlocking for a page of objects
93 | static void remove_object_from_free_list(GPTR group, GCPTR object) {
94 | GCPTR prev = GET_LINK_POINTER(object->prev);
95 | GCPTR next = GET_LINK_POINTER(object->next);
96 |
97 | if (object == group->free) {
98 | // we end up here a lot
99 | group->free = next; // free must be locked
100 | }
101 |
102 | if (object == group->black) {
103 | group->black = next; // safe to not lock
104 | }
105 |
106 | if (object == group->last) {
107 | // we end up here a lot
108 | group->last = ((next == NULL) ? prev : next);
109 | }
110 |
111 | if (prev != NULL) {
112 | SET_LINK_POINTER(prev->next, next);
113 | }
114 | if (next != NULL) {
115 | SET_LINK_POINTER(next->prev, prev);
116 | }
117 | }
118 |
119 | static int all_green_page(int page, GPTR group) {
120 | BPTR page_base = PAGE_INDEX_TO_PTR(page);
121 | BPTR next_object = page_base;
122 | int all_green = 1;
123 | while (all_green && (next_object < (page_base + BYTES_PER_PAGE))) {
124 | GCPTR gcptr = (GCPTR) next_object;
125 | if (all_green && GREENP(gcptr)) {
126 | next_object = next_object + group->size;
127 | } else {
128 | all_green = 0;
129 | }
130 | }
131 | return(all_green);
132 | }
133 |
134 | static void identify_single_free_page(int page, GPTR group) {
135 | if (all_green_page(page, group)) {
136 | pthread_mutex_lock(&(group->free_lock));
137 | if (all_green_page(page, group)) {
138 | GCPTR next = (GCPTR) PAGE_INDEX_TO_PTR(page);
139 | // remove all objects on page from free list
140 | int object_count = BYTES_PER_PAGE / group->size;
141 | for (int i = 0; i < object_count; i++) {
142 | remove_object_from_free_list(group, next);
143 | next = (GCPTR) ((BPTR) next + group->size);
144 | }
145 | pages[page].group = 0;
146 | pages[page].group = FREE_PAGE;
147 | // HEY! conditionalize this clear page - just here to catch bugs
148 | //memset(PAGE_INDEX_TO_PTR(page), 0xEF, BYTES_PER_PAGE);
149 | }
150 | pthread_mutex_unlock(&(group->free_lock));
151 | }
152 | }
153 |
154 | // Return base page index, even if page is somewhere in the middle of object.
155 | static int identify_multiple_free_pages(int page, GPTR group) {
156 | BPTR page_base = PAGE_INDEX_TO_PTR(page);
157 | GCPTR gcptr = (GCPTR) page_base;
158 | if (pages[page].base == gcptr) {
159 | // Getting here means we've found the start of a multi-page object
160 | if (GREENP(gcptr)) {
161 | pthread_mutex_lock(&(group->free_lock));
162 | if (GREENP(gcptr)) {
163 | int num_pages = group->size / BYTES_PER_PAGE;
164 | remove_object_from_free_list(group, gcptr);
165 | for (int i = 0; i < num_pages; i++) {
166 | pages[page + i].base = 0;
167 | pages[page + i].group = FREE_PAGE;
168 | // HEY! conditionalize this clear page - just here to catch bugs
169 | //memset(PAGE_INDEX_TO_PTR(page + i), 0, BYTES_PER_PAGE);
170 | }
171 | }
172 | pthread_mutex_unlock(&(group->free_lock));
173 | }
174 | } else {
175 | // Getting here means we've run into a race condition with a multi-page
176 | // object allocation. We passed the base page when the page was still
177 | // empty, but the object got allocated and now we need to jump past it.
178 | assert(pages[page].base < gcptr);
179 | printf("mapping race page %d to base ptr %p\n", page, pages[page].base);
180 | page = PTR_TO_PAGE_INDEX(pages[page].base);
181 | }
182 | return(page);
183 | }
184 |
185 | void identify_free_pages() {
186 | int page = 0;
187 | while (page < total_partition_pages) {
188 | GPTR group = pages[page].group;
189 | if (group > EXTERNAL_PAGE) {
190 | if (group->size <= BYTES_PER_PAGE) {
191 | identify_single_free_page(page, group);
192 | page = page + 1;
193 | } else {
194 | page = identify_multiple_free_pages(page, group);
195 | page = page + (group->size / BYTES_PER_PAGE);
196 | //page = page + 1;
197 | }
198 | } else {
199 | page = page + 1;
200 | }
201 | }
202 | }
203 |
204 | void RTroom_print(long *green_count, long *alloc_count, long *hole_counts) {
205 | long total_empty_pages = 0;
206 | printf("----------------------------------------------------------------\n");
207 | for (long i = 0; i < (total_partition_pages + 1); i++) {
208 | if (hole_counts[i] > 0) {
209 | printf("Hole size = %d: %d\n", i, hole_counts[i]);
210 | }
211 | total_empty_pages = total_empty_pages + (hole_counts[i] * i);
212 | }
213 | printf("Total hole bytes = %d\n", total_empty_pages * BYTES_PER_PAGE);
214 | long total_committed_bytes = 0;
215 | for (int i = MIN_GROUP_INDEX; i <= MAX_GROUP_INDEX; i = i + 1) {
216 | if ((green_count[i] > 0) || (alloc_count[i] > 0)) {
217 | long total_group_bytes = ((alloc_count[i] + green_count[i]) *
218 | groups[i].size);
219 | printf("Group size = %d: allocated: %d, free: %d, total_bytes = %d\n",
220 | groups[i].size,
221 | alloc_count[i],
222 | green_count[i],
223 | total_group_bytes);
224 | total_committed_bytes = total_committed_bytes + total_group_bytes;
225 | }
226 | }
227 | printf("Total committed bytes = %d\n", total_committed_bytes);
228 | printf("Total hole + committed bytes = %d (max %d)\n",
229 | (total_empty_pages * BYTES_PER_PAGE) + total_committed_bytes,
230 | total_partition_pages * BYTES_PER_PAGE);
231 | printf("Static space allocated bytes = %d\n",
232 | static_frontier_ptr - first_static_ptr);
233 | printf("----------------------------------------------------------------\n");
234 | }
235 |
236 | void RTroom() {
237 | long green_count[MAX_GROUP_INDEX + 1];
238 | long alloc_count[MAX_GROUP_INDEX + 1];
239 | long hole_counts_len = (total_partition_pages + 1) * sizeof(long);
240 | // HEY! should malloc this
241 | long hole_counts[hole_counts_len];
242 | memset(green_count, 0, sizeof(green_count));
243 | memset(alloc_count, 0, sizeof(alloc_count));
244 | memset(hole_counts, 0, hole_counts_len);
245 | int page = 0;
246 | int hole_len = 0;
247 | lock_all_free_locks();
248 | while (page < total_partition_pages) {
249 | GPTR group = pages[page].group;
250 | if (group > EXTERNAL_PAGE) {
251 | if (hole_len > 0) {
252 | hole_counts[hole_len] = hole_counts[hole_len] + 1;
253 | hole_len = 0;
254 | }
255 | GCPTR next = (GCPTR) PAGE_INDEX_TO_PTR(page);
256 | if (group->size <= BYTES_PER_PAGE) {
257 | int object_count = BYTES_PER_PAGE / group->size;
258 | int green = 0;
259 | int alloc = 0;
260 | for (int i = 0; i < object_count; i++) {
261 | if (GREENP(next)) {
262 | green = green + 1;
263 | } else {
264 | alloc = alloc + 1;
265 | }
266 | next = (GCPTR) ((BPTR) next + group->size);
267 | }
268 | green_count[group->index] = green_count[group->index] + green;
269 | alloc_count[group->index] = alloc_count[group->index] + alloc;
270 | page = page + 1;
271 | } else {
272 | if (GREENP(next)) {
273 | printf("HEY! shouldn't see green multi page objects after coalesce!\n");
274 | green_count[group->index] = green_count[group->index] + 1;
275 | } else {
276 | alloc_count[group->index] = alloc_count[group->index] + 1;
277 | }
278 | page = page + (group->size / BYTES_PER_PAGE);
279 | }
280 | } else {
281 | if (group != EMPTY_PAGE) {
282 | Debugger("Should have found an EMPTY_PAGE!\n");
283 | }
284 | hole_len = hole_len + 1;
285 | page = page + 1;
286 | }
287 | }
288 | if (hole_len > 0) {
289 | hole_counts[hole_len] = hole_counts[hole_len] + 1;
290 | }
291 | unlock_all_free_locks();
292 | RTroom_print(green_count, alloc_count, hole_counts);
293 | }
294 |
295 | // Caller must hold empty_pages_lock when calling this.
296 | void delete_merged_holes(int delete_count) {
297 | HOLE_PTR prev = NULL;
298 | HOLE_PTR next = empty_pages;
299 | while ((delete_count > 0) && (next != NULL)) {
300 | if (next->page_count == 0) {
301 | if (prev == NULL) {
302 | empty_pages = next->next;
303 | next = empty_pages;
304 | } else {
305 | next = next->next;
306 | prev->next = next;
307 | }
308 | delete_count = delete_count - 1;
309 | } else {
310 | prev = next;
311 | next = next->next;
312 | }
313 | }
314 | assert(delete_count == 0);
315 | }
316 |
317 | void merge_adjacent_holes() {
318 | int merge_count = 0;
319 | pthread_mutex_lock(&empty_pages_lock);
320 | HOLE_PTR next = empty_pages;
321 | while (next != NULL) {
322 | // Skip merged holes with 0 page_counts
323 | if (next->page_count > 0) {
324 | int start_page = PTR_TO_PAGE_INDEX(next);
325 | int continue_merge;
326 | do {
327 | continue_merge = 0;
328 | int end_page = start_page + next->page_count;
329 | if (end_page < total_partition_pages) {
330 | if (pages[end_page].group == EMPTY_PAGE) {
331 | HOLE_PTR adjacent = (HOLE_PTR) PAGE_INDEX_TO_PTR(end_page);
332 | next->page_count = next->page_count + adjacent->page_count;
333 | adjacent->page_count = 0;
334 | merge_count = merge_count + 1;
335 | continue_merge = 1;
336 | }
337 | }
338 | } while (continue_merge == 1);
339 | }
340 | next = next->next;
341 | }
342 | delete_merged_holes(merge_count);
343 | pthread_mutex_unlock(&empty_pages_lock);
344 | }
345 |
346 |
347 | void coalesce_all_free_pages() {
348 | identify_free_pages();
349 | coalesce_free_pages();
350 | merge_adjacent_holes();
351 | }
352 |
--------------------------------------------------------------------------------
/rtalloc.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 Wade Lawrence Hennessey
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | // Real time storage allocater
19 |
20 | #define _GNU_SOURCE
21 |
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include "mem-config.h"
33 | #include "info-bits.h"
34 | #include "mem-internals.h"
35 | #include "allocate.h"
36 |
37 | static inline int size_to_group_index(int size) {
38 | int s = size;
39 | int index = 0;
40 |
41 | s = s - 1;
42 | while (s != 0) {
43 | s = s / 2;
44 | index = index + 1;
45 | }
46 | return(MAX(MIN_GROUP_INDEX, index));
47 | }
48 |
49 | static
50 | void init_group_info() {
51 | for (int index = MIN_GROUP_INDEX; index <= MAX_GROUP_INDEX; index = index + 1) {
52 | int size = 1 << index;
53 | groups[index].size = size;
54 | groups[index].index = index;
55 | groups[index].free = NULL;
56 | groups[index].last = NULL;
57 | groups[index].white = NULL;
58 | groups[index].black = NULL;
59 | groups[index].gray = NULL;
60 | groups[index].white_count = 0;
61 | groups[index].black_scanned_count = 0;
62 | groups[index].black_alloc_count = 0;
63 | pthread_mutex_init(&(groups[index].free_lock), NULL);
64 | pthread_mutex_init(&(groups[index].black_and_last_lock), NULL);
65 | }
66 | }
67 |
68 | static
69 | void init_page_info() {
70 | for (int i = 0; i < total_partition_pages; i++) {
71 | pages[i].base = NULL;
72 | pages[i].group = SYSTEM_PAGE;
73 | }
74 | }
75 |
76 | void RTinit_empty_pages(int first_page, int page_count, int type) {
77 | int last_page = first_page + page_count;
78 | for (int i = first_page; i < last_page; i++) {
79 | pages[i].base = NULL;
80 | pages[i].group = EMPTY_PAGE;
81 | }
82 |
83 | if (type == HEAP_SEGMENT) {
84 | pthread_mutex_lock(&empty_pages_lock);
85 | // Add the pages to the front of the empty page list
86 | HOLE_PTR new_hole = (HOLE_PTR) PAGE_INDEX_TO_PTR(first_page);
87 | new_hole->page_count = page_count;
88 | new_hole->next = empty_pages;
89 | empty_pages = new_hole;
90 | pthread_mutex_unlock(&empty_pages_lock);
91 | } else {
92 | Debugger("Can only init heap pages");
93 | }
94 | }
95 |
96 | static
97 | long allocate_segment(size_t desired_bytes, int type) {
98 | size_t actual_bytes = 0;
99 | BPTR first_segment_ptr, last_segment_ptr;
100 | int segment_page_count, first_segment_page;
101 | int segment = total_segments;
102 |
103 | if ((desired_bytes > 0) &&
104 | (desired_bytes == (desired_bytes & ~PAGE_ALIGNMENT_MASK)) &&
105 | (total_segments < MAX_SEGMENTS)) {
106 | first_segment_ptr = RTbig_malloc(desired_bytes);
107 |
108 | if (NULL != first_segment_ptr) {
109 | total_segments = total_segments + 1;
110 | actual_bytes = desired_bytes;
111 | segment_page_count = actual_bytes / BYTES_PER_PAGE;
112 | segments[segment].first_segment_ptr = first_segment_ptr;
113 | last_segment_ptr = first_segment_ptr +
114 | (segment_page_count * BYTES_PER_PAGE);
115 | segments[segment].last_segment_ptr = last_segment_ptr;
116 | segments[segment].segment_page_count = segment_page_count;
117 | segments[segment].type = type;
118 |
119 | // for now we only support a single static segment and
120 | // a single heap segment
121 | switch (type) {
122 | case HEAP_SEGMENT:
123 | first_partition_ptr = first_segment_ptr;
124 | last_partition_ptr = last_segment_ptr;
125 | first_segment_page = PTR_TO_PAGE_INDEX(first_segment_ptr);
126 | RTinit_empty_pages(first_segment_page, segment_page_count, type);
127 | break;
128 | case STATIC_SEGMENT:
129 | last_static_ptr = segments[0].last_segment_ptr;
130 | first_static_ptr = segments[0].first_segment_ptr;
131 | static_frontier_ptr = first_static_ptr;
132 | break;
133 | default: break;
134 | }
135 | } else {
136 | Debugger("Add support for more than 2 segments");
137 | }
138 | }
139 | return(actual_bytes);
140 | }
141 |
142 | static
143 | GCPTR allocate_empty_pages(int page_count) {
144 | int remaining_page_count, best_remaining_page_count;
145 | GCPTR base = NULL;
146 | HOLE_PTR prev = NULL;
147 | HOLE_PTR best = NULL;
148 | HOLE_PTR best_prev = NULL;
149 |
150 | pthread_mutex_lock(&empty_pages_lock);
151 | HOLE_PTR next = empty_pages;
152 | // Search for a best fit hole
153 | best_remaining_page_count = total_partition_pages + 1;
154 | while ((best_remaining_page_count > 0) && (next != NULL)) {
155 | if (next->page_count >= page_count) {
156 | remaining_page_count = next->page_count - page_count;
157 | if (remaining_page_count < best_remaining_page_count) {
158 | best_remaining_page_count = remaining_page_count;
159 | best = next;
160 | best_prev = prev;
161 | }
162 | }
163 | prev = next;
164 | next = next->next;
165 | }
166 |
167 | if (best != NULL) {
168 | HOLE_PTR rest;
169 | if (best_remaining_page_count == 0) {
170 | rest = best->next;
171 | } else {
172 | rest = (HOLE_PTR) ((BPTR) best + (page_count * BYTES_PER_PAGE));
173 | rest->page_count = best_remaining_page_count;
174 | rest->next = best->next;
175 | }
176 | if (best_prev == NULL) {
177 | empty_pages = rest;
178 | } else {
179 | best_prev->next = rest;
180 | }
181 | base = (GCPTR) best;
182 | }
183 | pthread_mutex_unlock(&empty_pages_lock);
184 | return(base);
185 | }
186 |
187 | // Whoever calls this function has to be holding the group->free_lock.
188 | static
189 | void init_pages_for_group(GPTR group, int min_pages) {
190 | int pages_per_object = group->size / BYTES_PER_PAGE;
191 | int byte_count = MAX(pages_per_object,min_pages) * BYTES_PER_PAGE;
192 | int num_objects = byte_count >> group->index;
193 | int page_count = (num_objects * group->size) / BYTES_PER_PAGE;
194 | GCPTR base = allocate_empty_pages(page_count);
195 |
196 | if (base == NULL) {
197 | int actual_bytes = allocate_segment(MAX(DEFAULT_HEAP_SEGMENT_SIZE,
198 | page_count * BYTES_PER_PAGE),
199 | HEAP_SEGMENT);
200 | assert(0 == actual_bytes); // while we only have 1 segment
201 | if (actual_bytes < byte_count) {
202 | // atomic and concurrent gc can't flip without
203 | // unlocking all group free locks
204 | pthread_mutex_unlock(&(group->free_lock));
205 | long current_gc_count = gc_count;
206 | if (RTatomic_gc) {
207 | // atomic gc
208 | run_gc = 1;
209 | while (gc_count < (current_gc_count + 2)) {
210 | sched_yield();
211 | }
212 | } else {
213 | // concurrent gc
214 | // need to wait until gc count increases by 2
215 | printf("alloc out ran gc, sync collect\n");
216 | while (gc_count < (current_gc_count + 2)) {
217 | // Should use a condition variable counter instead of polling here
218 | sched_yield();
219 | }
220 | }
221 | pthread_mutex_lock(&(group->free_lock));
222 | }
223 | if (NULL == group->free) {
224 | base = allocate_empty_pages(page_count);
225 | } else {
226 | // Gc added to free list, so no need to allocate or init empty pages.
227 | // Could just continue because base is still NULL, but being explicit
228 | // here seems clearer.
229 | return;
230 | }
231 | }
232 |
233 | if (base != NULL) {
234 | GCPTR current = NULL;
235 | GCPTR next = base;
236 | for (int i = 0; i < num_objects; i++) {
237 | GCPTR prev = current;
238 | current = next;
239 | next = (GCPTR) ((BPTR) current + group->size);
240 | current->prev = prev;
241 | current->next = next;
242 | SET_COLOR(current,GREEN);
243 | }
244 | SET_LINK_POINTER(current->next,NULL);
245 | assert(NULL == group->free);
246 | WITH_LOCK((group->black_and_last_lock),
247 | GCPTR last = group->last;
248 | group->free = base;
249 | if (last == NULL) { // No gray, black, or green objects?
250 | group->black = base;
251 | } else {
252 | SET_LINK_POINTER(base->prev, last);
253 | SET_LINK_POINTER(last->next,base);
254 | }
255 | group->last = current;);
256 | // Only now can we initialize EMPTY page table entries.
257 | // Doing it before object GCHDRs are correctly setup and colored green
258 | // allows conservative pointers and exposed uncleared
259 | // dead pointers on the stack to incorrectly make_gray
260 | // random bits that look like white objects!
261 | if (base != NULL) {
262 | int i;
263 | int next_page_index = PTR_TO_PAGE_INDEX(((BPTR) base));
264 | for (i = 1; i <= page_count; i++) {
265 | pages[next_page_index].base = base;
266 | pages[next_page_index].group = group;
267 | next_page_index = next_page_index + 1;
268 | }
269 | }
270 | }
271 | }
272 |
273 | static inline
274 | GPTR allocation_group(long *metadata, int size) {
275 | int data_size, real_size;
276 | if (size >= 0) {
277 | switch ((long) metadata) {
278 | case (long) RTnopointers:
279 | case (long) RTpointers:
280 | case (long) RTcustom1:
281 | // delete - dead variable: data_size = size;
282 | real_size = size + sizeof(GC_HEADER);
283 | break;
284 | default:
285 | data_size = (metadata[0] * size) + sizeof(void *);
286 | real_size = data_size + sizeof(GC_HEADER);
287 | break;
288 | }
289 |
290 | GPTR group;
291 | int group_index = size_to_group_index(real_size);
292 | if (group_index > MAX_GROUP_INDEX) {
293 | printf("%d", real_size);
294 | Debugger(" exceeds the maximum object size\n");
295 | } else {
296 | group = &(groups[group_index]);
297 | }
298 | return(group);
299 | } else {
300 | Debugger("Negative object size\n");
301 | }
302 | }
303 |
304 | static inline
305 | int initialize_object_metadata(void *metadata, GCPTR gcptr, GPTR group) {
306 | long md = (long) metadata;
307 | int body_size = group->size - sizeof(GC_HEADER);
308 | if (md < SC_METADATA) {
309 | SET_STORAGE_CLASS(gcptr, md);
310 | } else {
311 | LPTR base = (LPTR) (gcptr + 1);
312 | LPTR last_ptr = base + (group->size / sizeof(LPTR)) - 3;
313 | // Must init md pointer inside of free lock. If we try to init it with
314 | // the object body init outside the free lock, the gc might see an
315 | // uninitialized md pointer.
316 | *last_ptr = md;
317 | SET_STORAGE_CLASS(gcptr, SC_METADATA);
318 | body_size = body_size - sizeof(LPTR);
319 | }
320 | return(body_size);
321 | }
322 |
323 | static inline
324 | LPTR initialize_object_body(void *metadata, LPTR base, int body_size) {
325 | if (metadata != RTnopointers) {
326 | memset(base, 0, body_size);
327 | }
328 | }
329 |
330 | void *RTallocate(void *metadata, int size) {
331 | GPTR group = allocation_group(metadata,size);
332 | pthread_mutex_lock(&(group->free_lock));
333 | if (group->free == NULL) {
334 | init_pages_for_group(group,1);
335 | if (group->free == NULL) {
336 | out_of_memory("Heap", group->size);
337 | }
338 | }
339 | GCPTR new = group->free;
340 | group->free = GET_LINK_POINTER(new->next);
341 | // No need for an explicit flip lock here. During a flip the gc will
342 | // hold the free_lock for every group, so no allocator can get here
343 | // when the marked_color is being changed.
344 | SET_COLOR(new,marked_color); // Must allocate black!
345 | DEBUG(group->black_alloc_count = group->black_alloc_count + 1);
346 |
347 | int body_size = initialize_object_metadata(metadata, new, group);
348 | // Unlock only after storage class and md initialization because
349 | // gc recyling garbage can read and write next ptr and md.
350 | pthread_mutex_unlock(&(group->free_lock));
351 | LPTR base = (LPTR) (new + 1);
352 | initialize_object_body(metadata, base, body_size);
353 | return(base);
354 | }
355 |
356 | void *RTstatic_allocate(void *metadata, int size) {
357 | size = ROUND_UPTO_LONG_ALIGNMENT(size);
358 |
359 | // Should we lock during flip and copy frontier_ptr at that time?
360 | // Seems like it shouldn't be needed.
361 | pthread_mutex_lock(&static_frontier_ptr_lock);
362 | // Static object headers are only 1 word long instead of 2
363 | LPTR ptr = (LPTR) static_frontier_ptr;
364 | static_frontier_ptr = static_frontier_ptr + size + sizeof(long);
365 |
366 | if (static_frontier_ptr > last_static_ptr) {
367 | out_of_memory("Static", size);
368 | } else {
369 | *ptr = (size << LINK_INFO_BITS);
370 | if ((((long) metadata) <= SC_POINTERS) || (((long) metadata) == SC_CUSTOM1)) {
371 | SET_STORAGE_CLASS(((GCPTR) (ptr - 1)), (long) metadata);
372 | } else {
373 | Debugger("Add static support for SC_METADATA");
374 | }
375 | ptr = ptr + 1;
376 | memset(ptr, 0, size);
377 | pthread_mutex_unlock(&static_frontier_ptr_lock);
378 | return(ptr);
379 | }
380 | }
381 |
382 | // The gc itself runs on the intial process thread. We don't keep
383 | // track of that here, we just need to keep track of mutator threads here.
384 | void init_mutator_threads() {
385 | int last_thread_index = MAX_THREADS - 1;
386 | for (int i = 0; i < last_thread_index; i++) {
387 | threads[i].next = threads + i + 1;
388 | }
389 | threads[last_thread_index].next = NULL;
390 | free_threads = threads;
391 | live_threads = NULL;
392 | }
393 |
394 | void register_global_root(void *root) {
395 | pthread_mutex_lock(&global_roots_lock);
396 | if (total_global_roots == MAX_GLOBAL_ROOTS) {
397 | Debugger("global roots full!\n");
398 | } else {
399 | global_roots[total_global_roots] = (char *) root;
400 | total_global_roots = total_global_roots + 1;
401 | }
402 | pthread_mutex_unlock(&global_roots_lock);
403 | }
404 |
405 | static
406 | size_t default_stack_size() {
407 | pthread_attr_t attr;
408 | void *stackaddr;
409 | size_t stacksize;
410 |
411 | pthread_t thread = pthread_self();
412 | pthread_getattr_np(thread, &attr);
413 | pthread_attr_getstack(&attr, &stackaddr, &stacksize);
414 | return(stacksize);
415 | }
416 |
417 | static
418 | void init_saved_threads() {
419 | size_t stack_size = default_stack_size();
420 | for (int i = 0; i < MAX_THREADS; i++) {
421 | saved_threads[i].saved_stack_base = RTbig_malloc(stack_size);
422 | }
423 | }
424 |
425 | void RTinit_heap(size_t first_segment_bytes, size_t static_size) {
426 | enable_write_barrier = 0;
427 |
428 | printf("Default stacksize is %d\n", default_stack_size());
429 |
430 | total_partition_pages = first_segment_bytes / BYTES_PER_PAGE;
431 | groups = RTbig_malloc(sizeof(GROUP_INFO) * (MAX_GROUP_INDEX + 1));
432 | pages = RTbig_malloc(sizeof(PAGE_INFO) * total_partition_pages);
433 | segments = RTbig_malloc(sizeof(SEGMENT) * MAX_SEGMENTS);
434 | threads = RTbig_malloc(sizeof(THREAD_INFO) * MAX_THREADS);
435 | saved_threads = RTbig_malloc(sizeof(THREAD_STATE) * MAX_THREADS);
436 | global_roots = RTbig_malloc(sizeof(char **) * MAX_GLOBAL_ROOTS);
437 | #if USE_BIT_WRITE_BARRIER
438 | RTwrite_vector_length = first_segment_bytes / (MIN_GROUP_SIZE * BITS_PER_LONG);
439 | RTwrite_vector = RTbig_malloc(RTwrite_vector_length * sizeof(long));
440 | memset(RTwrite_vector, 0, RTwrite_vector_length * sizeof(long));
441 | //printf("using bit write barrier, ");
442 | #else
443 | RTwrite_vector_length = (total_partition_pages *
444 | (BYTES_PER_PAGE / MIN_GROUP_SIZE));
445 | RTwrite_vector = RTbig_malloc(RTwrite_vector_length);
446 | memset(RTwrite_vector, 0, RTwrite_vector_length);
447 | //printf("using byte write barrier, ");
448 | #endif
449 | if ((pages == 0) || (groups == 0) || (segments == 0) ||
450 | (threads == 0) || (global_roots == 0) || (RTwrite_vector == 0)) {
451 | out_of_memory("Heap Memory tables", 0);
452 | }
453 |
454 | init_page_info();
455 | empty_pages = NULL;
456 | init_mutator_threads();
457 | total_segments = 0;
458 |
459 | if ((static_size > 0) &&
460 | (allocate_segment(static_size, STATIC_SEGMENT) == 0)) {
461 | out_of_memory("Static Memory Initialization", static_size/1024);
462 | }
463 |
464 | if (allocate_segment(first_segment_bytes, HEAP_SEGMENT) == 0) {
465 | out_of_memory("Heap Memory allocation", first_segment_bytes/1024);
466 | }
467 |
468 | marked_color = GENERATION0;
469 | unmarked_color = GENERATION1;
470 | init_saved_threads();
471 | init_group_info();
472 | init_realtime_gc();
473 | }
474 |
475 | static THREAD_INFO *alloc_thread() {
476 | if (NULL == free_threads) {
477 | Debugger("Out of threads");
478 | } else {
479 | pthread_mutex_lock(&threads_lock);
480 | THREAD_INFO *thread = free_threads;
481 | // Remove thread from free_threads
482 | free_threads = free_threads->next;
483 | pthread_mutex_unlock(&threads_lock);
484 | return(thread);
485 | }
486 | }
487 |
488 | static void make_thread_live(THREAD_INFO *thread) {
489 | pthread_mutex_lock(&threads_lock);
490 | // Add thread to live_threads;
491 | thread->next = live_threads;
492 | live_threads = thread;
493 | total_threads = total_threads + 1;
494 | pthread_mutex_unlock(&threads_lock);
495 | }
496 |
497 | static void free_thread(THREAD_INFO *thread) {
498 | pthread_mutex_lock(&threads_lock);
499 | // Find this thread in live_threads list
500 | if (thread == live_threads) {
501 | // Remove thread from live_threads list
502 | live_threads = live_threads->next;
503 | } else {
504 | // Find thread in live list
505 | THREAD_INFO *target_thread = live_threads;
506 | THREAD_INFO *prev_thread = NULL;
507 | while (target_thread != thread) {
508 | prev_thread = target_thread;
509 | target_thread = target_thread->next;
510 | }
511 | // Remove thread from live_threads list
512 | prev_thread->next = target_thread->next;
513 | }
514 | // Add thread to the head of free_threads list
515 | thread->next = free_threads;
516 | free_threads->next = thread;
517 | total_threads = total_threads - 1;
518 | pthread_mutex_unlock(&threads_lock);
519 | }
520 |
521 | static void thread_cleanup_handler(void *arg) {
522 | THREAD_INFO *thread = arg;
523 | printf("Called cleanup handler for pthread %p\n", thread->pthread);
524 | free_thread(thread);
525 | }
526 |
527 | void *rtalloc_start_thread(void *thread_arg) {
528 | THREAD_INFO *thread = thread_arg;
529 | printf("Thread %p started\n", thread->pthread);
530 | pthread_attr_t attr;
531 | void *stackaddr;
532 | size_t stacksize;
533 | pthread_getattr_np(thread->pthread, &attr);
534 | pthread_attr_getstack(&attr, &stackaddr, &stacksize);
535 | // We don't really need this info, but it might be nice for debugging
536 | // stackaddr is the LOWEST addressable byte of the stack
537 | // The stack pointer starts at stackaddr + stacksize!
538 | // printf("Stackaddr is %p\n", stackaddr);
539 | // printf("Stacksize is %x\n", stacksize);
540 | thread->stack_base = stackaddr;
541 | thread->stack_size = stacksize;
542 | thread->stack_bottom = (char *) &stacksize;
543 | timerclear(&(thread->max_pause_tv));
544 | timerclear(&(thread->total_pause_tv));
545 | fflush(stdout);
546 |
547 | if (0 != pthread_setspecific(thread_key, (void *) thread)) {
548 | printf("pthread_setspecific failed!\n");
549 | } else {
550 | pthread_cleanup_push(&thread_cleanup_handler, thread);
551 | // Only now is this thread ready to be considered "live" by the gc
552 | make_thread_live(thread);
553 | thread->started = 1;
554 | // Now we can call the real start function
555 | (thread->start_func)(thread->args);
556 | pthread_cleanup_pop(1);
557 | }
558 | }
559 |
560 | int RTpthread_create(pthread_t *pthread, const pthread_attr_t *attr,
561 | void *(*start_func) (void *), void *args) {
562 | THREAD_INFO *new_thread = alloc_thread();
563 | new_thread->start_func = start_func;
564 | new_thread->args = args;
565 | new_thread->started = 0;
566 |
567 | int return_val;
568 | if (0 != (return_val = pthread_create(&(new_thread->pthread),
569 | attr,
570 | rtalloc_start_thread,
571 | new_thread))) {
572 | return(return_val);
573 | } else {
574 | *pthread = new_thread->pthread;
575 | while (1 != new_thread->started);
576 | return(return_val);
577 | }
578 | }
579 |
--------------------------------------------------------------------------------
/rtgc.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 Wade Lawrence Hennessey
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | // Real time garbage collector running on one or more threads/cores
19 |
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include "mem-config.h"
31 | #include "info-bits.h"
32 | #include "mem-internals.h"
33 | #include "allocate.h"
34 |
35 | struct timeval max_flip_tv, total_flip_tv;
36 |
37 | static
38 | void RTmake_object_gray(GCPTR current) {
39 | GPTR group = PTR_TO_GROUP(current);
40 | GCPTR prev = GET_LINK_POINTER(current->prev);
41 | GCPTR next = GET_LINK_POINTER(current->next);
42 |
43 | // Remove current from WHITE space
44 | if (current == group->white) {
45 | group->white = next;
46 | }
47 | if (prev != NULL) {
48 | SET_LINK_POINTER(prev->next, next);
49 | }
50 | if (next != NULL) {
51 | SET_LINK_POINTER(next->prev, prev);
52 | }
53 |
54 | // Link current onto the end of the gray set. This give us a breadth
55 | // first search when scanning the gray set (not that it matters)
56 | SET_LINK_POINTER(current->prev, NULL);
57 | GCPTR gray = group->gray;
58 | if (gray == NULL) {
59 | pthread_mutex_lock(&(group->black_and_last_lock));
60 | SET_LINK_POINTER(current->next, group->black);
61 | if (group->black == NULL) {
62 | assert(NULL == group->free);
63 | group->black = current;
64 | group->last = current;
65 | pthread_mutex_unlock(&(group->black_and_last_lock));
66 | } else {
67 | pthread_mutex_unlock(&(group->black_and_last_lock));
68 | // looks like a race with alloc setting color on
69 | // black->prev. Should use a more specific lock than free.
70 | WITH_LOCK(group->free_lock,
71 | SET_LINK_POINTER((group->black)->prev, current););
72 | }
73 | } else {
74 | SET_LINK_POINTER(current->next, gray);
75 | SET_LINK_POINTER(gray->prev, current);
76 | }
77 | assert(WHITEP(current));
78 | SET_COLOR(current, GRAY);
79 | group->gray = current;
80 | assert(group->white_count > 0); // no lock needed, white_count is gc only
81 | DEBUG(group->white_count = group->white_count - 1);
82 | }
83 |
84 | static inline int valid_interior_ptr(GCPTR gcptr, BPTR interior_ptr) {
85 | long delta = interior_ptr - (BPTR) gcptr;
86 | return(delta < (INTERIOR_PTR_RETENTION_LIMIT + sizeof(GC_HEADER)));
87 | }
88 |
89 | static inline GCPTR interior_to_gcptr_3(BPTR ptr, PPTR page, GPTR group) {
90 | GCPTR gcptr;
91 | if (group->size >= BYTES_PER_PAGE) {
92 | gcptr = page->base;
93 | } else {
94 | // This only works because first_partition_ptr is BYTES_PER_PAGE aligned
95 | gcptr = (GCPTR) ((long) ptr & (-1 << group->index));
96 | }
97 | return(gcptr);
98 | }
99 |
100 | static inline GCPTR interior_to_gcptr(BPTR ptr) {
101 | PPTR page = pages + PTR_TO_PAGE_INDEX(ptr);
102 | GPTR group = page->group;
103 | GCPTR gcptr;
104 |
105 | if (group > EXTERNAL_PAGE) {
106 | if (group->size >= BYTES_PER_PAGE) {
107 | gcptr = page->base;
108 | } else {
109 | // This only works because first_partition_ptr is BYTES_PER_PAGE aligned
110 | gcptr = (GCPTR) ((long) ptr & (-1 << group->index));
111 | }
112 | } else {
113 | Debugger("ERROR! Found IN_HEAP pointer with NULL group!\n");
114 | }
115 | // FIX me - gcptr can be returned uninitailzed.
116 | return(gcptr);
117 | }
118 |
119 | void RTtrace_pointer(void *ptr) {
120 | if (IN_PARTITION(ptr)) {
121 | PPTR page = pages + PTR_TO_PAGE_INDEX(ptr);
122 | GPTR group = page->group;
123 | if (group > EXTERNAL_PAGE) {
124 | GCPTR gcptr = interior_to_gcptr_3(ptr, page, group);
125 | if (WHITEP(gcptr) && valid_interior_ptr(gcptr, ptr)) {
126 | RTmake_object_gray(gcptr);
127 | }
128 | }
129 | }
130 | }
131 |
132 | // Slightly shorter trace that skips partition check for ptrs known
133 | // to point into the heap
134 | void RTtrace_heap_pointer(void *ptr) {
135 | GCPTR gcptr = interior_to_gcptr(ptr);
136 | if (WHITEP(gcptr)) {
137 | RTmake_object_gray(gcptr);
138 | }
139 | }
140 |
141 | // Scan memory to trace *possible* pointers
142 | static
143 | void scan_memory_segment(BPTR low, BPTR high) {
144 | for (BPTR next = low; next < high; next = next + GC_POINTER_ALIGNMENT) {
145 | BPTR ptr = *((BPTR *) next);
146 | if (IN_PARTITION(ptr)) {
147 | PPTR page = pages + PTR_TO_PAGE_INDEX(ptr);
148 | GPTR group = page->group;
149 | if (group > EXTERNAL_PAGE) {
150 | GCPTR gcptr = interior_to_gcptr_3(ptr, page, group);
151 | if (WHITEP(gcptr) && valid_interior_ptr(gcptr, ptr)) {
152 | RTmake_object_gray(gcptr);
153 | }
154 | }
155 | }
156 | }
157 | }
158 |
159 | static
160 | void scan_memory_segment_with_metadata(BPTR low, BPTR high) {
161 | LPTR last_ptr = (LPTR) high - 1;
162 | RT_METADATA *md = (RT_METADATA *) *last_ptr;
163 | long size = *md;
164 | long length = high - low - sizeof(RT_METADATA *);
165 | long count = length / size;
166 |
167 | for (int i = 0; i < count; i++) {
168 | BPTR offset = low + (i * size);
169 | for (int j = 1; md[j] != -1; j++) {
170 | BPTR ptr = *((BPTR *) (offset + md[j]));
171 | if (IN_PARTITION(ptr)) {
172 | PPTR page = pages + PTR_TO_PAGE_INDEX(ptr);
173 | GPTR group = page->group;
174 | if (group > EXTERNAL_PAGE) {
175 | GCPTR gcptr = interior_to_gcptr_3(ptr, page, group);
176 | if (WHITEP(gcptr) && valid_interior_ptr(gcptr, ptr)) {
177 | RTmake_object_gray(gcptr);
178 | }
179 | }
180 | }
181 | }
182 | }
183 | }
184 |
185 | // Public version
186 | void RTscan_memory_segment(BPTR low, BPTR high) {
187 | scan_memory_segment(low, high);
188 | }
189 |
190 | #if USE_BIT_WRITE_BARRIER
191 | static
192 | int scan_write_vector() {
193 | int mark_count = 0;
194 | for (long index = 0; index < RTwrite_vector_length; index++) {
195 | if (0 != RTwrite_vector[index]) {
196 | BPTR base_ptr = first_partition_ptr +
197 | (index * MIN_GROUP_SIZE * BITS_PER_LONG);
198 | for (long bit = 0; bit < BITS_PER_LONG; bit = bit + 1) {
199 | unsigned long mask = 1L << bit;
200 | if (0 != (RTwrite_vector[index] & mask)) {
201 | GCPTR gcptr = (GCPTR) (base_ptr + (bit * MIN_GROUP_SIZE));
202 | mark_count = mark_count + 1;
203 | if (WHITEP(gcptr)) {
204 | RTmake_object_gray(gcptr);
205 | }
206 | mask = ~mask;
207 | // Must clear only the bit we just found set.
208 | // Clearing entire long at end of bit scan
209 | // creates a race condition with mark_write_vector.
210 | locked_long_and(RTwrite_vector + index, mask);
211 | }
212 | }
213 | }
214 | }
215 | return(mark_count);
216 | }
217 |
218 | static
219 | void mark_write_vector(GCPTR gcptr) {
220 | long ptr_offset = ((BPTR) gcptr - first_partition_ptr);
221 | long long_index = ptr_offset / (MIN_GROUP_SIZE * BITS_PER_LONG);
222 | int bit = (ptr_offset % (MIN_GROUP_SIZE * BITS_PER_LONG)) / MIN_GROUP_SIZE;
223 | unsigned long bit_mask = 1L << bit;
224 | assert(0 != bit_mask);
225 | locked_long_or(RTwrite_vector + long_index, bit_mask);
226 | }
227 | #else
228 | static
229 | int scan_write_vector() {
230 | int mark_count = 0;
231 | for (long index = 0; index < RTwrite_vector_length; index++) {
232 | if (1 == RTwrite_vector[index]) {
233 | GCPTR gcptr = (GCPTR) (first_partition_ptr + (index * MIN_GROUP_SIZE));
234 | RTwrite_vector[index] = 0;
235 | mark_count = mark_count + 1;
236 | if (WHITEP(gcptr)) {
237 | RTmake_object_gray(gcptr);
238 | }
239 | }
240 | }
241 | return(mark_count);
242 | }
243 |
244 | static
245 | void mark_write_vector(GCPTR gcptr) {
246 | long index = ((BPTR) gcptr - first_partition_ptr) / MIN_GROUP_SIZE;
247 | RTwrite_vector[index] = 1;
248 | }
249 | #endif
250 |
251 | // Snapshot-at-gc-start write barrier.
252 | // This is really just a version of scan_memory_segment on a single pointer.
253 | // It marks the RTwrite_vector instead of immediately making white
254 | // objects become gray.
255 | void *RTwrite_barrier(void *lhs_address, void *rhs) {
256 | if (enable_write_barrier) {
257 | BPTR object = *((BPTR *) lhs_address);
258 | if (IN_HEAP(object)) {
259 | GCPTR gcptr = interior_to_gcptr(object);
260 | if (WHITEP(gcptr) && valid_interior_ptr(gcptr, object)) {
261 | mark_write_vector(gcptr);
262 | }
263 | }
264 | }
265 | return((void *) (*(LPTR)lhs_address = (long) rhs));
266 | }
267 |
268 | void *RTsafe_bash(void * lhs_address, void * rhs) {
269 | BPTR object;
270 | GCPTR gcptr;
271 |
272 | if (CHECK_BASH) {
273 | object = *((BPTR *) lhs_address);
274 | if (IN_HEAP(object)) {
275 | gcptr = interior_to_gcptr(object);
276 | if WHITEP(gcptr) {
277 | Debugger("White object is escaping write_barrier!\n");
278 | }
279 | }
280 | }
281 | return((void *) (*(LPTR)lhs_address = (long) rhs));
282 | }
283 |
284 | void *RTsafe_setfInit(void * lhs_address, void * rhs) {
285 | if (CHECK_SETFINIT) {
286 | BPTR object = *((BPTR *) lhs_address);
287 | if (object != NULL) {
288 | // if ((int) object != rhs)
289 | Debugger("RTsafe_setfInit problem\n");
290 | }
291 | }
292 | return((void *) (* (LPTR) lhs_address = (long) rhs));
293 | }
294 |
295 | // This is just a version of scan_memory_segment that marks the
296 | // write_vector instead of immediately making white objects become gray.
297 | void memory_segment_write_barrier(BPTR low, BPTR high) {
298 | Debugger("HEY! I haven't been tested!\n");
299 | if (enable_write_barrier) {
300 | for (BPTR next = low; next < high; next = next + GC_POINTER_ALIGNMENT) {
301 | BPTR object = *((BPTR *) next);
302 | if (IN_HEAP(object)) {
303 | GCPTR gcptr = interior_to_gcptr(object);
304 | if (WHITEP(gcptr) && valid_interior_ptr(gcptr, object)) {
305 | mark_write_vector(gcptr);
306 | }
307 | }
308 | }
309 | }
310 | }
311 |
312 | void *RTmemcpy(void *p1, void *p2, int num_bytes) {
313 | memory_segment_write_barrier(p1, (BPTR) p1 + num_bytes);
314 | memcpy(p1, p2, num_bytes);
315 | return(p1);
316 | }
317 |
318 | void *RTrecordcpy(void *p1, void *p2, int num_bytes) {
319 | RTmemcpy(p1, p2, num_bytes);
320 | }
321 |
322 | void *RTmemset(void *p1, int data, int num_bytes) {
323 | memory_segment_write_barrier(p1, (BPTR) p1 + num_bytes);
324 | memset(p1, data, num_bytes);
325 | return(p1);
326 | }
327 |
328 | static
329 | void scan_saved_registers(int i) {
330 | // HEY! just scan saved regs that need it, not all 23 of them
331 | BPTR registers = (BPTR) saved_threads[i].registers;
332 | scan_memory_segment(registers, registers + (23 * sizeof(long)));
333 | }
334 |
335 | static
336 | void scan_saved_stack(int i) {
337 | BPTR top = (BPTR) saved_threads[i].saved_stack_base;
338 | BPTR bottom = top + saved_threads[i].saved_stack_size;
339 | BPTR ptr_aligned_top = (BPTR) ((long) top & ~(GC_POINTER_ALIGNMENT - 1));
340 | scan_memory_segment(ptr_aligned_top, bottom);
341 | }
342 |
343 | static
344 | void scan_saved_thread_state(int i) {
345 | scan_saved_registers(i);
346 | scan_saved_stack(i);
347 | }
348 |
349 | static
350 | void scan_threads() {
351 | for (int i = 0; i < total_saved_threads; i++) {
352 | scan_saved_thread_state(i);
353 | }
354 |
355 | // move this to it's own function
356 | if (0 != saved_no_write_barrier_state) {
357 | BPTR low = (BPTR) &saved_no_write_barrier_state;;
358 | BPTR high = ((BPTR) low + sizeof(long));
359 | scan_memory_segment(low, high);
360 | }
361 | }
362 |
363 | static
364 | void scan_global_roots() {
365 | for (int i = 0; i < total_global_roots; i++) {
366 | BPTR ptr = *((BPTR *) *(global_roots + i));
367 | if (IN_PARTITION(ptr)) {
368 | PPTR page = pages + PTR_TO_PAGE_INDEX(ptr);
369 | GPTR group = page->group;
370 | if (group > EXTERNAL_PAGE) {
371 | GCPTR gcptr = interior_to_gcptr_3(ptr, page, group);
372 | if (WHITEP(gcptr) && valid_interior_ptr(gcptr, ptr)) {
373 | RTmake_object_gray(gcptr);
374 | }
375 | }
376 | }
377 | }
378 | }
379 |
380 | static
381 | void scan_static_space() {
382 | BPTR next = first_static_ptr;
383 | // Ok to not acquire lock or copy at flip time???
384 | // might scan uninitalized object or more than needed this way
385 | // problaby safer to set static_frontier_at_flip in rtstop.c
386 | BPTR end = static_frontier_ptr;
387 | while (next < end) {
388 | BPTR low = next + sizeof(long);
389 | int size = *((long *) next);
390 | size = size >> LINK_INFO_BITS;
391 | next = low + size;
392 | GCPTR gcptr = (GCPTR) (low - sizeof(GC_HEADER));
393 | scan_object(gcptr, size + sizeof(GC_HEADER));
394 | }
395 | }
396 |
397 | static int total_root_scanners = 0;
398 | static void (*root_scanners[10])();
399 |
400 | static int total_custom_scanners = 0;
401 | static void (*custom_scanners[5])(void *low, void *high);
402 |
403 | void RTregister_root_scanner(void (*root_scanner)()) {
404 | root_scanners[0] = root_scanner;
405 | total_root_scanners = total_root_scanners + 1;
406 | }
407 |
408 | int RTregister_custom_scanner(void (*custom_scanner)(void *low, void *high)) {
409 | custom_scanners[0] = custom_scanner;
410 | total_custom_scanners = total_custom_scanners + 1;
411 | return(SC_CUSTOM1);
412 | }
413 |
414 | // HEY! Generalize this to allow more than 1 no_write_barrier state
415 | // to be registered.
416 | void RTregister_no_write_barrier_state(void *start, int len) {
417 | RTno_write_barrier_state_ptr = start;
418 | }
419 |
420 | static
421 | void scan_root_set() {
422 | scan_threads();
423 | scan_global_roots();
424 | scan_static_space();
425 | for (int i = 0; i < total_root_scanners; i++) {
426 | (*root_scanners[i])();
427 | }
428 | }
429 |
430 |
431 | void scan_object(GCPTR ptr, int total_size) {
432 | BPTR bptr, low, high;
433 |
434 | bptr = (BPTR) ptr;
435 | low = bptr + sizeof(GC_HEADER);
436 | high = bptr + total_size;
437 | switch (GET_STORAGE_CLASS(ptr)) {
438 | case SC_NOPOINTERS: break;
439 | case SC_POINTERS:
440 | scan_memory_segment(low, high);
441 | break;
442 | case SC_CUSTOM1:
443 | (*custom_scanners[0])(low, high);
444 | break;
445 | case SC_METADATA:
446 | scan_memory_segment_with_metadata(low, high);
447 | break;
448 | default: Debugger(0);
449 | }
450 | }
451 |
452 | static
453 | void scan_object_with_group(GCPTR ptr, GPTR group) {
454 | scan_object(ptr, group->size);
455 | SET_COLOR(ptr,marked_color);
456 | group->black = ptr;
457 | DEBUG(group->black_scanned_count = group->black_scanned_count + 1);
458 | }
459 |
460 | // HEY! Fix this up now that it's not continuation based.
461 | static
462 | void scan_gray_set() {
463 | int i, scan_count, rescan_all_groups;
464 |
465 | i = MIN_GROUP_INDEX;
466 | scan_count = 0;
467 | do {
468 | while (i <= MAX_GROUP_INDEX) {
469 | GPTR group = &groups[i];
470 | GCPTR current = group->black;
471 | // current could be gray, black, or green
472 | if ((current != NULL ) && (!(GRAYP(current)))) {
473 | current = GET_LINK_POINTER(current->prev);
474 | }
475 | while (current != NULL) {
476 | scan_object_with_group(current,group);
477 | scan_count = scan_count + 1;
478 | current = GET_LINK_POINTER(current->prev);
479 | }
480 | i = i + 1;
481 | }
482 | if (scan_count > 0) {
483 | rescan_all_groups = 1;
484 | i = MIN_GROUP_INDEX;
485 | scan_count = 0;
486 | } else {
487 | rescan_all_groups = 0;
488 | }
489 | } while (rescan_all_groups == 1);
490 | }
491 |
492 | static
493 | void flip() {
494 | assert(0 == enable_write_barrier);
495 | // No allocation allowed during a flip
496 | lock_all_free_locks();
497 | for (int i = MIN_GROUP_INDEX; i <= MAX_GROUP_INDEX; i++) {
498 | GPTR group = &groups[i];
499 | GCPTR free = group->free;
500 | if (free != NULL) {
501 | GCPTR prev = GET_LINK_POINTER(free->prev);
502 | if (prev != NULL) {
503 | SET_LINK_POINTER(prev->next,NULL); // end black set
504 | }
505 | SET_LINK_POINTER(free->prev,NULL);
506 | } else {
507 | GCPTR last = group->last;
508 | if (last != NULL) {
509 | SET_LINK_POINTER(last->next,NULL); // end black set
510 | }
511 | group->last = NULL;
512 | }
513 |
514 | // used to handle this with:
515 | // group->white = (GREENP(black) ? NULL : black)
516 | if (group->black == group->free) {
517 | // Must have no retained objects, we have nothing
518 | // available to retain this cycle
519 | group->white = NULL;
520 | } else {
521 | group->white = group->black;
522 | }
523 | group->black = group->free;
524 | group->gray = NULL;
525 |
526 | group->white_count = group->black_scanned_count + group->black_alloc_count;
527 | assert(group->white_count >= 0);
528 |
529 | group->black_scanned_count = 0;
530 | group->black_alloc_count = 0;
531 | }
532 |
533 | stop_all_mutators_and_save_state();
534 | }
535 |
536 | // The alloc counterpart to this function is init_pages_for_group.
537 | // We need to change garbage color to green now so conservative
538 | // scanning in a later gc cycle doesn't start making free objects
539 | // that look white turn gray!
540 | static
541 | void recycle_group_garbage(GPTR group) {
542 | int count = 0;
543 | GCPTR last = NULL;
544 | GCPTR next = group->white;
545 |
546 | pthread_mutex_lock(&(group->free_lock));
547 | while (next != NULL) {
548 | // Finalize code was here. Need to add it back
549 |
550 | SET_COLOR(next,GREEN);
551 | if (DETECT_INVALID_REFS) {
552 | memset((BPTR) next + sizeof(GC_HEADER),
553 | INVALID_ADDRESS,
554 | (group->size - sizeof(GC_HEADER)));
555 | }
556 | last = next;
557 | next = GET_LINK_POINTER(next->next);
558 | count = count + 1;
559 | }
560 | if (count != group->white_count) {
561 | DEBUG(printf("group->white_count is %d, actual count is %d\n",
562 | group->white_count, count));
563 | DEBUG(Debugger("group->white_count doesn't equal actual count\n"));
564 | }
565 |
566 | if (last != NULL) {
567 | SET_LINK_POINTER(last->next, NULL);
568 |
569 | if (group->free == NULL) {
570 | group->free = group->white;
571 | }
572 |
573 | if (group->black == NULL) {
574 | group->black = group->white;
575 | }
576 |
577 |
578 | if (group->last != NULL) {
579 | SET_LINK_POINTER((group->last)->next, group->white);
580 | }
581 | SET_LINK_POINTER((group->white)->prev, group->last);
582 | group->last = last;
583 | }
584 | group->white = NULL;
585 | group->white_count = 0; // no lock needed, white_count is gc only
586 | pthread_mutex_unlock(&(group->free_lock));
587 | }
588 |
589 | static
590 | void recycle_all_garbage() {
591 | assert(0 == enable_write_barrier);
592 | for (int i = MIN_GROUP_INDEX; i <= MAX_GROUP_INDEX; i++) {
593 | recycle_group_garbage(&groups[i]);
594 | }
595 | coalesce_all_free_pages();
596 | }
597 |
598 | static
599 | void full_gc() {
600 | flip();
601 | assert(1 == enable_write_barrier);
602 | scan_root_set();
603 |
604 | int mark_count = 0;
605 | do {
606 | scan_gray_set();
607 | mark_count = scan_write_vector();
608 | } while (mark_count > 0);
609 |
610 | enable_write_barrier = 0;
611 | recycle_all_garbage();
612 |
613 | gc_count = gc_count + 1;
614 | }
615 |
616 | void RTfull_gc() {
617 | full_gc();
618 | }
619 |
620 | void rtgc_loop() {
621 | while (1) {
622 | if (1 == RTatomic_gc) while (0 == run_gc);
623 | full_gc();
624 | full_gc();
625 | if (0 == (gc_count % 5000)) {
626 | //printf("gc end - gc_count %d\n", gc_count);
627 | //fflush(stdout);
628 | }
629 | if (1 == RTatomic_gc) run_gc = 0;
630 | }
631 | }
632 |
633 | int rtgc_count(void) {
634 | return(gc_count);
635 | }
636 |
637 | void init_realtime_gc() {
638 | // The gc_flip signal_handler uses this to find the thread corresponding to
639 | // the mutator pthread it is running on
640 | if (0 != pthread_key_create(&thread_key, NULL)) {
641 | Debugger("thread_key create failed!\n");
642 | }
643 |
644 | printf("Running last commit before t1/t2 branch creation\n");
645 | printf("Page size is %d\n", BYTES_PER_PAGE);
646 | printf((RTatomic_gc ? "***ATOMIC GC***\n" : "***REAL-TIME GC***\n"));
647 | #ifdef NDEBUG
648 | printf("NDEBUG is defined\n");
649 | #endif
650 | total_global_roots = 0;
651 | gc_count = 0;
652 | pthread_mutex_init(&threads_lock, NULL);
653 | pthread_mutex_init(&global_roots_lock, NULL);
654 | pthread_mutex_init(&empty_pages_lock, NULL);
655 | pthread_mutex_init(&static_frontier_ptr_lock, NULL);
656 | sem_init(&gc_semaphore, 0, 0);
657 | init_signals_for_rtgc();
658 | timerclear(&max_flip_tv);
659 | timerclear(&total_flip_tv);
660 | }
661 |
--------------------------------------------------------------------------------
/creeping.txt:
--------------------------------------------------------------------------------
1 |
2 | The Adventure of the Creeping Man
3 |
4 | Mr. Sherlock Holmes was always of opinion that I should
5 | publish the singular facts connected with Professor Presbury, if
6 | only to dispel once for all the ugly rumours which some twenty
7 | years ago agitated the university and were echoed in the learned
8 | societies of London. There were, however, certain obstacles in
9 | the way, and the true history of this curious case remained
10 | entombed in the tin box which contains so many records of my
11 | friend's adventures. Now we have at last obtained permission to
12 | ventilate the facts which formed one of the very last cases
13 | handled by Holmes before his retirement from practice. Even
14 | now a certain reticence and discretion have to be observed in
15 | laying the matter before the public.
16 | It was one Sunday evening early in September of the year
17 | 1903 that I received one of Holmes's laconic messages:
18 |
19 | Come at once if convenient -- if inconvenient come all the
20 | same. S. H.
21 |
22 | The relations between us in those latter days were peculiar. He
23 | was a man of habits, narrow and concentrated habits, and I had
24 | become one of them. As an institution I was like the violin, the
25 | shag tobacco, the old black pipe, the index books, and others
26 | perhaps less excusable. When it was a case of active work and a
27 | comrade was needed upon whose nerve he could place some
28 | reliance, my role was obvious. But apart from this I had uses. I
29 | was a whetstone for his mind. I stimulated him. He liked to think
30 | aloud in my presence. His remarks could hardly be said to be
31 | made to me -- many of them would have been as appropriately
32 | addressed to his bedstead -- but none the less, having formed the
33 | habit, it had become in some way helpful that I should register
34 | and interject. If I irritated him by a certain methodical slowness
35 | in my mentality, that irritation served only to make his own
36 | flame-like intuitions and impressions flash up the more vividly
37 | and swiftly. Such was my humble role in our alliance.
38 | When I arrived at Baker Street I found him huddled up in his
39 | armchair with updrawn knees, his pipe in his mouth and his
40 | brow furrowed with thought. It was clear that he was in the
41 | throes of some vexatious problem. With a wave of his hand he
42 | indicated my old armchair, but otherwise for half an hour he
43 | gave no sign that he was aware of my presence. Then with a start
44 | he seemed to come from his reverie, and with his usual whimsi-
45 | cal smile he greeted me back to what had once been my home.
46 | "You will excuse a certain abstraction of mind, my dear
47 | Watson," said he. "Some curious facts have been submitted to
48 | me within the last twenty-four hours, and they in turn have given
49 | rise to some speculations of a more general character. I have
50 | serious thoughts of writing a small monograph upon the uses of
51 | dogs in the work of the detective."
52 | "But surely, Holmes, this has been explored," said I.
53 | "Bloodhounds -- sleuth-hounds --"
54 | "No, no, Watson, that side of the matter is, of course,
55 | obvious. But there is another which is far more subtle. You may
56 | recollect that in the case which you, in your sensational way,
57 | coupled with the Copper Beeches, I was able, by watching the
58 | mind of the child, to form a deduction as to the criminal habits
59 | of the very smug and respectable father."
60 | "Yes, I remember it well."
61 | "My line of thoughts about dogs is analogous. A dog reflects
62 | the family life. Whoever saw a frisky dog in a gloomy family, or
63 | a sad dog in a happy one? Snarling people have snarling dogs,
64 | dangerous people have dangerous ones. And their passing moods
65 | may reflect the passing moods of others."
66 | I shook my head. "Surely, Holmes, this is a little far-fetched,"
67 | said I.
68 | He had refilled his pipe and resumed his seat, taking no notice
69 | of my comment.
70 | "The practical application of what I have said is very close to
71 | the problem which I am investigating. It is a tangled skein, you
72 | understand. and I am looking for a loose end. One possible loose
73 | end lies in the question: Why does Professor Presbury's wolf-
74 | hound, Roy, endeavour to bite him?"
75 | I sank back in my chair in some disappointment. Was it for so
76 | trivial a question as this that I had been summoned from my
77 | work? Holmes glanced across at me.
78 | "The same old Watson!" said he. "You never learn that the
79 | gravest issues may depend upon the smallest things. But is it not
80 | on the face of it strange that a staid, elderly philosopher -- you've
81 | heard of Presbury, of course, the famous Camford physiologist? --
82 | that such a man, whose friend has been his devoted wolf-
83 | hound, should now have been twice attacked by his own dog?
84 | What do you make of it?"
85 | "The dog is ill."
86 | "Well, that has to be considered. But he attacks no one else,
87 | nor does he apparently molest his master, save on very special
88 | occasions. Curious, Watson -- very curious. But young Mr. Ben-
89 | nett is before his time if that is his ring. I had hoped to have a
90 | longer chat with you before he came."
91 | There was a quick step on the stairs, a sharp tap at the door
92 | and a moment later the new client presented himself. He was a
93 | tall, handsome youth about thirty, well dressed and elegant, but
94 | with something in his bearing which suggested the shyness of the
95 | student rather than the self-possession of the man of the world.
96 | He shook hands with Holmes, and then looked with some sur-
97 | prise at me.
98 | "This matter is very delicate, Mr. Holmes," he said. "Con-
99 | sider the relation in which I stand to Professor Presbury both
100 | privately and publicly. I really can hardly justify myself if I
101 | speak before any third person."
102 | "Have no fear, Mr. Bennett. Dr. Watson is the very soul of
103 | discretion, and I can assure you that this is a matter in which I
104 | am very likely to need an assistant."
105 | "As you like, Mr. Holmes. You will, I am sure, understand
106 | my having some reserves in the matter."
107 | "You will appreciate it, Watson, when I tell you that this
108 | gentleman, Mr. Trevor Bennett, is professional assistant to the
109 | great scientist, lives under his roof, and is engaged to his only
110 | daughter. Certainly we must agree that the professor has every
111 | claim upon his loyalty and devotion. But it may best be shown
112 | by taking the necessary steps to clear up this strange mystery."
113 | "I hope so, Mr. Holmes. That is my one object. Does Dr.
114 | Watson know the situation?"
115 | "I have not had time to explain it."
116 | "Then perhaps I had better go over the ground again before
117 | explaining some fresh developments."
118 | "I will do so myself," said Holmes, "in order to show that I
119 | have the events in their due order. The professor, Watson, is a
120 | man of European reputation. His life has been academic. There
121 | has never been a breath of scandal. He is a widower with one
122 | daughter, Edith. He is, I gather, a man of very virile and
123 | positive, one might almost say combative, character. So the
124 | matter stood until a very few months ago.
125 | "Then the current of his life was broken. He is sixty-one years
126 | of age, but he became engaged to the daughter of Professor
127 | Morphy, his colleague in the chair of comparative anatomy. It
128 | was not, as I understand, the reasoned courting of an elderly man
129 | but rather the passionate frenzy of youth, for no one could have
130 | shown himself a more devoted lover. The lady, Alice Morphy,
131 | was a very perfect girl both in mind and body, so that there was
132 | every excuse for the professor's infatuation. None the less, it did
133 | not meet with full approval in his own family."
134 | "We thought it rather excessive," said our visitor.
135 | "Exactly. Excessive and a little violent and unnatural. Profes-
136 | sor Presbury was rich, however, and there was no objection upon
137 | the part of the father. The daughter, however, had other views,
138 | and there were already several candidates for her hand, who, if
139 | they were less eligible from a worldly point of view, were at
140 | least more of an age. The girl seemed to like the professor in
141 | spite of his eccentricities. It was only age which stood in the
142 | way.
143 | "About this time a little mystery suddenly clouded the normal
144 | routine of the professor's life. He did what he had never done
145 | before. He left home and gave no indication where he was
146 | going. He was away a fortnight and returned looking rather
147 | travel-worn. He made no allusion to where he had been, al-
148 | though he was usually the frankest of men. It chanced, however,
149 | that our client here, Mr. Bennett, received a letter from a fellow-
150 | student in Prague, who said that he was glad to have seen
151 | Professor Presbury there, although he had not been able to talk to
152 | him. Only in this way did his own household learn where he had
153 | been.
154 | "Now comes the point. From that time onward a curious
155 | change came over the professor. He became furtive and sly.
156 | Those around him had always the feeling that he was not the
157 | man that they had known, but that he was under some shadow
158 | which had darkened his higher qualities. His intellect was not
159 | affected. His lectures were as brilliant as ever. But always there
160 | was something new, something sinister and unexpected. His
161 | daughter, who was devoted to him, tried again and again to
162 | resume the old relations and to penetrate this mask which her
163 | father seemed to have put on. You, sir, as I understand, did the
164 | same -- but all was in vain. And now, Mr. Bennett, tell in your
165 | own words the incident of the letters."
166 | "You must understand, Dr. Watson, that the professor had no
167 | secrets from me. If I were his son or his younger brother I could
168 | not have more completely enjoyed his confidence. As his secre-
169 | tary I handled every paper which came to him, and I opened and
170 | subdivided his letters. Shortly after his return all this was changed.
171 | He told me that certain letters might come to him from London
172 | which would be marked by a cross under the stamp. These were
173 | to be set aside for his own eyes only. I may say that several of
174 | these did pass through my hands, that they had the E. C. mark,
175 | and were in an illiterate handwriting. If he answered them at all
176 | the answers did not pass through my hands nor into the letter-
177 | basket in which our correspondence was collected."
178 | "And the box," said Holmes.
179 | "Ah, yes, the box. The professor brought back a little wooden
180 | box from his travels. It was the one thing which suggested a
181 | Continental tour, for it was one of those quaint carved things
182 | which one associates with Germany. This he placed in his instru-
183 | ment cupboard. One day, in looking for a canula, I took up the
184 | box. To my surprise he was very angry, and reproved me in
185 | words which were quite savage for my curiosity. It was the first
186 | time such a thing had happened, and I was deeply hurt. I
187 | endeavoured to explain that it was a mere accident that I had
188 | touched the box, but all the evening I was conscious that he
189 | looked at me harshly and that the incident was rankling in his
190 | mind." Mr. Bennett drew a little diary book from his pocket.
191 | "That was on July 2d," said he.
192 | "You are certainly an admirable witness," said Holmes. "I
193 | may need some of these dates which you have noted."
194 | "I learned method among other things from my great teacher.
195 | From the time that I observed abnormality in his behaviour I felt
196 | that it was my duty to study his case. Thus I have it here that it
197 | was on that very day, July 2d, that Roy attacked the professor as
198 | he came from his study into the hall. Again, on July 11th, there
199 | was a scene of the same sort, and then I have a note of yet
200 | another upon July 20th. After that we had to banish Roy to the
201 | stables. He was a dear, affectionate animal -- but I fear I weary
202 | you."
203 | Mr. Bennett spoke in a tone of reproach, for it was very clear
204 | that Holmes was not listening. His face was rigid and his eyes
205 | gazed abstractedly at the ceiling. With an effort he recovered
206 | himself.
207 | "Singular! Most singular!" he murmured. "These details were
208 | new to me, Mr. Bennett. I think we have now fairly gone over
209 | the old ground, have we not? But you spoke of some fresh
210 | developments."
211 | The pleasant, open face of our visitor clouded over, shadowed
212 | by some grim remembrance. "What I speak of occurred the
213 | night before last," said he. "I was lying awake about two in the
214 | morning, when I was aware of a dull muffled sound coming
215 | from the passage. I opened my door and peeped out. I should
216 | explain that the professor sleeps at the end of the passage --"
217 | "The date being?" asked Holmes.
218 | Our visitor was clearly annoyed at so irrelevant an interruption.
219 | "I have said, sir, that it was the night before last -- that is,
220 | September 4th."
221 | Holmes nodded and smiled.
222 | "Pray continue," said he.
223 | "He sleeps at the end of the passage and would have to pass
224 | my door in order to reach the staircase. It was a really terrifying
225 | experience, Mr. Holmes. I think that I am as strong-nerved as
226 | my neighbours, but I was shaken by what I saw. The passage
227 | was dark save that one window halfway along it threw a patch of
228 | light. I could see that something was coming along the passage,
229 | something dark and crouching. Then suddenly it emerged into
230 | the light, and I saw that it was he. He was crawling, Mr.
231 | Holmes -- crawling! He was not quite on his hands and knees. I
232 | should rather say on his hands and feet, with his face sunk
233 | between his hands. Yet he seemed to move with ease. I was so
234 | paralyzed by the sight that it was not until he had reached my
235 | door that I was able to step forward and ask if I could assist him.
236 | His answer was extraordinary. He sprang up, spat out some
237 | atrocious word at me, and hurried on past me, and down the
238 | staircase. I waited about for an hour, but he did not come back.
239 | It must have been daylight before he regained his room."
240 | "Well, Watson, what make you of that?" asked Holmes with
241 | the air of the pathologist who presents a rare specimen.
242 | "Lumbago, possibly. I have known a severe attack make a
243 | man walk in just such a way, and nothing would be more trying
244 | to the temper."
245 | "Good, Watson! You always keep us flat-footed on the ground.
246 | But we can hardly accept lumbago, since he was able to stand
247 | erect in a moment."
248 | "He was never better in health," said Bennett. "In fact, he is
249 | stronger than I have known him for years. But there are the
250 | facts, Mr. Holmes. It is not a case in which we can consult the
251 | police, and yet we are utterly at our wit's end as to what to do,
252 | and we feel in some strange way that we are drifting towards
253 | disaster. Edith -- Miss Presbury -- feels as I do, that we cannot
254 | wait passively any longer."
255 | "It is certainly a very curious and suggestive case. What do
256 | you think, Watson?"
257 | "Speaking as a medical man," said I, "it appears to be a
258 | case for an alienist. The old gentleman's cerebral processes
259 | were disturbed by the love affair. He made a journey abroad
260 | in the hope of breaking himself of the passion. His letters
261 | and the box may be connected with some other private trans-
262 | action -- a loan, perhaps, or share cenificates, which are in
263 | the box."
264 | "And the wolfhound no doubt disapproved of the financial
265 | bargain. No, no, Watson, there is more in it than this. Now, I
266 | can only suggest --"
267 | What Sherlock Holmes was about to suggest will never be
268 | known, for at this moment the door opened and a young lady
269 | was shown into the room. As she appeared Mr. Bennett sprang
270 | up with a cry and ran forward with his hands out to meet those
271 | which she had herself outstretched.
272 | "Edith, dear! Nothing the matter, I hope?"
273 | "I felt I must follow you. Oh, Jack, I have been so dreadfully
274 | frightened! It is awful to be there alone."
275 | "Mr. Holmes, this is the young lady I spoke of. This is my
276 | fiancee."
277 | "We were gradually coming to that conclusion, were we not,
278 | Watson?" Holmes answered with a smile. "I take it, Miss
279 | Presbury, that there is some fresh development in the case, and
280 | that you thought we should know?"
281 | Our new visitor, a bright, handsome girl of a conventional
282 | English type, smiled back at Holmes as she seated herself beside
283 | Mr. Bennett.
284 | "When I found Mr. Bennett had left his hotel I thought I
285 | should probably find him here. Of course, he had told me that he
286 | would consult you. But, oh, Mr. Holmes, can you do nothing for
287 | my poor father?"
288 | "I have hopes, Miss Presbury, but the case is still obscure.
289 | Perhaps what you have to say may throw some fresh light upon
290 | it."
291 | "It was last night, Mr. Holmes. He had been very strange all
292 | day. I am sure that there are times when he has no recollection of
293 | what he does. He lives as in a strange dream. Yesterday was
294 | such a day. It was not my father with whom I lived. His outward
295 | shell was there, but it was not really he."
296 | "Tell me what happened."
297 | "I was awakened in the night by the dog barking most furi-
298 | ously. Poor Roy, he is chained now near the stable. I may say
299 | that I always sleep with my door locked; for, as Jack -- as Mr.
300 | Bennett -- will tell you, we all have a feeling of impending
301 | danger. My room is on the second floor. It happened that the
302 | blind was up in my window, and there was bright moonlight
303 | outside. As I lay with my eyes fixed upon the square of light,
304 | listening to the frenzied barkings of the dog, I was amazed to see
305 | my father's face looking in at me. Mr. Holmes, I nearly died of
306 | surprise and horror. There it was pressed against the window-
307 | pane, and one hand seemed to be raised as if to push up the
308 | window. If that window had opened, I think I should have gone
309 | mad. It was no delusion, Mr. Holmes. Don't deceive yourself by
310 | thinking so. I dare say it was twenty seconds or so that I lay
311 | paralyzed and watched the face. Then it vanished, but I could
312 | not -- I could not spring out of bed and look out after it. I lay
313 | cold and shivering till morning. At breakfast he was sharp and
314 | fierce in manner, and made no allusion to the adventure of the
315 | night. Neither did I, but I gave an excuse for coming to town --
316 | and here I am."
317 | Holmes looked thoroughly surprised at Miss Presbury's narrative.
318 | "My dear young lady, you say that your room is on the
319 | second floor. Is there a long ladder in the garden?"
320 | "No, Mr. Holmes, that is the amazing part of it. There is no
321 | possible way of reaching the window -- and yet he was there."
322 | "The date being September 5th," said Holmes. "That cer-
323 | tainly complicates matters."
324 | It was the young lady's turn to look surprised. "This is the
325 | second time that you have alluded to the date, Mr. Holmes,"
326 | said Bennett. "Is it possible that it has any bearing upon the
327 | case?"
328 | "It is possible -- very possible -- and yet I have not my full
329 | material at present."
330 | "Possibly you are thinking of the connection between insanity
331 | and phases of the moon?"
332 | "No, I assure you. It was quite a different line of thought.
333 | Possibly you can leave your notebook with me, and I will check
334 | the dates. Now I think, Watson, that our line of action is
335 | perfectly clear. This young lady has informed us -- and I have the
336 | greatest confidence in her intuition -- that her father remembers
337 | little or nothing which occurs upon certain dates. We will there-
338 | fore call upon him as if he had given us an appointment upon
339 | such a date. He will put it down to his own lack of memory.
340 | Thus we will open our campaign by having a good close view of
341 | him."
342 | "That is excellent," said Mr. Bennett. "I warn you, however,
343 | that the professor is irascible and violent at times."
344 | Holmes smiled. "There are reasons why we should come at
345 | once -- very cogent reasons if my theories hold good. To-morrow,
346 | Mr. Bennett, will certainly see us in Camford. There is, if I
347 | remember right, an inn called the Chequers where the port used to
348 | be above mediocrity and the linen was above reproach. I think,
349 | Watson, that our lot for the next few days might lie in less
350 | pleasant places."
351 | Monday morning found us on our way to the famous univer-
352 | sity town -- an easy effort on the part of Holmes, who had no
353 | roots to pull up, but one which involved frantic planning and
354 | hurrying on my part, as my practice was by this time not
355 | inconsiderable. Holmes made no allusion to the case until after
356 | we had deposited our suitcases at the ancient hostel of which he
357 | had spoken.
358 | "I think, Watson, that we can catch the professor just before
359 | lunch. He lectures at eleven and should have an interval at
360 | home."
361 | "What possible excuse have we for calling?"
362 | Holmes glanced at his notebook.
363 | "There was a period of excitement upon August 26th. We will
364 | assume that he is a little hazy as to what he does at such times. If
365 | we insist that we are there by appointment I think he will hardly
366 | venture to contradict us. Have you the effrontery necessary to
367 | put it through?"
368 | "We can but try."
369 | "Excellent, Watson! Compound of the Busy Bee and Excel-
370 | sior. We can but try -- the motto of the firm. A friendly native
371 | will surely guide us."
372 | Such a one on the back of a smart hansom swept us past a row
373 | of ancient colleges and, finally turning into a tree-lined drive,
374 | pulled up at the door of a charming house, girt round with lawns
375 | and covered with purple wistaria. Professor Presbury was cer-
376 | tainly surrounded with every sign not only of comfort but of
377 | luxury. Even as we pulled up, a grizzled head appeared at the
378 | front window, and we were aware of a pair of keen eyes from
379 | under shaggy brows which surveyed us through large horn glasses.
380 | A moment later we were actually in his sanctum, and the myste-
381 | rious scientist, whose vagaries had brought us from London, was
382 | standing before us. There was certainly no sign of eccentricity
383 | either in his manner or appearance, for he was a portly, large-
384 | featured man, grave, tall, and frock-coated, with the dignity of
385 | bearing which a lecturer needs. His eyes were his most remark-
386 | able feature, keen, observant, and clever to the verge of cunning.
387 | He looked at our cards. "Pray sit down, gentlemen. What can
388 | I do for you?"
389 | Mr. Holmes smiled amiably.
390 | "It was the question which I was about to put to you, Professor."
391 | "To me, sir!"
392 | "Possibly there is some mistake. I heard through a second
393 | person that Professor Presbury of Camford had need of my
394 | services."
395 | "Oh, indeed!" It seemed to me that there was a malicious
396 | sparkle in the intense gray eyes. "You heard that, did you? May
397 | I ask the name of your informant?"
398 | "I am sorry, Professor, but the matter was rather confidential.
399 | If I have made a mistake there is no harm done. I can only
400 | express my regret."
401 | "Not at all. I should wish to go funher into this matter. It
402 | interests me. Have you any scrap of writing, any letter or
403 | telegram, to bear out your assertion?"
404 | "No, I have not."
405 | "I presume that you do not go so far as to assert that I
406 | summoned you?"
407 | "I would rather answer no questions," said Holmes.
408 | "No, I dare say not," said the professor with asperity. "How-
409 | ever, that particular one can be answered very easily without
410 | your aid."
411 | He walked across the room to the bell. Our London friend
412 | Mr. Bennett, answered the call.
413 | "Come in, Mr. Bennett. These two gentlemen have come
414 | from London under the impression that they have been sum-
415 | moned. You handle all my correspondence. Have you a note of
416 | anything going to a person named Holmes?"
417 | "No, sir," Bennett answered with a flush.
418 | "That is conclusive," said the professor, glaring angrily at my
419 | companion. "Now, sir" -- he leaned forward with his two hands
420 | upon the table --" it seems to me that your position is a very
421 | questionable one."
422 | Holmes shrugged his shoulders.
423 | "I can only repeat that I am sorry that we have made a
424 | needless intrusion."
425 | "Hardly enough, Mr. Holmes!" the old man cried in a high
426 | screaming voice, with extraordinary malignancy upon his face.
427 | He got between us and the door as he spoke, and he shook his
428 | two hands at us with furious passion. "You can hardly get out of
429 | it so easily as that." His face was convulsed, and he grinned and
430 | gibbered at us in his senseless rage. I am convinced that we
431 | should have had to fight our way out of the room if Mr. Bennett
432 | had not intervened.
433 | "My dear Professor," he cried, "consider your position!
434 | Consider the scandal at the university! Mr. Holmes is a well-
435 | known man. You cannot possibly treat him with such discourtesy."
436 | Sulkily our host -- if I may call him so -- cleared the path to the
437 | door. We were glad to find ourselves outside the house and in
438 | the quiet of the tree-lined drive. Holmes seemed great!y amused
439 | by the episode.
440 | "Our learned friend's nerves are somewhat out of order," said
441 | he. "Perhaps our intrusion was a little crude, and yet we have
442 | gained that personal contact which I desired. But, dear me,
443 | Watson, he is surely at our heels. The villain still pursues us."
444 | There were the sounds of running feet behind, but it was, to
445 | my relief, not the formidable professor but his assistant who
446 | appeared round the curve of the drive. He came panting up to us.
447 | "I am so sorry, Mr. Holmes. I wished to apologize."
448 | "My dear sir, there is no need. It is all in the way of
449 | professional experience."
450 | "I have never seen him in a more dangerous mood. But he
451 | grows more sinister. You can understand now why his daughter
452 | and I are alarmed. And yet his mind is perfectly clear."
453 | "Too clear!" said Holmes. "That was my miscalculation. It
454 | is evident that his memory is much more reliable than I had
455 | thought. By the way, can we, before we go, see the window of
456 | Miss Presbury's room?"
457 | Mr. Bennett pushed his way through some shrubs, and we had
458 | a view of the side of the house.
459 | "It is there. The second on the left."
460 | "Dear me, it seems hardly accessible. And yet you will
461 | observe that there is a creeper below and a water-pipe above
462 | which give some foothold."
463 | "I could not climb it myself," said Mr. Bennett.
464 | "Very likely. It would certainly be a dangerous exploit for
465 | any normal man."
466 | "There was one other thing I wish to tell you, Mr. Holmes. I
467 | have the address of the man in London to whom the professor
468 | writes. He seems to have written this morning, and I got it from
469 | his blotting-paper. It is an ignoble position for a trusted secre-
470 | tary, but what else can I do?"
471 | Holmes glanced at the paper and put it into his pocket.
472 | "Dorak -- a curious name. Slavonic, I imagine. Well, it is an
473 | important link in the chain. We return to London this afternoon,
474 | Mr. Bennett. I see no good purpose to be served by our remain-
475 | ing. We cannot arrest the professor because he has done no
476 | crime, nor can we place him under constraint, for he cannot be
477 | proved to be mad. No action is as yet possible."
478 | "Then what on earth are we to do?"
479 | "A little patience, Mr. Bennett. Things will soon develop.
480 | Unless I am mistaken, next Tuesday may mark a crisis. Certainly
481 | we shall be in Camford on that day. Meanwhile, the general
482 | position is undeniably unpleasant, and if Miss Presbury can
483 | prolong her visit "
484 | "That is easy."
485 | "Then let her stay till we can assure her that all danger is past.
486 | Meanwhile, let him have his way and do not cross him. So long
487 | as he is in a good humour all is well."
488 | "There he is!" said Bennett in a startled whisper. Looking
489 | between the branches we saw the tall, erect figure emerge from
490 | the hall door and look around him. He stood leaning forward, his
491 | hands swinging straight before him, his head turning from side to
492 | side. The secretary with a last wave slipped off among the trees,
493 | and we saw him presently rejoin his employer, the two entering
494 | the house together in what seemed to be animated and even
495 | excited conversation.
496 | "I expect the old gentleman has been putting two and two
497 | together," said Holmes as we walked hotelward. "He struck me
498 | as having a particularly clear and logical brain from the little I
499 | saw of him. Explosive, no doubt, but then from his point of view
500 | he has something to explode about if detectives are put on his
501 | track and he suspects his own household of doing it. I rather
502 | fancy that friend Bennett is in for an uncomfortable time."
503 | Holmes stopped at a post-office and sent off a telegram on our
504 | way. The answer reached us in the evening, and he tossed it
505 | across to me.
506 |
507 | Have visited the Commercial Road and seen Dorak. Suave
508 | person, Bohemian, elderly. Keeps large general store.
509 | MERCER.
510 |
511 | "Mercer is since your time," said Holmes. "He is my general
512 | utility man who looks up routine business. It was important to
513 | know something of the man with whom our professor was so
514 | secretly corresponding. His nationality connects up with the
515 | Prague visit."
516 | "Thank goodness that something connects with something,"
517 | said I. "At present we seem to be faced by a long series of
518 | inexplicable incidents with no bearing upon each other."For
519 | example, what possible connection can there be between an
520 | angry wolfhound and a visit to Bohemia, or either of them with a
521 | man crawling down a passage at night? As to your dates, that is
522 | the biggest mystification of all."
523 | Holmes smiled and rubbed his hands. We were, I may say,
524 | seated in the old sitting-room of the ancient hotel, with a bottle
525 | of the famous vintage of which Holmes had spoken on the table
526 | between us.
527 | "Well, now, let us take the dates first," said he, his finger-
528 | tips together and his manner as if he were addressing a class.
529 | "This excellent young man's diary shows that there was trouble
530 | upon July 2d, and from then onward it seems to have been at
531 | nine-day intervals, with, so far as I remember, only one excep-
532 | tion. Thus the last outbreak upon Friday was on September 3d,
533 | which also falls into the series, as did August 26th, which
534 | preceded it. The thing is beyond coincidence."
535 | I was forced to agree.
536 | "Let us, then, form the provisional theory that every nine
537 | days the professor takes some strong drug which has a passing
538 | but highly poisonous effect. His naturally violent nature is inten-
539 | sified by it. He learned to take this drug while he was in Prague,
540 | and is now supplied with it by a Bohemian intermediary in
541 | London. This all hangs together, Watson!"
542 | "But the dog, the face at the window, the creeping man in the
543 | passage?"
544 | "Well, well, we have made a beginning. I should not expect
545 | any fresh developments until next Tuesday. In the meantime we
546 | can only keep in touch with friend Bennett and enjoy the ameni-
547 | ties of this charming town."
548 | In the morning Mr. Bennett slipped round to bring us the latest
549 | report. As Holmes had imagined, times had not been easy with
550 | him. Without exactly accusing him of being responsible for our
551 | presence, the professor had been very rough and rude in his
552 | speech, and evidently felt some strong grievance. This morning
553 | he was quite himself again, however, and had delivered his usual
554 | brilliant lecture to a crowded class. "Apart from his queer fits,"
555 | said Bennett, "he has actually more energy and vitality than I
556 | can ever remember, nor was his brain ever clearer. But it's not
557 | he -- it's never the man whom we have known."
558 | "I don't think you have anything to fear now for a week at
559 | least," Holmes answered. "I am a busy man, and Dr. Watson
560 | has his patients to attend to. Let us agree that we meet here at this
561 | hour next Tuesday, and I shall be surprised if before we leave
562 | you again we are not able to explain, even if we cannot perhaps
563 | put an end to, your troubles. Meanwhile, keep us posted in what
564 | occurs."
565 | I saw nothing of my friend for the next few days, but on the
566 | following Monday evening I had a short note asking me to meet
567 | him next day at the train. From what he told me as we travelled
568 | up to Camford all was well, the peace of the professor's house
569 | had been unruffled, and his own conduct perfectly normal. This
570 | also was the report which was given us by Mr. Bennett himself
571 | when he called upon us that evening at our old quarters in the
572 | Chequers. "He heard from his London correspondent to-day.
573 | There was a letter and there was a small packet, each with the
574 | cross under the stamp which warned me not to touch them.
575 | There has been nothing else."
576 | "That may prove quite enough," said Holmes grimly. "Now,
577 | Mr. Bennett, we shall, I think, come to some conclusion to-
578 | night. If my deductions are correct we should have an opportu-
579 | nity of bringing matters to a head. In order to do so it is
580 | necessary to hold the professor under observation. I would sug-
581 | gest, therefore, that you remain awake and on the lookout.
582 | Should you hear him pass your door, do not interrupt him, but
583 | follow him as discreetly as you can. Dr. Watson and I will not
584 | be far off. By the way, where is the key of that little box of
585 | which you spoke?"
586 | "Upon his watch-chain."
587 | "I fancy our researches must lie in that direction. At the worst
588 | the lock should not be very formidable. Have you any other
589 | able-bodied man on the premises?"
590 | "There is the coachman, Macphail."
591 | "Where does he sleep?"
592 | "Over the stables."
593 | "We might possibly want him. Well, we can do no more until
594 | we see how things develop, Good-bye -- but I expect that we
595 | shall see you before morning."
596 | It was nearly midnight before we took our station among some
597 | bushes immediately opposite the hall door of the professor. It
598 | was a fine night, but chilly, and we were glad of our warm
599 | overcoats. There was a breeze, and clouds were scudding across
600 | the sky, obscuring from time to time the half-moon. It would
601 | have been a dismal vigil were it not for the expectation and
602 | excitement which carried us along, and the assurance of my
603 | comrade that we had probably reached the end of the strange
604 | sequence of events which had engaged our attention.
605 | "If the cycle of nine days holds good then we shall have the
606 | professor at his worst to-night," said Holmes. "The fact that
607 | these strange symptoms began after his visit to Prague, that he is
608 | in secret correspondence with a Bohemian dealer in London,
609 | who presumably represents someone in Prague, and that he
610 | received a packet from him this very day, all point in one
611 | direction. What he takes and why he takes it are still beyond our
612 | ken, but that it emanates in some way from Prague is clear
613 | enough. He takes it under definite directions which regulate this
614 | ninth-day system, which was the first point which attracted my
615 | attention. But his symptoms are most remarkable. Did you ob-
616 | serve his knuckles?"
617 | I had to confess that I did not.
618 | "Thick and horny in a way which is quite new in my experi-
619 | ence. Always look at the hands first, Watson. Then cuffs, trouser-
620 | knees, and boots. Very curious knuckles which can only be
621 | explained by the mode of progression observed by --" Holmes
622 | paused and suddenly clapped his hand to his forehead. "Oh,
623 | Watson, Watson, what a fool I have been! It seems incredible,
624 | and yet it must be true. All points in one direction. How could I
625 | miss seeing the connection of ideas? Those knuckles how could
626 | I have passed those knuckles? And the dog! And the ivy! It's
627 | surely time that I disappeared into that little farm of my dreams.
628 | Look out, Watson! Here he is! We shall have the chance of
629 | seeing for ourselves."
630 | The hall door had slowly opened, and against the lamplit
631 | background we saw the tall figure of Professor Presbury. He was
632 | clad in his dressing gown. As he stood outlined in the doorway
633 | he was erect but leaning forward with dangling arms, as when
634 | we saw him last.
635 | Now he stepped forward into the drive, and an extraordinary
636 | change came over him. He sank down into a crouching position
637 | and moved along upon his hands and feet, skipping every now
638 | and then as if he were overflowing with energy and vitality. He
639 | moved along the face of the house and then round the corner. As
640 | he disappeared Bennett slipped through the hall door and softly
641 | followed him.
642 | "Come, Watson, come!" cried Holmes, and we stole as softly
643 | as we could through the bushes until we had gained a spot
644 | whence we could see the other side of the house, which was
645 | bathed in the light of the half-moon. The professor was clearly
646 | visible crouching at the foot of the ivy-covered wall. As we
647 | watched him he suddenly began with incredible agility to ascend
648 | it. From branch to branch he sprang, sure of foot and firm of
649 | grasp, climbing apparently in mere joy at his own powers, with
650 | no definite object in view. With his dressing-gown flapping on
651 | each side of him, he looked like some huge bat glued against the
652 | side of his own house, a great square dark patch upon the
653 | moonlit wall. Presently he tired of this amusement, and, drop-
654 | ping from branch to branch, he squatted down into the old
655 | attitude and moved towards the stables, creeping along in the
656 | same strange way as before. The wolfhound was out now,
657 | barking furiously, and more excited than ever when it actually
658 | caught sight of its master. It was straining on its chain and
659 | quivering with eagerness and rage. The professor squatted down
660 | very deliberately just out of reach of the hound and began to
661 | provoke it in every possible way. He took handfuls of pebbles
662 | from the drive and threw them in the dog's face, prodded him
663 | with a stick which he had picked up, flicked his hands about
664 | only a few inches from the gaping mouth, and endeavoured in
665 | every way to increase the animal's fury, which was already
666 | beyond all control. In all our adventures I do not know that I
667 | have ever seen a more strange sight than this impassive and still
668 | dignified figure crouching frog-like upon the ground and goading
669 | to a wilder exhibition of passion the maddened hound, which
670 | ramped and raged in front of him, by all manner of ingenious
671 | and calculated cruelty.
672 | And then in a moment it happened! It was not the chain that
673 | broke, but it was the collar that slipped, for it had been made for
674 | a thick-necked Newfoundland. We heard the rattle of falling
675 | metal, and the next instant dog and man were rolling on the
676 | ground together, the one roaring in rage, the other screaming in a
677 | strange shrill falsetto of terror. It was a very narrow thing for the
678 | professor's life. The savage creature had him fairly by the throat,
679 | its fangs had bitten deep, and he was senseless before we could
680 | reach them and drag the two apart. It might have been a danger-
681 | ous task for us, but Bennett's voice and presence brought the
682 | great wolflhound instantly to reason. The uproar had brought the
683 | sleepy and astonished coachman from his room above the sta-
684 | bles. "I'm not surprised," said he, shaking his head. "I've seen
685 | him at it before. I knew the dog would get him sooner or later."
686 | The hound was secured, and together we carried the professor
687 | up to his room, where Bennett, who had a medical degree,
688 | helped me to dress his torn throat. The sharp teeth had passed
689 | dangerously near the carotid artery, and the haemorrhage was
690 | serious. In half an hour the danger was past, I had given the
691 | patient an injection of morphia, and he had sunk into deep sleep.
692 | Then, and only then, were we able to look at each other and to
693 | take stock of the situation.
694 | "I think a first-class surgeon should see him," said I.
695 | "For God's sake, no!" cried Bennett. "At present the scandal
696 | is confined to our own household. It is safe with us. If it gets
697 | beyond these walls it will never stop. Consider his position at the
698 | university, his European reputation, the feelings of his daughter."
699 | "Quite so," said Holmes. "I think it may be quite possible to
700 | keep the matter to ourselves, and also to prevent its recurrence
701 | now that we have a free hand. The key from the watch-chain,
702 | Mr. Bennett. Macphail will guard the patient and let us know if
703 | there is any change. Let us see what we can find in the profes-
704 | sor's mysterious box."
705 | There was not much, but there was enough -- an empty phial,
706 | another nearly full, a hypodermic syringe, several letters in a
707 | crabbed, foreign hand. The marks on the envelopes showed that
708 | they were those which had disturbed the routine of the secretary,
709 | and each was dated from the Commercial Road and signed "A.
710 | Dorak." They were mere invoices to say that a fresh bottle was
711 | being sent to Professor Presbury, or receipt to acknowledge
712 | money. There was one other envelope, however, in a more
713 | educated hand and bearing the Austrian stamp with the postmark
714 | of Prague. "Here we have our material!" cried Holmes as he
715 | tore out the enclosure.
716 |
717 | HONOURED COLLEAGUE [it ran]:
718 | Since your esteemed visit I have thought much of your case,
719 | and though in your circumstances there are some special
720 | reasons for the treatment, I would none the less enjoin
721 | caution, as my results have shown that it is not without
722 | danger of a kind.
723 | It is possible that the serum of anthropoid would have
724 | been better. I have, as I explained to you, used black-faced
725 | langur because a specimen was accessible. Langur is, of
726 | course, a crawler and climber, while anthropoid walks
727 | erect and is in all ways nearer.
728 | I beg you to take every possible precaution that there be
729 | no premature revelation of the process. I have one other
730 | client in England, and Dorak is my agent for both.
731 | Weekly reports will oblige.
732 | Yours with high esteem,
733 | H. LOWENSTEIN.
734 |
735 | Lowenstein! The name brought back to me the memory of
736 | some snippet from a newspaper which spoke of an obscure
737 | scientist who was striving in some unknown way for the secret of
738 | rejuvenescence and the elixir of life. Lowenstein of Prague!
739 | Lowenstein with the wondrous strength-giving serum, tabooed
740 | by the profession because he refused to reveal its source. In a
741 | few words I said what I remembered. Bennett had taken a
742 | manual of zoology from the shelves. " 'Langur.' " he read.
743 | " 'the great black-faced monkey of the Himalayan slopes, big-
744 | gest and most human of climbing monkeys. Many details are
745 | added. Well, thanks to you, Mr. Holmes, it is very clear that we
746 | have traced the evil to its source."
747 | "The real source," said Holmes, "lies, of course, in that
748 | untimely love affair which gave our impetuous professor the idea
749 | that he could only gain his wish by turning himself into a
750 | younger man. When one tries to rise above Nature one is liable
751 | to fall below it. The highest type of man may revert to the
752 | animal if he leaves the straight road of destiny." He sat musing
753 | for a little with the phial in his hand, looking at the clear liquid
754 | within. "When I have written to this man and told him that I
755 | hold him criminally responsible for the poisons which he cir-
756 | culates, we will have no more trouble. But it may recur. Others
757 | may find a better way. There is danger there -- a very real danger
758 | to humanity. Consider, Watson, that the material, the sensual,
759 | the worldly would all prolong their worthless lives. The spiritual
760 | would not avoid the call to something higher. It would be the
761 | survival of the least fit. What sort of cesspool may not our poor
762 | world become?" Suddenly the dreamer disappeared, and Holmes,
763 | the man of action, sprang from his chair. "I think there is
764 | nothing more to be said, Mr. Bennett. The various incidents will
765 | now fit themselves easily into the general scheme. The dog, of
766 | course, was aware of the change far more quickly than you. His
767 | smell would insure that. It was the monkey, not the professor,
768 | whom Roy attacked, just as it was the monkey who teased Roy.
769 | Climbing was a joy to the creature, and it was a mere chance, I
770 | take it, that the pastime brought him to the young lady's win-
771 | dow. There is an early train to town, Watson, but I think we
772 | shall just have time for a cup of tea at the Chequers before we
773 | catch it."
774 |
--------------------------------------------------------------------------------
/redhead.txt:
--------------------------------------------------------------------------------
1 |
2 | The Red-headed League
3 |
4 | I had called upon my friend, Mr. Sherlock Holmes, one day in
5 | the autumn of last year and found him in deep conversation with
6 | a very stout, florid-faced, elderly gentleman with fiery red hair.
7 | With an apology for my intrusion, I was about to withdraw when
8 | Holmes pulled me abruptly into the room and closed the door
9 | behind me.
10 | "You could not possibly have come at a better time, my dear
11 | Watson," he said cordially.
12 | "I was afraid that you were engaged."
13 | "So I am. Very much so."
14 | "Then I can wait in the next room."
15 | "Not at all. This gentleman, Mr. Wilson, has been my partner
16 | and helper in many of my most successful cases, and I have no
17 | doubt that he will be of the utmost use to me in yours also."
18 | The stout gentleman half rose from his chair and gave a bob of
19 | greeting, with a quick little questioning glance from his small
20 | fat-encircled eyes.
21 | "Try the settee," said Holmes, relapsing into his armchair
22 | and putting his fingertips together, as was his custom when in
23 | judicial moods. "I know, my dear Watson, that you share my
24 | love of all that is bizarre and outside the conventions and
25 | humdrum routine of everyday life. You have shown your relish
26 | for it by the enthusiasm which has prompted you to chronicle,
27 | and, if you will excuse my saying so, somewhat to embellish so
28 | many of my own little adventures."
29 | "Your cases have indeed been of the greatest interest to me,"
30 | I observed.
31 | "You will remember that I remarked the other day, just before
32 | we went into the very simple problem presented by Miss Mary
33 | Sutherland, that for strange effects and extraordinary combina-
34 | tions we must go to life itself, which is always far more daring
35 | than any effort of the imagination."
36 | "A proposition which I took the liberty of doubting."
37 | "You did, Doctor, but none the less you must come round to
38 | my view, for otherwise I shall keep on piling fact upon fact on
39 | you until your reason breaks down under them and acknowledges
40 | me to be right. Now, Mr. Jabez Wilson here has been good
41 | enough to call upon me this morning, and to begin a narrative
42 | which promises to be one of the most singular which I have
43 | listened to for some time. You have heard me remark that the
44 | strangest and most unique things are very often connected not
45 | with the larger but with the smaller crimes, and occasionally,
46 | indeed, where there is room for doubt whether any positive
47 | crime has been committed. As far as I have heard it is impossible
48 | for me to say whether the present case is an instance of crime or
49 | not, but the course of events is certainly among the most singular
50 | that I have ever listened to. Perhaps, Mr. Wilson, you would
51 | have the great kindness to recommence your narrative. I ask you
52 | not merely because my friend Dr. Watson has not heard the
53 | opening part but also because the peculiar nature of the story
54 | makes me anxious to have every possible detail from your lips.
55 | As a rule, when I have heard some slight indication of the course
56 | of events, I am able to guide myself by the thousands of other
57 | similar cases which occur to my memory. In the present instance
58 | I am forced to admit that the facts are, to the best of my belief,
59 | unique."
60 | The portly client puffed out his chest with an appearance of
61 | some little pride and pulled a dirty and wrinkled newspaper from
62 | the inside pocket of his greatcoat. As he glanced down the
63 | advertisement column, with his head thrust forward and the
64 | paper flattened out upon his knee, I took a good look at the man
65 | and endeavoured, after the fashion of my companion, to read the
66 | indications which might be presented by his dress or appearance.
67 | I did not gain very much, however, by my inspection. Our
68 | visitor bore every mark of being an average commonplace Brit-
69 | ish tradesman, obese, pompous, and slow. He wore rather baggy
70 | gray shepherd's check trousers, a not over-clean black frock-
71 | coat, unbuttoned in the front, and a drab waistcoat with a heavy
72 | brassy Albert chain, and a square pierced bit of metal dangling
73 | down as an ornament. A frayed top-hat and a faded brown
74 | overcoat with a wrinkled velvet collar lay upon a chair beside
75 | him. Altogether, look as I would, there was nothing remarkable
76 | about the man save his blazing red head, and the expression of
77 | extreme chagrin and discontent upon his features.
78 | Sherlock Holmes's quick eye took in my occupation, and he
79 | shook his head with a smile as he noticed my questioning
80 | glances. "Beyond the obvious facts that he has at some time
81 | done manual labour, that he takes snuff, that he is a Freemason.
82 | that he has been in China, and that he has done a considerable
83 | amount of writing lately, I can deduce nothing else."
84 | Mr. Jabez Wilson started up in his chair, with his forefinger
85 | upon the paper, but his eyes upon my companion.
86 | "How, in the name of good-fortune, did you know all that,
87 | Mr. Holmes?" he asked. "How did you know, for example, that
88 | I did manual labour? It's as true as gospel, for I began as a ship's
89 | carpenter."
90 | "Your hands, my dear sir. Your right hand is quite a size
91 | larger than your left. You have worked with it, and the muscles
92 | are more developed."
93 | "Well, the snuff, then, and the Freemasonry?"
94 | "I won't insult your intelligence by telling you how I read
95 | that, especially as, rather against the strict rules of your order,
96 | you use an arc-and-compass breastpin."
97 | "Ah, of course, I forgot that. But the writing?"
98 | "What else can be indicated by that right cuff so very shiny
99 | for five inches, and the left one with the smooth patch near the
100 | elbow where you rest it upon the desk?"
101 | "Well, but China?"
102 | "The fish that you have tattooed immediately above your right
103 | wrist could only have been done in China. I have made a small
104 | study of tattoo marks and have even contributed to the literature
105 | of the subject. That trick of staining the fishes' scales of a
106 | delicate pink is quite peculiar to China. When, in addition, I see
107 | a Chinese coin hanging from your watch-chain, the matter be-
108 | comes even more simple."
109 | Mr. Jabez Wilson laughed heavily. "Well, I never!" said he.
110 | "I thought at first that you had done something clever, but I see
111 | that there was nothing in it, after all."
112 | "I begin to think, Watson," said Holmes, "that I make a
113 | mistake in explaining. 'Omne ignotum pro magnifico,' you know,
114 | and my poor little reputation, such as it is, will suffer shipwreck
115 | if I am so candid. Can you not find the advertisement, Mr.
116 | Wilson?"
117 | "Yes, I have got it now," he answered with his thick red
118 | finger planted halfway down the column. "Here it is. This is
119 | what began it all. You just read it for yourself, sir."
120 | I took the paper from him and read as follows.
121 |
122 | TO THE RED-HEADED LEAGUE:
123 | On account of the bequest of the late Ezekiah Hopkins, of
124 | Lebanon, Pennsylvania, U. S. A., there is now another
125 | vacancy open which entitles a member of the League to a
126 | salary of 4 pounds a week for purely nominal services. All red-
127 | headed men who are sound in body and mind and above
128 | the age of twenty-one years, are eligible. Appiy in person
129 | on Monday, at eleven o'clock, to Duncan Ross, at the
130 | offices of the League, 7 Pope's Coun, Fleet Street.
131 |
132 | "What on earth does this mean?" I ejaculated after I had
133 | twice read over the extraordinary announcement.
134 | Holmes chuckled and wriggled in his chair, as was his habit
135 | when in high spirits. "It is a little off the beaten track, isn't it?"
136 | said he. "And now, Mr. Wilson, off you go at scratch and tell
137 | us all about yourself, your household, and the effect which this
138 | advertisement had upon your fortunes. You will first make a
139 | note, Doctor, of the paper and the date."
140 | "It is The Morning Chronicle of April 27, 1890. Just two
141 | months ago."
142 | "Very good. Now, Mr. Wilson?"
143 | "Well, it is just as I have been telling you, Mr. Sherlock
144 | Holmes," said Jabez Wilson, mopping his forehead; "I have a
145 | small pawnbroker's business at Coburg Square, near the City.
146 | It's not a very large affair, and of late years it has not done more
147 | than just give me a living. I used to be able to keep two
148 | assistants, but now I only keep one; and I would have a job to
149 | pay him but that he is willing to come for half wages so as to
150 | learn the business."
151 | "What is the name of this obliging youth?" asked Sherlock
152 | Holmes.
153 | "His name is Vincent Spaulding, and he's not such a youth,
154 | either. It's hard to say his age. I should not wish a smarter
155 | assistant, Mr. Holmes; and I know very well that he could better
156 | himself and earn twice what I am able to give him. But, after all,
157 | if he is satisfied, why should I put ideas in his head?"
158 | "Why, indeed? You seem most fortunate in having an em-
159 | ployee who comes under the full market price. It is not a
160 | common experience among employers in this age. I don't know
161 | that your assistant is not as remarkable as your advertisement."
162 | "Oh, he has his faults, too," said Mr. Wilson. "Never was
163 | such a fellow for photography. Snapping away with a camera
164 | when he ought to be improving his mind, and then diving down
165 | into the cellar like a rabbit into its hole to develop his pictures.
166 | That is his main fault, but on the whole he's a good worker.
167 | There's no vice in him."
168 | "He is still with you, I presume?"
169 | "Yes, sir. He and a girl of fourteen, who does a bit of simple
170 | cooking and keeps the place clean -- that's all I have in the
171 | house, for I am a widower and never had any family. We live
172 | very quietly, sir, the three of us; and we keep a roof over our
173 | heads and pay our debts, if we do nothing more.
174 | "The first thing that put us out was that advertisement.
175 | Spaulding, he came down into the office just this day eight
176 | weeks, with this very paper in his hand, and he says:
177 | " 'I wish to the Lord, Mr. Wilson, that I was a red-headed
178 | man.'
179 | " 'Why that?' I asks.
180 | " 'Why,' says he, 'here's another vacancy on the League of
181 | the Red-headed Men. It's worth quite a little fortune to any man
182 | who gets it, and I understand that there are more vacancies than
183 | there are men, so that the trustees are at their wits' end what to
184 | do with the money. If my hair would only change colour, here's
185 | a nice little crib all ready for me to step into.'
186 | " 'Why, what is it, then?' I asked. You see. Mr. Holmes, I
187 | am a very stay-at-home man, and as my business came to me
188 | instead of my having to go to it, I was often weeks on end
189 | without putting my foot over the door-mat. In that way I didn't
190 | know much of what was going on outside, and I was always glad
191 | of a bit of news.
192 | " 'Have you never heard of the League of the Red-headed
193 | Men?' he asked with his eyes open.
194 | " 'Never.'
195 | " 'Why, [ wonder at that, for you are eligibile yourself for
196 | one of the vacancies.'
197 | " 'And what are they worth?' I asked.
198 | " 'Oh, merely a couple of hundred a year, but the work is
199 | slight, and it need not interfere very much with one's other
200 | occupations.'
201 | "Well, you can easily think that that made me prick up my
202 | ears, for the business has not been over-good for some years,
203 | and an extra couple of hundred would have been very handy.
204 | " 'Tell me all about it,' said I.
205 | " 'Well ' said he. showing me the advertisement. 'you can
206 | see for yourself that the League has a vacancy, and there is the
207 | address where you should apply for particulars. As far as I can
208 | make out, the League was founded by an American millionaire.
209 | Ezekiah Hopkins, who was very peculiar in his ways. He was
210 | himself red-headed, and he had a great sympathy for all red-
211 | headed men; so when he died it was found that he had left his
212 | enormous fortune in the hands of trustees, with instructions to
213 | apply the interest to the providing of easy berths to men whose
214 | hair is of that colour. From all I hear it is splendid pay and very
215 | little to do.'
216 | " 'But,' said I, 'there would be millions of red-headed men
217 | who would apply.'
218 | " 'Not so many as you might think,' he answered. 'You see it
219 | is really confined to Londoners, and to grown men. This Ameri-
220 | can had started from London when he was young, and he wanted
221 | to do the old town a good turn. Then, again, I have heard it is no
222 | use your applying if your hair is light red, or dark red, or
223 | anything but real bright, blazing, fiery red. Now, if you cared to
224 | apply, Mr. Wilson, you would just walk in; but perhaps it would
225 | hardly be worth your while to put yourself out of the way for the
226 | sake of a few hundred pounds.'
227 | "Now, it is a fact, gentlemen, as you may see for yourselves,
228 | that my hair is of a very full and rich tint, so that it seemed to me
229 | that if there was to be any competition in the matter I stood as
230 | good a chance as any man that I had ever met. Vincent Spaulding
231 | seemed to know so much about it that I thought he might prove
232 | useful, so I just ordered him to put up the shutters for the day and
233 | to come right away with me. He was very willing to have a
234 | holiday, so we shut the business up and started off for the
235 | address that was given us in the advertisement.
236 | "I never hope to see such a sight as that again, Mr. Holmes.
237 | From north, south, east, and west every man who had a shade of
238 | red in his hair had tramped into the city to answer the advertise-
239 | ment. Fleet Street was choked with red-headed folk, and Pope's
240 | Court looked like a coster's orange barrow. I should not have
241 | thought there were so many in the whole country as were brought
242 | together by that single advertisement. Every shade of colour they
243 | were -- straw, lemon, orange, brick, Irish-setter, liver, clay; but,
244 | as Spaulding said, there were not many who had the real vivid
245 | flame-coloured tint. When I saw how many were waiting, I
246 | would have given it up in despair; but Spaulding would not hear
247 | of it. How he did it I could not imagine, but he pushed and
248 | pulled and butted until he got me through the crowd, and right
249 | up to the steps which led to the office. There was a double
250 | stream upon the stair, some going up in hope, and some coming
251 | back dejected; but we wedged in as well as we could and soon
252 | found ourselves in the office."
253 | "Your experience has been a most entertaining one," re-
254 | marked Holmes as his client paused and refreshed his memory
255 | with a huge pinch of snuff. "Pray continue your very interesting
256 | statement."
257 | "There was nothing in the office but a couple of wooden
258 | chairs and a deal table, behind which sat a small man with a
259 | head that was even redder than mine. He said a few words to
260 | each candidate as he came up, and then he always managed to
261 | find some fault in them which would disqualify them. Getting a
262 | vacancy did not seem to be such a very easy matter, after all.
263 | However, when our turn came the little man was much more
264 | favourable to me than to any of the others, and he closed the
265 | door as we entered, so that he might have a private word with
266 | us.
267 | " 'This is Mr. Jabez Wilson,' said my assistant, 'and he is
268 | willing to fill a vacancy in the League.'
269 | " 'And he is admirably suited for it,' the other answered. 'He
270 | has every requirement. I cannot recall when I have seen anything
271 | so fine.' He took a step backward, cocked his head on one side,
272 | and gazed at my hair until I felt quite bashful. Then suddenly he
273 | plunged forward, wrung my hand, and congratulated me warmly
274 | on my success.
275 | " 'It would be injustice to hesitate,' said he. 'You will,
276 | however, I am sure, excuse me for taking an obvious precaution.'
277 | With that he seized my hair in both his hands, and tugged until I
278 | yelled with the pain. 'There is water in your eyes,' said he as he
279 | released me. 'I perceive that all is as it should be. But we have
280 | to be careful, for we have twice been deceived by wigs and once
281 | by paint. I could tell you tales of cobbler's wax which would
282 | disgust you with human nature.' He stepped over to the window
283 | and shouted through it at the top of his voice that the vacancy
284 | was filled. A groan of disappointment came up from below, and
285 | the folk all trooped away in different directions until there was
286 | not a red-head to be seen except my own and that of the
287 | manager.
288 | " 'My name,' said he, 'is Mr. Duncan Ross, and I am myself
289 | one of the pensioners upon the fund left by our noble benefactor.
290 | Are you a married man, Mr. Wilson? Have you a family?'
291 | "I answered that I had not.
292 | "His face fell immediately.
293 | " 'Dear me!' he said gravely, 'that is very serious indeed! I
294 | am sorry to hear you say that. The fund was, of course, for the
295 | propagation and spread of the red-heads as well as for their
296 | maintenance. It is exceedingly unfortunate that you should be a
297 | bachelor.'
298 | "My face lengthened at this, Mr. Holmes, for I thought that I
299 | was not to have the vacancy after all; but after thinking it over
300 | for a few minutes he said that it would be all right.
301 | " 'In the case of another,' said he, 'the objection might be
302 | fatal, but we must stretch a point in favour of a man with such a
303 | head of hair as yours. When shall you be able to enter upon your
304 | new duties?'
305 | " 'Well, it is a little awkward, for I have a business already,'
306 | said I.
307 | " 'Oh, never mind about that, Mr. Wilson!' said Vincent
308 | Spaulding. 'I should be able to look after that for you.'
309 | " 'What would be the hours?' I asked.
310 | " 'Ten to two.'
311 | "Now a pawnbroker's business is mostly done of an evening,
312 | Mr. Holmes, especially Thursday and Friday evening, which is
313 | just before pay-day; so it would suit me very well to earn a little
314 | in the mornings. Besides, I knew that my assistant was a good
315 | man, and that he would see to anything that turned up.
316 | " 'That would suit me very well,' said I. 'And the pay?'
317 | " 'Is 4 pounds a week.'
318 | " 'And the work?'
319 | " 'Is purely nominal.'
320 | " 'What do you call purely nominal?'
321 | " 'Well, you have to be in the office, or at least in the
322 | building, the whole time. If you leave, you forfeit your whole
323 | position forever. The will is very clear upon that point. You
324 | don't comply with the conditions if you budge from the office
325 | during that time.'
326 | " 'It's only four hours a day, and I should not think of
327 | leaving,' said I.
328 | " 'No excuse will avail,' said Mr. Duncan Ross; 'neither
329 | sickness nor business nor anything else. There you must stay, or
330 | you lose your billet.'
331 | " 'And the work?'
332 | " 'Is to copy out the Encyclopedia Britannica. There is the
333 | first volume of it in that press. You must find your own ink.
334 | pens, and blotting-paper, but we provide this table and chair.
335 | Will you be ready to-morrow?'
336 | " 'Certainly,' I answered.
337 | " 'Then, good-bye, Mr. Jabez Wilson, and let me congratu-
338 | late you once more on the important position which you have
339 | been fortunate enough to gain.' He bowed me out of the room
340 | and I went home with my assistant, hardly knowing what to say
341 | or do, I was so pleased at my own good fortune.
342 | "Well, I thought over the matter all day, and by evening I was
343 | in low spirits again; for I had quite persuaded myself that the
344 | whole affair must be some great hoax or fraud, though what its
345 | object might be I could not imagine. It seemed altogether past
346 | belief that anyone could make such a will, or that they would
347 | pay such a sum for doing anything so simple as copying out the
348 | Encyclopedia Britannica. Vincent Spaulding did what he could
349 | to cheer me up, but by bedtime I had reasoned myself out of the
350 | whole thing. However, in the morning I determined to have a
351 | look at it anyhow, so I bought a penny bottle of ink, and with a
352 | quill-pen, and seven sheets of foolscap paper, I started off for
353 | Pope's Court.
354 | "Well, to my surprise and delight, everything was as right as
355 | possible. The table was set out ready for me, and Mr. Duncan
356 | Ross was there to see that I got fairly to work. He started me off
357 | upon the letter A, and then he left me; but he would drop in from
358 | time to time to see that all was right with me. At two o'clock he
359 | bade me good-day, complimented me upon the amount that I
360 | had written, and locked the door of the office after me.
361 | "This went on day after day, Mr. Holmes, and on Saturday
362 | the manager came in and planked down four golden sovereigns
363 | for my week's work. It was the same next week, and the same
364 | the week after. Every morning I was there at ten, and every
365 | afternoon I left at two. By degrees Mr. Duncan Ross took to
366 | coming in only once of a morning, and then, after a time, he did
367 | not come in at all. Still, of course, I never dared to leave the
368 | room for an instant, for I was not sure when he might come, and
369 | the billet was such a good one, and suited me so well, that I
370 | would not risk the loss of it.
371 | "Eight weeks passed away like this, and I had written about
372 | Abbots and Archery and Armour and Architecture and Attica,
373 | and hoped with diligence that I might get on to the B's before
374 | very long. It cost me something in foolscap, and I had pretty
375 | nearly filled a shelf with my writings. And then suddenly the
376 | whole business came to an end."
377 | "To an end?"
378 | "Yes, sir. And no later than this morning. I went to my work
379 | as usual at ten o'clock, but the door was shut and locked, with a
380 | little square of card-board hammered on to the middle of the
381 | panel with a tack. Here it is, and you can read for yourself."
382 | He held up a piece of white card-board about the size of a
383 | sheet of note-paper. It read in this fashion:
384 |
385 | THE RED-HEADED LEAGUE
386 | IS
387 | DISSOLVED.
388 | October 9, 1890.
389 |
390 | Sherlock Holmes and I surveyed this curt announcement and
391 | the rueful face behind it, until the comical side of the affair so
392 | completely overtopped every other consideration that we both
393 | burst out into a roar of laughter.
394 | "I cannot see that there is anything very funny," cried our
395 | client, flushing up to the roots of his flaming head. "If you can
396 | do nothing better than laugh at me, I can go elsewhere."
397 | "No, no," cried Holmes, shoving him back into the chair
398 | from which he had half risen. "I really wouldn't miss your case
399 | for the world. It is most refreshingly unusual. But there is, if you
400 | will excuse my saying so, something just a little funny about it.
401 | Pray what steps did you take when you found the card upon the
402 | door?"
403 | "I was staggered, sir. I did not know what to do. Then I
404 | called at the offices round, but none of them seemed to know
405 | anything about it. Finally, I went to the landlord, who is an
406 | accountant living on the ground-floor, and I asked him if he
407 | could tell me what had become of the Red-headed League. He
408 | said that he had never heard of any such body. Then I asked him
409 | who Mr. Duncan Ross was. He answered that the name was new
410 | to him.
411 | " 'Well,' said I, 'the gentleman at No. 4.'
412 | " 'What, the red-headed man?'
413 | " 'Yes.'
414 | " 'Oh,' said he, 'his name was William Morris. He was a
415 | solicitor and was using my room as a temporary convenience
416 | until his new premises were ready. He moved out yesterday.'
417 | " 'Where could I find him?'
418 | " 'Oh, at his new offices. He did tell me the address. Yes, 17
419 | King Edward Street, near St. Paul's.'
420 | "I started off, Mr. Holmes, but when I got to that address it
421 | was a manufactory of artificial knee-caps, and no one in it had
422 | ever heard of either Mr. William Morris or Mr. Duncan Ross."
423 | "And what did you do then?" asked Holmes.
424 | "I went home to Saxe-Coburg Square, and I took the advice
425 | of my assistant. But he could not help me in any way. He could
426 | only say that if I waited I should hear by post. But that was not
427 | quite good enough, Mr. Holmes. I did not wish to lose such a
428 | place without a struggle, so, as I had heard that you were good
429 | enough to give advice to poor folk who were in need of it, I
430 | came right away to you."
431 | "And you did very wisely," said Holmes. "Your case is an
432 | exceedingly remarkable one, and I shall be happy to look into it.
433 | From what you have told me I think that it is possible that graver
434 | issues hang from it than might at first sight appear."
435 | "Grave enough!" said Mr. Jabez Wilson. "Why, I have lost
436 | four pound a week."
437 | "As far as you are personally concerned," remarked Holmes,
438 | "I do not see that you have any grievance against this extraordi-
439 | nary league. On the contrary, you are, as I understand, richer by
440 | some 30 pounds, to say nothing of the minute knowledge which you
441 | have gained on every subject which comes under the letter A.
442 | You have lost nothing by them."
443 | "No, sir. But I want to find out about them, and who they
444 | are, and what their object was in playing this prank -- if it was a
445 | prank -- upon me. It was a pretty expensive joke for them, for it
446 | cost them two and thirty pounds."
447 | "We shall endeavour to clear up these points for you. And,
448 | first, one or two questions, Mr. Wilson. This assistant of yours
449 | who first called your attention to the advertisement -- how long
450 | had he been with you?"
451 | "About a month then."
452 | "How did he come?"
453 | "In answer to an advertisement."
454 | "Was he the only applicant?"
455 | "No, I had a dozen."
456 | "Why did you pick him?"
457 | "Because he was handy and would come cheap."
458 | "At half-wages, in fact."
459 | "Yes."
460 | "What is he like, this Vincent Spaulding?"
461 | "Small, stout-built, very quick in his ways, no hair on his
462 | face, though he's not short of thirty. Has a white splash of acid
463 | upon his forehead."
464 | Holmes sat up in his chair in considerable excitement. "I
465 | thought as much," said he. "Have you ever observed that his
466 | ears are pierced for earrings?"
467 | "Yes, sir. He told me that a gypsy had done it for him when
468 | he was a lad."
469 | "Hum!" said Holmes, sinking back in deep thought. "He is
470 | still with you?"
471 | "Oh, yes, sir; I have only just left him."
472 | "And has your business been attended to in your absence?"
473 | "Nothing to complain of, sir. There's never very much to do
474 | of a morning."
475 | "That will do, Mr. Wilson. I shall be happy to give you an
476 | opinion upon the subject in the course of a day or two. To-day is
477 | Saturday, and I hope that by Monday we may come to a
478 | conclusion."
479 | "Well, Watson," said Holmes when our visitor had left us,
480 | "what do you make of it all?"
481 | "I make nothing of it," I answered frankly. "It is a most
482 | mysterious business."
483 | "As a rule," said Holmes, "the more bizarre a thing is the
484 | less mysterious it proves to be. It is your commonplace, feature-
485 | less crimes which are really puzzling, just as a commonplace
486 | face is the most difficult to identify. But I must be prompt over
487 | this matter."
488 | "What are you going to do, then?" I asked.
489 | "To smoke," he answered. "It is quite a three pipe problem,
490 | and I beg that you won't speak to me for fifty minutes." He
491 | curled himself up in his chair, with his thin knees drawn up to
492 | his hawk-like nose, and there he sat with his eyes closed and his
493 | black clay pipe thrusting out like the bill of some strange bird. I
494 | had come to the conclusion that he had dropped asleep, and
495 | indeed was nodding myself, when he suddenly sprang out of his
496 | chair with the gesture of a man who has made up his mind and
497 | put his pipe down upon the mantelpiece.
498 | "Sarasate plays at the St. James's Hall this afternoon," he
499 | remarked. "What do you think, Watson? Could your patients
500 | spare you for a few hours?"
501 | "I have nothing to do to-day. My practice is never very
502 | absorbing."
503 | "Then put on your hat and come. I am going through the City
504 | first, and we can have some lunch on the way. I observe that
505 | there is a good deal of German music on the programme, which
506 | is rather more to my taste than Italian or French. It is introspec-
507 | tive, and I want to introspect. Come along!"
508 | We travelled by the Underground as far as Aldersgate; and a
509 | short walk took us to Saxe-Coburg Square, the scene of the
510 | singular story which we had listened to in the morning. It was a
511 | poky, little, shabby-genteel place, where four lines of dingy
512 | two-storied brick houses looked out into a small railed-in enclo-
513 | sure, where a lawn of weedy grass and a few clumps of faded
514 | laurel-bushes made a hard fight against a smoke-laden and
515 | uncongenial atmosphere. Three gilt balls and a brown board with
516 | "JABEZ WILSON" in white letters, upon a corner house, announced
517 | the place where our red-headed client carried on his business.
518 | Sherlock Holmes stopped in front of it with his head on one side
519 | and looked it all over, with his eyes shining brightly between
520 | puckered lids. Then he walked slowly up the street, and then
521 | down again to the corner, still looking keenly at the houses.
522 | Finally he returned to the pawnbroker's, and, having thumped
523 | vigorously upon the pavement with his stick two or three times,
524 | he went up to the door and knocked. It was instantly opened by a
525 | bright-looking, clean-shaven young fellow, who asked him to
526 | step in.
527 | "Thank you," said Holmes, "I only wished to ask you how
528 | you would go from here to the Strand."
529 | "Third right, fourth left," answered the assistant promptly,
530 | closing the door.
531 | "Smart fellow, that," observed Holmes as we walked away.
532 | "He is, in my judgment. the fourth smartest man in London, and
533 | for daring I am not sure that he has not a claim to be third. I
534 | have known something of him before."
535 | "Evidently," said I, "Mr. Wilson's assistant counts for a
536 | good deal in this mystery of the Red-headed League. I am sure
537 | that you inquired your way merely in order that you might see
538 | him."
539 | "Not him."
540 | "What then?"
541 | "The knees of his trousers."
542 | "And what did you see?"
543 | "What I expected to see."
544 | "Why did you beat the pavement?"
545 | "My dear doctor, this is a time for observation, not for talk.
546 | We are spies in an enemy's country. We know something of
547 | Saxe-Coburg Square. Let us now explore the parts which lie
548 | behind it."
549 | The road in which we found ourselves as we turned round the
550 | corner from the retired Saxe-Coburg Square presented as great a
551 | contrast to it as the front of a picture does to the back. It was one
552 | of the main arteries which conveyed the traffic of the City to the
553 | north and west. The roadway was blocked with the immense
554 | stream of commerce flowing in a double tide inward and out-
555 | ward, while the footpaths were black with the hurrying swarm of
556 | pedestrians. It was difficult to realize as we looked at the line of
557 | fine shops and stately business premises that they really abutted
558 | on the other side upon the faded and stagnant square which we
559 | had just quitted.
560 | "Let me see," said Holmes, standing at the corner and glanc-
561 | ing along the line, "I should like just to remember the order of
562 | the houses here. It is a hobby of mine to have an exact knowl-
563 | edge of London. There is Mortimer's, the tobacconist, the little
564 | newspaper shop, the Coburg branch of the City and Suburban
565 | Bank, the Vegetarian Restaurant, and McFarlane's carriage-building
566 | depot. That carries us right on to the other block. And now,
567 | Doctor, we've done our work, so it's time we had some play. A
568 | sandwich and a cup of coffee, and then off to violin-land, where
569 | all is sweetness and delicacy and harmony, and there are no
570 | red-headed clients to vex us with their conundrums."
571 | My friend was an enthusiastic musician, being himself not
572 | only a very capable perfomer but a composer of no ordinary
573 | merit. All the afternoon he sat in the stalls wrapped in the most
574 | perfect happiness, gently waving his long, thin fingers in time to
575 | the music, while his gently smiling face and his languid, dreamy
576 | eyes were as unlike those of Holmes, the sleuth-hound, Holmes
577 | the relentless, keen-witted, ready-handed criminal agent, as it was
578 | possible to conceive. In his singular character the dual nature
579 | alternately asserted itself, and his extreme exactness and astute-
580 | ness represented, as I have often thought, the reaction against the
581 | poetic and contemplative mood which occasionally predominated
582 | in him. The swing of his nature took him from extreme languor
583 | to devouring energy; and, as I knew well, he was never so truly
584 | formidable as when, for days on end, he had been lounging in
585 | his armchair amid his improvisations and his black-letter edi-
586 | tions. Then it was that the lust of the chase would suddenly come
587 | upon him, and that his brilliant reasoning power would rise to
588 | the level of intuition, until those who were unacquainted with his
589 | methods would look askance at him as on a man whose knowl-
590 | edge was not that of other mortals. When I saw him that after-
591 | noon so enwrapped in the music at St. James's Hall I felt that an
592 | evil time might be coming upon those whom he had set himself
593 | to hunt down.
594 | "You want to go home, no doubt, Doctor," he remarked as
595 | we emerged.
596 | "Yes, it would be as well."
597 | "And I have some business to do which will take some hours.
598 | This business at Coburg Square is serious."
599 | "Why serious?"
600 | "A considerable crime is in contemplation. I have every
601 | reason to believe that we shall be in time to stop it. But to-day
602 | being Saturday rather complicates matters. I shall want your help
603 | to-night."
604 | "At what time?"
605 | "Ten will be early enough."
606 | "I shall be at Baker Street at ten."
607 | "Very well. And, I say, Doctor, there may be some little
608 | danger, so kindly put your army revolver in your pocket." He
609 | waved his hand, turned on his heel, and disappeared in an instant
610 | among the crowd.
611 | I trust that I am not more dense than my neighbours, but I was
612 | always oppressed with a sense of my own stupidity in my
613 | dealings with Sherlock Holmes. Here I had heard what he had
614 | heard, I had seen what he had seen, and yet from his words it
615 | was evident that he saw clearly not only what had happened but
616 | what was about to happen, while to me the whole business was
617 | still confused and grotesque. As I drove home to my house in
618 | Kensington I thought over it all, from the extraordinary story of
619 | the red-headed copier of the Encyclopedia down to the visit to
620 | Saxe-Coburg Square, and the ominous words with which he had
621 | parted from me. What was this nocturnal expedition, and why
622 | should I go armed? Where were we going, and what were we to
623 | do? I had the hint from Holmes that this smooth-faced pawn-
624 | broker's assistant was a formidable man -- a man who might play
625 | a deep game. I tried to puzzle it out, but gave it up in despair
626 | and set the matter aside until night should bring an explanation.
627 | It was a quarter-past nine when I started from home and made
628 | my way across the Park, and so through Oxford Street to Baker
629 | Street. Two hansoms were standing at the door, and as I entered
630 | the passage I heard the sound of voices from above. On entering
631 | his room I found Holmes in animated conversation with two
632 | men, one of whom I recognized as Peter Jones, the official
633 | police agent, while the other was a long, thin, sad-faced man,
634 | with a very shiny hat and oppressively respectable frock-coat.
635 | "Ha! Our party is complete," said Holmes, buttoning up his
636 | peajacket and taking his heavy hunting crop from the rack.
637 | "Watson, I think you know Mr. Jones, of Scotland Yard? Let me
638 | introduce you to Mr. Merryweather, who is to be our companion
639 | in to-night's adventure."
640 | "We're hunting in couples again, Doctor, you see," said
641 | Jones in his consequential way. "Our friend here is a wonderful
642 | man for starting a chase. All he wants is an old dog to help him
643 | to do the running down."
644 | "I hope a wild goose may not prove to be the end of our
645 | chase," observed Mr. Merryweather gloomily.
646 | "You may place considerable confidence in Mr. Holmes,
647 | sir," said the police agent loftily. "He has his own little meth-
648 | ods, which are, if he won't mind my saying so, just a little too
649 | theoretical and fantastic, but he has the makings of a detective in
650 | him. It is not too much to say that once or twice, as in that
651 | business of the Sholto murder and the Agra treasure, he has been
652 | more nearly correct than the official force."
653 | "Oh, if you say so, Mr. Jones, it is all right," said the
654 | stranger with deference. "Still, I confess that I miss my rubber.
655 | It is the first Saturday night for seven-and-twenty years that I
656 | have not had my rubber."
657 | "I think you will find," said Sherlock Holmes, "that you will
658 | play for a higher stake to-night than you have ever done yet, and
659 | that the play will be more exciting. For you, Mr. Merryweather,
660 | the stake will be some 30,000 pounds; and for you, Jones, it will be the
661 | man upon whom you wish to lay your hands."
662 | "John Clay, the murderer, thief, smasher, and forger. He's a
663 | young man, Mr. Merryweather, but he is at the head of his
664 | profession, and I would rather have my bracelets on him than on
665 | any criminal in London. He's a remarkable man, is young John
666 | Clay. His grandfather was a royal duke, and he himself has been
667 | to Eton and Oxford. His brain is as cunning.as his fingers, and
668 | though we meet signs of him at every turn, we never know
669 | where to find the man himself. He'll crack a crib in Scotland one
670 | week, and be raising money to build an orphanage in Cornwall
671 | the next. I've been on his track for years and have never set eyes
672 | on him yet."
673 | "I hope that I may have the pleasure of introducing you
674 | to-night. I've had one or two little turns also with Mr. John
675 | Clay, and I agree with you that he is at the head of his profes-
676 | sion. It is past ten, however, and quite time that we started. If
677 | you two will take the first hansom, Watson and I will follow
678 | in the second."
679 | Sherlock Holmes was not very communicative during the long
680 | drive and lay back in the cab humming the tunes which he had
681 | heard in the afternoon. We rattled through an endless labyrinth
682 | of gas-lit streets until we emerged into Farrington Street.
683 | "We are close there now," my friend remarked. "This fellow
684 | Merryweather is a bank director, and personally interested in the
685 | matter. I thought it as well to have Jones with us also. He is not
686 | a bad fellow, though an absolute imbecile in his profession. He
687 | has one positive virtue. He is as brave as a bulldog and as
688 | tenacious as a lobster if he gets his claws upon anyone. Here we
689 | are, and they are waiting for us."
690 | We had reached the same crowded thoroughfare in which we
691 | had found ourselves in the morning. Our cabs were dismissed,
692 | and, following the guidance of Mr. Merryweather, we passed
693 | down a narrow passage and through a side door, which he
694 | opened for us. Within there was a small corridor, which ended in
695 | a very massive iron gate. This also was opened, and led down a
696 | flight of winding stone steps, which terminated at another formi-
697 | dable gate. Mr. Merryweather stopped to light a lantern, and
698 | then conducted us down a dark, earth-smelling passage, and so,
699 | after opening a third door, into a huge vault or cellar, which was
700 | piled all round with crates and massive boxes.
701 | "You are not very vulnerable from above," Holmes remarked
702 | as he held up the lantern and gazed about him.
703 | "Nor from below," said Mr. Merryweather, striking his stick
704 | upon the flags which lined the floor. "Why, dear me, it sounds
705 | quite hollow!" he remarked, looking up in surprise.
706 | "I must really ask you to be a little more quiet!" said Holmes
707 | severely. "You have already imperilled the whole success of our
708 | expedition. Might I beg that you would have the goodness to sit
709 | down upon one of those boxes, and not to interfere?"
710 | The solemn Mr. Merryweather perched himself upon a crate,
711 | with a very injured expression upon his face, while Holmes fell
712 | upon his knees upon the floor and, with the lantern and a
713 | magnifying lens, began to exarnine minutely the cracks between
714 | the stones. A few seconds sufficed to satisfy him, for he sprang
715 | to his feet again and put his glass in his pocket.
716 | "We have at least an hour before us," he remarked, "for they
717 | can hardly take any steps until the good pawnbroker is safely in
718 | bed. Then they will not lose a minute, for the sooner they do
719 | their work the longer time they will have for their escape. We
720 | are at present, Doctor -- as no doubt you have divined -- in the
721 | cellar of the City branch of one of the principal London banks.
722 | Mr. Merryweather is the chairman of directors, and he will
723 | explain to you that there are reasons why the more daring
724 | criminals of London should take a considerable interest in this
725 | cellar at present."
726 | "It is our French gold," whispered the director. "We have
727 | had several warnings that an attempt might be made upon it."
728 | "Your French gold?"
729 | "Yes. We had occasion some months ago to strengthen our
730 | resources and borrowed for that purpose 30,000 napoleons from
731 | the Bank of France. It has become known that we have never
732 | had occasion to unpack the money, and that it is still lying in our
733 | cellar. The crate upon which I sit contains 2,000 napoleons
734 | packed between layers of lead foil. Our reserve of bullion is
735 | much larger at present than is usually kept in a single branch
736 | office, and the directors have had misgivings upon the subject."
737 | "Which were very well justified," observed Holmes. "And
738 | now it is time that we arranged our little plans. I expect that
739 | within an hour matters will come to a head. In the meantime
740 | Mr. Merryweather, we must put the screen over that dark lantern."
741 | "And sit in the dark?"
742 | "I am afraid so. I had brought a pack of cards in my pocket,
743 | and I thought that, as we were a partie carree, you might have
744 | your rubber after all. But I see that the enemy's preparations
745 | have gone so far that we cannot risk the presence of a light. And,
746 | first of all, we must choose our positions. These are daring men,
747 | and though we shall take them at a disadvantage, they may do us
748 | some harm unless we are careful. I shall stand behind this crate,
749 | and do you conceal yourselves behind those. Then, when I flash
750 | a light upon them, close in swiftly. If they fire, Watson, have no
751 | compunction about shooting them down."
752 | I placed my revolver, cocked, upon the top of the wooden
753 | case behind which I crouched. Holmes shot the slide across the
754 | front of his lantern and left us in pitch darkness -- such an
755 | absolute darkness as I have never before experienced. The smell
756 | of hot metal remained to assure us that the light was still there,
757 | ready to flash out at a moment's notice. To me, with my nerves
758 | worked up to a pitch of expectancy, there was something de-
759 | pressing and subduing in the sudden gloom, and in the cold dank
760 | air of the vault.
761 | "They have but one retreat," whispered Holmes. "That is
762 | back through the house into Saxe-Coburg Square. I hope that
763 | you have done what I asked you, Jones?"
764 | "l have an inspector and two officers waiting at the front
765 | door."
766 | "Then we have stopped all the holes. And now we must be
767 | silent and wait."
768 | What a time it seemed! From comparing notes afterwards it
769 | was but an hour and a quarter, yet it appeared to me that the
770 | night must have almost gone. and the dawn be breaking above
771 | us. My limbs were weary and stiff, for I feared to change my
772 | position; yet my nerves were worked up to the highest pitch of
773 | tension, and my hearing was so acute that I could not only hear
774 | the gentle breathing of my companions, but I could distinguish
775 | the deeper, heavier in-breath of the bulky Jones from the thin,
776 | sighing note of the bank director. From my position I could look
777 | over the case in the direction of the floor. Suddenly my eyes
778 | caught the glint of a light.
779 | At first it was but a lurid spark upon the stone pavement. Then
780 | it lengthened out until it became a yellow line, and then, without
781 | any warning or sound, a gash seemed to open and a hand
782 | appeared; a white, almost womanly hand, which felt about in the
783 | centre of the little area of light. For a minute or more the hand,
784 | with its writhing fingers, protruded out of the floor. Then it was
785 | withdrawn as suddenly as it appeared, and all was dark again
786 | save the single lurid spark which marked a chink between the
787 | stones.
788 | Its disappearance, however, was but momentary. With a rend-
789 | ing, tearing sound, one of the broad. white stones turned over
790 | upon its side and left a square, gaping hole, through which
791 | streamed the light of a lantern. Over the edge there peeped a
792 | clean-cut, boyish face, which looked keenly about it, and then.
793 | with a hand on either side of the aperture, drew itself shoulder-
794 | high and waist-high, until one knee rested upon the edge. In
795 | another instant he stood at the side of the hole and was hauling
796 | after him a companion, lithe and small like himself, with a pale
797 | face and a shock of very red hair.
798 | "It's all clear," he whispered. "Have you the chisel and the
799 | bags? Great Scott! Jump, Archie, jump, and I'll swing for it!"
800 | Sherlock Holmes had sprung out and seized the intruder by the
801 | collar. The other dived down the hole, and I heard the sound of
802 | rending cloth as Jones clutched at his skirts. The light flashed
803 | upon the barrel of a revolver, but Holmes's hunting crop came
804 | down on the man's wrist, and the pistol clinked upon the stone
805 | floor.
806 | "It's no use, John Clay," said Holmes blandly. "You have
807 | no chance at all."
808 | "So I see," the other answered with the utmost coolness. "I
809 | fancy that my pal is all right, though I see you have got his
810 | coat-tails."
811 | "There are three men waiting for him at the door," said
812 | Holmes.
813 | "Oh, indeed! You seem to have done the thing very com-
814 | pletely. I must compliment you."
815 | "And I you," Holmes answered. "Your red-headed idea was
816 | very new and effective."
817 | "You'll see your pal again presently," said Jones. "He's
818 | quicker at climbing down holes than I am. Just hold out while I
819 | fix the derbies."
820 | "I beg that you will not touch me with your filthy hands,"
821 | remarked our prisoner as the handcuffs clattered upon his wrists.
822 | "You may not be aware that I have royal blood in my veins.
823 | Have the goodness, also, when you address me always to say
824 | 'sir' and 'please.' "
825 | "All right," said Jones with a stare and a snigger. "Well,
826 | would you please, sir, march upstairs, where we can get a cab to
827 | carry your Highness to the police-station?"
828 | "That is better," said John Clay serenely. He made a sweep-
829 | ing bow to the three of us and walked quietly off in the custody
830 | of the detective.
831 | "Really, Mr. Holmes," said Mr. Merryweather as we fol-
832 | lowed them from the cellar, "I do not know how the bank can
833 | thank you or repay you. There is no doubt that you have detected
834 | and defeated in the most complete manner one of the most
835 | determined attempts at bank robbery that have ever come within
836 | my experience."
837 | "I have had one or two little scores of my own to settle with
838 | Mr. John Clay," said Holmes. "I have been at some small
839 | expense over this matter, which I shall expect the bank to
840 | refund, but beyond that I am amply repaid by having had an
841 | experience which is in many ways unique, and by hearing the
842 | very remarkable narrative of the Red-headed League."
843 |
844 | "You see, Watson," he explained in the early hours of the
845 | morning as we sat over a glass of whisky and soda in Baker
846 | Street, "it was perfectly obvious from the first that the only
847 | possible object of this rather fantastic business of the advertise-
848 | ment of the League, and the copying of the Encyclopedia, must
849 | be to get this not over-bright pawnbroker out of the way for a
850 | number of hours every day. It was a curious way of managing it,
851 | but, really, it would be difficult to suggest a better. The method
852 | was no doubt suggested to Clay's ingenious mind by the colour
853 | of his accomplice's hair. The 4 pounds a week was a lure which must
854 | draw him, and what was it to them, who were playing for
855 | thousands? They put in the advertisement, one rogue has the
856 | temporary office, the other rogue incites the man to apply for it.
857 | and together they manage to secure his absence every morning in
858 | the week. From the time that I heard of the assistant having
859 | come for half wages, it was obvious to me that he had some
860 | strong motive for securing the situation."
861 | "But how could you guess what the motive was?"
862 | "Had there been women in the house, I should have suspected
863 | a mere vulgar intrigue. That, however, was out of the question.
864 | The man's business was a small one, and there was nothing in
865 | his house which could account for such elaborate preparations,
866 | and such an expenditure as they were at. It must, then, be
867 | something out of the house. What could it be? I thought of the
868 | assistant's fondness for photography, and his trick of vanishing
869 | into the cellar. The cellar! There was the end of this tangled
870 | clue. Then I made inquiries as to this mysterious assistant and
871 | found that I had to deal with one of the coolest and most daring
872 | criminals in London. He was doing something in the cellar --
873 | something which took many hours a day for months on end.
874 | What could it be, once more? I could think of nothing save that
875 | he was running a tunnel to some other building.
876 | "So far I had got when we went to visit the scene of action. I
877 | surprised you by beating upon the pavement with my stick. I was
878 | ascertaining whether the cellar stretched out in front or behind. It
879 | was not in front. Then I rang the bell, and, as I hoped, the
880 | assistant answered it. We have had some skirmishes, but we had
881 | never set eyes upon each other before. I hardly looked at his
882 | face. His knees were what I wished to see. You must yourself
883 | have remarked how worn, wrinkled, and stained they were.
884 | They spoke of those hours of burrowing. The only remaining
885 | point was what they were burrowing for. I walked round the
886 | corner, saw the City and Suburban Bank abutted on our friend's
887 | premises, and felt that I had solved my problem. When you
888 | drove home after the concert I called upon Scotland Yard and
889 | upon the chairman of the bank directors, with the result that you
890 | have seen."
891 | "And how could you tell that they would make their attempt
892 | to-night?" I asked.
893 | "Well, when they closed their League offices that was a sign
894 | that they cared no longer about Mr. Jabez Wilson's presence -- in
895 | other words, that they had completed their tunnel. But it was
896 | essential that they should use it soon, as it might be discovered,
897 | or the bullion might be removed. Saturday would suit them
898 | better than any other day, as it would give them two days for
899 | their escape. For all these reasons I expected them to come
900 | to-night."
901 | "You reasoned it out beautifully," I exclaimed in unfeigned
902 | admiration "It is so long a chain, and yet every link rings true."
903 | "It saved me from ennui," he answered, yawning. "Alas! I
904 | already feel it closing in upon me. My life is spent in one long
905 | effort to escape from the commonplaces of existence. These little
906 | problems help me to do so."
907 | "And you are a benefactor of the race," said I.
908 | He shrugged his shoulders. "Well, perhaps, after all, it is of
909 | some little use," he remarked. " 'L'homme c'est rien -- l' oeuvre
910 | c'est tout,' as Gustave Flaubert wrote to George Sand."
911 |
--------------------------------------------------------------------------------