-
Notifications
You must be signed in to change notification settings - Fork 79
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Split semian operations into more cohesive translation units #101
Conversation
@@ -6,3 +6,5 @@ | |||
/html/ | |||
Gemfile.lock | |||
vendor/ | |||
*.swp |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry, i'm a vim junky.
@@ -3,6 +3,7 @@ language: ruby | |||
sudo: true | |||
|
|||
before_install: | |||
- gem update --system |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This fixes CI which was failing on installing native extensions for rainbows
ext/semian/semian_resource_alloc.c
Outdated
@@ -0,0 +1,12 @@ | |||
#include <semian_resource_alloc.h> | |||
|
|||
const rb_data_type_t |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could not be inlined because it's not a function, and defining a variable in a header file would have resulted in multiples definitions of the symbol. That is the only reason this is in its own C file.
ext/semian/semian_tickets.c
Outdated
} | ||
|
||
void | ||
configure_tickets(int sem_id, int tickets, int should_initialize) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function has been split apart into multiple functions to simplify the implementation of additional ticket strategies (quota).
semian.gemspec
Outdated
@@ -12,7 +12,7 @@ Gem::Specification.new do |s| | |||
across process boundaries with SysV semaphores. | |||
DOC | |||
s.homepage = 'https://github.com/shopify/semian' | |||
s.authors = ['Scott Francis', 'Simon Eskildsen'] | |||
s.authors = ['Scott Francis', 'Simon Eskildsen', 'Dale Hamel'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bit gratuitous maybe ;)
5665747
to
3dd0a91
Compare
ext/semian/semset.c
Outdated
@@ -0,0 +1,69 @@ | |||
#include <semset.h> | |||
|
|||
const char *SEMINDEX_STRING[] = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This must be defined within a C file to prevent multiple definitions. This is why this is declared as an extern in the corresponding header file.
3dd0a91
to
0bc6b8b
Compare
ext/semian/semset.c
Outdated
// It is necessary for the cardinatily of the semaphore set to be part of the key | ||
// or else sem_get will complain that we have requested an incorrect number of sems | ||
// for the desired key, and have changed the number of semaphores for a given key | ||
sprintf(semset_size_key, "_NUM_SEMS_%d", SI_NUM_SEMAPHORES); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit of a hack, adding the cardinality of the semaphore set to the key before hashing it.
This allows for an upgrade path, increasing the number of semaphores.
Note that we never actually clean up the semaphore set of the old cardinality, but we could manually if we chose to. Rebooting would also release them.
Note that I have another WIP branch where I have started implementing the quota logic, but to make that upcoming PR easier to review I decided to pull this refactor out to be reviewed separately |
For what it's worth, this change is one that I'll probably be dragging our feet a little bit on releasing to our production environment due to a couple of things are creating some caution:
Don't get me wrong, I ❤️ the intention and thought behind this and applaud the effort - I might just sit back and watch someone else test this one in production first :) |
I had considered breaking his up even further,
Perhaps building up just a couple of the files at a time.
I'll resubmit this as spears PRa to facilitate the review process, but
leave this larger omnibus one open to track
…On Sun, Feb 12, 2017 at 18:19 Jacob Bednarz ***@***.***> wrote:
For what it's worth, this change is one that I'll probably be dragging our
feet a little bit on releasing to our production environment due to a
couple of things are creating some caution:
- This is a big PR to review and understand the moving parts. I'm a
fan of small incremental changes because if something breaks I'll have a
fair idea on where the wheels started to fall off.
- The changes have all been done in a single commit making it
difficult for me to follow along with your thought process and potentially
debugging solutions that I could hit.
Don't get me wrong, I ❤️ the intention and thought behind this and
applaud the effort - I might just sit back and watch someone else test this
one in production first :)
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#101 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAlwd5phk1cRP2RkSYUU7XG0EGLvcu87ks5rb5N8gaJpZM4L-g9O>
.
|
ext/semian/semian_resource.c
Outdated
{ | ||
semian_resource_t *res = NULL; | ||
TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res); | ||
if (perform_semop(res->sem_id, SI_SEM_TICKETS, 1, SEM_UNDO, NULL) == -1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that in the original C this appeared to be using the magic number 0 to refer to the first semaphore in the set, which happens to be the ticket semaphore
ext/semian/semian_resource.c
Outdated
semian_resource_t *res = NULL; | ||
|
||
TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res); | ||
if (semctl(res->sem_id, SI_NUM_SEMAPHORES, IPC_RMID) == -1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that in the original C this was also usin the index 0. The index seems to be a required arg but it doesn't care what it is.
I think that using an invalid semaphore ID is a better practice for this purpose, and this one is..
ext/semian/semian_resource.c
Outdated
raise_semian_syscall_error("semget()", errno); | ||
} | ||
|
||
set_semaphore_permissions(res->sem_id, c_permissions); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that in the original C, this was coercing a Long to a fixed int.
ext/semian/semian_tickets.c
Outdated
|
||
/* it's possible that we haven't actually initialized the | ||
semaphore structure yet - wait a bit in that case */ | ||
if (get_sem_val(sem_id, SI_SEM_CONFIGURED_TICKETS) == 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably be abstracted into a generic spinlock inline, to poll arbitrary sems for arbitrary values
@dalehamel Legendary effort! If you break these up, I'm 👍 to canarying a bunch of this in our production environment once it's ready. |
@jacobbednarz These should be ready for review: Note that pretty much every one of these PRs is going to depend on the previous one (I can't think of any sane way to do this otherwise), so I can't put up additional PRs until the prior ones are reviewed and merged. |
obsoletes #67 |
Closing this, it's going to be done over a series of smaller PRs and this is just confusing people |
Note: This is an omnibus PR, broken down into smaller, more reviewable chunks. See below.
What
semian.c has been broken up into several files, each with a more cohesive purpose.
I have also fixed travis CI for ruby 2.3.1 by adding a line to update rubygems, resulting in bundler completing successfully.
Note that while there are quite a lot of additions / deletions, very little logic has changed. Where it has, it has been pulled out into separate functions, and the control flow should remain identical. This is evinced by the fact that CI still passes, so the behavior is the same.
Per C conventions, header files have been used almost entirely for function declarations, where the corresponding C files provide definitions for those prototypes. This rule is only broken for inline functions, as described below. Pre-processor macros have also been relegated to the header files, per C conventions.
The biggest change here is that where all functions were previously declared static, we now have 3 types of functions:
I have also adopted the convention that function prototypes should be documented within their header file. If a function is static, it may be documented above the definition. Each header file also offers a brief explanation for its own purpose, which applies to the the corresponding C file.
The only actual logic changes here are:
Why
Why do this enormous refactor?
I found it difficult to reason about semian in a single large source file. Though 500 lines isn't large by most standards, the lack of forward declarations forced the code to be organized in a particular way that was somewhat unintuitive.
Moreover, there were clearly a few distinct concerns that were being muddled together:
By separating these out into more cohesive translation units, I find it much easier to reason about these separate concerns independently.
As a side effect, I also chose to optimize a few simple functions as inlines. I doubt this will have a significant performance improvement, but saving a few instructions here or there was never a bad thing.
The biggest motivation to this was that I found it difficult to reason about how tickets were being configured and managed. By abstracting this and cleaning it up, I think it's become very obvious, and the implementation of the ticket quota strategy (and, potentially, addition future strategies) should be easy to drop in.
How
This omnibus PR is being broken out into smaller, more reviewable PRs
Note: Many of these PRs won't work on their own, as they depend on other aspects of the restructuring. For instance, the first 2 PRs are fine to merge independently, but subsequent PRs depend on prior ones. This is not yet an exhaustive list.
Ready for review:
Pending dependent PR merges: