├── 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 |
alt 0
26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 |
alt 0alt 0alt 0
alt 0alt 0alt 0
alt 0alt 0alt 0
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 | --------------------------------------------------------------------------------