Commit 3e68d6d7 by Ian Lance Taylor

compiler/runtime: Copy channel implementation from master library.

From-SVN: r181874
parent 04f7a48e
......@@ -13493,12 +13493,18 @@ Receive_expression::do_check_types(Gogo*)
tree
Receive_expression::do_get_tree(Translate_context* context)
{
Location loc = this->location();
Channel_type* channel_type = this->channel_->type()->channel_type();
if (channel_type == NULL)
{
go_assert(this->channel_->type()->is_error());
return error_mark_node;
}
Expression* td = Expression::make_type_descriptor(channel_type, loc);
tree td_tree = td->get_tree(context);
Type* element_type = channel_type->element_type();
Btype* element_type_btype = element_type->get_backend(context->gogo());
tree element_type_tree = type_to_tree(element_type_btype);
......@@ -13507,8 +13513,7 @@ Receive_expression::do_get_tree(Translate_context* context)
if (element_type_tree == error_mark_node || channel == error_mark_node)
return error_mark_node;
return Gogo::receive_from_channel(element_type_tree, channel,
this->for_select_, this->location());
return Gogo::receive_from_channel(element_type_tree, td_tree, channel, loc);
}
// Dump ast representation for a receive expression.
......
......@@ -1947,7 +1947,7 @@ class Receive_expression : public Expression
public:
Receive_expression(Expression* channel, Location location)
: Expression(EXPRESSION_RECEIVE, location),
channel_(channel), for_select_(false)
channel_(channel)
{ }
// Return the channel.
......@@ -1955,11 +1955,6 @@ class Receive_expression : public Expression
channel()
{ return this->channel_; }
// Note that this is for a select statement.
void
set_for_select()
{ this->for_select_ = true; }
protected:
int
do_traverse(Traverse* traverse)
......@@ -1998,8 +1993,6 @@ class Receive_expression : public Expression
private:
// The channel from which we are receiving.
Expression* channel_;
// Whether this is for a select statement.
bool for_select_;
};
#endif // !defined(GO_EXPRESSIONS_H)
......@@ -2201,13 +2201,12 @@ Gogo::runtime_error(int code, Location location)
}
// Return a tree for receiving a value of type TYPE_TREE on CHANNEL.
// This does a blocking receive and returns the value read from the
// channel. If FOR_SELECT is true, this is being done because it was
// chosen in a select statement.
// TYPE_DESCRIPTOR_TREE is the channel's type descriptor. This does a
// blocking receive and returns the value read from the channel.
tree
Gogo::receive_from_channel(tree type_tree, tree channel, bool for_select,
Location location)
Gogo::receive_from_channel(tree type_tree, tree type_descriptor_tree,
tree channel, Location location)
{
if (type_tree == error_mark_node || channel == error_mark_node)
return error_mark_node;
......@@ -2222,12 +2221,10 @@ Gogo::receive_from_channel(tree type_tree, tree channel, bool for_select,
"__go_receive_small",
2,
uint64_type_node,
TREE_TYPE(type_descriptor_tree),
type_descriptor_tree,
ptr_type_node,
channel,
boolean_type_node,
(for_select
? boolean_true_node
: boolean_false_node));
channel);
if (call == error_mark_node)
return error_mark_node;
// This can panic if there are too many operations on a closed
......@@ -2253,15 +2250,13 @@ Gogo::receive_from_channel(tree type_tree, tree channel, bool for_select,
location,
"__go_receive_big",
3,
boolean_type_node,
void_type_node,
TREE_TYPE(type_descriptor_tree),
type_descriptor_tree,
ptr_type_node,
channel,
ptr_type_node,
tmpaddr,
boolean_type_node,
(for_select
? boolean_true_node
: boolean_false_node));
tmpaddr);
if (call == error_mark_node)
return error_mark_node;
// This can panic if there are too many operations on a closed
......
......@@ -527,14 +527,9 @@ class Gogo
// Receive a value from a channel.
static tree
receive_from_channel(tree type_tree, tree channel, bool for_select,
receive_from_channel(tree type_tree, tree type_descriptor_tree, tree channel,
Location);
// Return a tree for receiving an integer on a channel.
static tree
receive_as_64bit_integer(tree type, tree channel, bool blocking,
bool for_select);
// Make a trampoline which calls FNADDR passing CLOSURE.
tree
make_trampoline(tree fnaddr, tree closure, Location);
......
......@@ -1780,7 +1780,6 @@ Parse::init_vars_from_receive(const Typed_identifier_list* vars, Type* type,
Statement* s = Statement::make_tuple_receive_assignment(val_var,
received_var,
receive->channel(),
false,
location);
if (!this->gogo_->in_global_scope())
......@@ -3769,7 +3768,6 @@ Parse::tuple_assignment(Expression_list* lhs, Range_clause* p_range_clause)
Expression* channel = receive->channel();
Statement* s = Statement::make_tuple_receive_assignment(val, success,
channel,
false,
location);
this->gogo_->add_statement(s);
}
......
......@@ -54,8 +54,6 @@ enum Runtime_function_type
RFT_MAPITER,
// Go type chan any, C type struct __go_channel *.
RFT_CHAN,
// Go type *chan any, C type struct __go_channel **.
RFT_CHANPTR,
// Go type non-empty interface, C type struct __go_interface.
RFT_IFACE,
// Go type interface{}, C type struct __go_empty_interface.
......@@ -148,10 +146,6 @@ runtime_function_type(Runtime_function_type bft)
t = Type::make_channel_type(true, true, Type::make_void_type());
break;
case RFT_CHANPTR:
t = Type::make_pointer_type(runtime_function_type(RFT_CHAN));
break;
case RFT_IFACE:
{
Typed_identifier_list* methods = new Typed_identifier_list();
......@@ -223,7 +217,6 @@ convert_to_runtime_function_type(Runtime_function_type bft, Expression* e,
case RFT_SLICE:
case RFT_MAP:
case RFT_CHAN:
case RFT_CHANPTR:
case RFT_IFACE:
case RFT_EFACE:
return Expression::make_unsafe_cast(runtime_function_type(bft), e, loc);
......@@ -393,12 +386,3 @@ Runtime::map_iteration_type()
return Type::make_array_type(runtime_function_type(RFT_POINTER), iexpr);
}
// Return the type used to pass a list of general channels to the
// select runtime function.
Type*
Runtime::chanptr_type()
{
return runtime_function_type(RFT_CHANPTR);
}
......@@ -121,31 +121,44 @@ DEF_GO_RUNTIME(CHAN_LEN, "__go_chan_len", P1(CHAN), R1(INT))
DEF_GO_RUNTIME(CHAN_CAP, "__go_chan_cap", P1(CHAN), R1(INT))
// Send a small value on a channel.
DEF_GO_RUNTIME(SEND_SMALL, "__go_send_small", P3(CHAN, UINT64, BOOL), R0())
// Send a small value on a channel without blocking.
DEF_GO_RUNTIME(SEND_NONBLOCKING_SMALL, "__go_send_nonblocking_small",
P2(CHAN, UINT64), R1(BOOL))
DEF_GO_RUNTIME(SEND_SMALL, "__go_send_small", P3(TYPE, CHAN, UINT64), R0())
// Send a big value on a channel.
DEF_GO_RUNTIME(SEND_BIG, "__go_send_big", P3(CHAN, POINTER, BOOL), R0())
// Send a big value on a channel without blocking.
DEF_GO_RUNTIME(SEND_NONBLOCKING_BIG, "__go_send_nonblocking_big",
P2(CHAN, POINTER), R1(BOOL))
DEF_GO_RUNTIME(SEND_BIG, "__go_send_big", P3(TYPE, CHAN, POINTER), R0())
// Receive a small value from a channel.
DEF_GO_RUNTIME(RECEIVE_SMALL, "__go_receive_small", P2(CHAN, BOOL), R1(UINT64))
DEF_GO_RUNTIME(RECEIVE_SMALL, "__go_receive_small", P2(TYPE, CHAN), R1(UINT64))
// Receive a big value from a channel.
DEF_GO_RUNTIME(RECEIVE_BIG, "__go_receive_big", P3(CHAN, POINTER, BOOL),
R1(BOOL))
DEF_GO_RUNTIME(RECEIVE_BIG, "__go_receive_big", P3(TYPE, CHAN, POINTER), R0())
// Receive a value from a channel returning whether it is closed.
DEF_GO_RUNTIME(CHANRECV2, "runtime.chanrecv2", P2(CHAN, POINTER), R1(BOOL))
DEF_GO_RUNTIME(CHANRECV2, "runtime.chanrecv2", P3(TYPE, CHAN, POINTER),
R1(BOOL))
// Start building a select statement.
DEF_GO_RUNTIME(NEWSELECT, "runtime.newselect", P1(INT), R1(POINTER))
// Receive a value from a channel returning whether it is closed, for select.
DEF_GO_RUNTIME(CHANRECV3, "runtime.chanrecv3", P2(CHAN, POINTER), R1(BOOL))
// Add a default clause to a select statement.
DEF_GO_RUNTIME(SELECTDEFAULT, "runtime.selectdefault", P2(POINTER, INT), R0())
// Add a send clause to a select statement.
DEF_GO_RUNTIME(SELECTSEND, "runtime.selectsend",
P4(POINTER, CHAN, POINTER, INT), R0())
// Add a receive clause to a select statement, for a clause which does
// not check whether the channel is closed.
DEF_GO_RUNTIME(SELECTRECV, "runtime.selectrecv",
P4(POINTER, CHAN, POINTER, INT), R0())
// Add a receive clause to a select statement, for a clause which does
// check whether the channel is closed.
DEF_GO_RUNTIME(SELECTRECV2, "runtime.selectrecv2",
P5(POINTER, CHAN, POINTER, BOOLPTR, INT), R0())
// Run a select, returning the index of the selected clause.
DEF_GO_RUNTIME(SELECTGO, "runtime.selectgo", P1(POINTER), R1(INT))
// Panic.
......@@ -213,11 +226,6 @@ DEF_GO_RUNTIME(GO, "__go_go", P2(FUNC_PTR, POINTER), R0())
DEF_GO_RUNTIME(DEFER, "__go_defer", P3(BOOLPTR, FUNC_PTR, POINTER), R0())
// Run a select statement.
DEF_GO_RUNTIME(SELECT, "__go_select", P4(UINTPTR, BOOL, CHANPTR, BOOLPTR),
R1(UINTPTR))
// Convert an empty interface to an empty interface, returning ok.
DEF_GO_RUNTIME(IFACEE2E2, "runtime.ifaceE2E2", P1(EFACE), R2(EFACE, BOOL))
......
......@@ -43,11 +43,6 @@ class Runtime
static Type*
map_iteration_type();
// Return the type used to pass a list of general channels to the
// select runtime function.
static Type*
chanptr_type();
private:
static Named_object*
runtime_declaration(Function);
......
......@@ -165,12 +165,10 @@ class Statement
Expression* should_set, Location);
// Make an assignment from a nonblocking receive to a pair of
// variables. FOR_SELECT is true is this is being created for a
// case x, ok := <-c in a select statement.
// variables.
static Statement*
make_tuple_receive_assignment(Expression* val, Expression* closed,
Expression* channel, bool for_select,
Location);
Expression* channel, Location);
// Make an assignment from a type guard to a pair of variables.
static Statement*
......@@ -634,14 +632,9 @@ class Send_statement : public Statement
Send_statement(Expression* channel, Expression* val,
Location location)
: Statement(STATEMENT_SEND, location),
channel_(channel), val_(val), for_select_(false)
channel_(channel), val_(val)
{ }
// Note that this is for a select statement.
void
set_for_select()
{ this->for_select_ = true; }
protected:
int
do_traverse(Traverse* traverse);
......@@ -663,8 +656,6 @@ class Send_statement : public Statement
Expression* channel_;
// The value to send.
Expression* val_;
// Whether this is for a select statement.
bool for_select_;
};
// Select_clauses holds the clauses of a select statement. This is
......@@ -693,23 +684,32 @@ class Select_clauses
Named_object* var, Named_object* closedvar, bool is_default,
Block* statements, Location location)
{
this->clauses_.push_back(Select_clause(is_send, channel, val, closed, var,
closedvar, is_default, statements,
location));
int index = static_cast<int>(this->clauses_.size());
this->clauses_.push_back(Select_clause(index, is_send, channel, val,
closed, var, closedvar, is_default,
statements, location));
}
size_t
size() const
{ return this->clauses_.size(); }
// Traverse the select clauses.
int
traverse(Traverse*);
// Lower statements.
void
lower(Gogo*, Named_object*, Block*);
lower(Gogo*, Named_object*, Block*, Temporary_statement*);
// Determine types.
void
determine_types();
// Check types.
void
check_types();
// Whether the select clauses may fall through to the statement
// which follows the overall select statement.
bool
......@@ -717,7 +717,8 @@ class Select_clauses
// Convert to the backend representation.
Bstatement*
get_backend(Translate_context*, Unnamed_label* break_label, Location);
get_backend(Translate_context*, Temporary_statement* sel,
Unnamed_label* break_label, Location);
// Dump AST representation.
void
......@@ -734,27 +735,37 @@ class Select_clauses
is_default_(false)
{ }
Select_clause(bool is_send, Expression* channel, Expression* val,
Expression* closed, Named_object* var,
Select_clause(int index, bool is_send, Expression* channel,
Expression* val, Expression* closed, Named_object* var,
Named_object* closedvar, bool is_default, Block* statements,
Location location)
: channel_(channel), val_(val), closed_(closed), var_(var),
closedvar_(closedvar), statements_(statements), location_(location),
is_send_(is_send), is_default_(is_default), is_lowered_(false)
: index_(index), channel_(channel), val_(val), closed_(closed),
var_(var), closedvar_(closedvar), statements_(statements),
location_(location), is_send_(is_send), is_default_(is_default),
is_lowered_(false)
{ go_assert(is_default ? channel == NULL : channel != NULL); }
// Return the index of this clause.
int
index() const
{ return this->index_; }
// Traverse the select clause.
int
traverse(Traverse*);
// Lower statements.
void
lower(Gogo*, Named_object*, Block*);
lower(Gogo*, Named_object*, Block*, Temporary_statement*);
// Determine types.
void
determine_types();
// Check types.
void
check_types();
// Return true if this is the default clause.
bool
is_default() const
......@@ -798,6 +809,18 @@ class Select_clauses
dump_clause(Ast_dump_context*) const;
private:
void
lower_default(Block*, Expression*, Expression*);
void
lower_send(Block*, Expression*, Expression*, Expression*);
void
lower_recv(Gogo*, Named_object*, Block*, Expression*, Expression*,
Expression*);
// The index of this case in the generated switch statement.
int index_;
// The channel.
Expression* channel_;
// The value to send or the lvalue to receive into.
......@@ -822,12 +845,6 @@ class Select_clauses
bool is_lowered_;
};
void
add_clause_backend(Translate_context*, Location, int index,
int case_value, Select_clause*, Unnamed_label*,
std::vector<std::vector<Bexpression*> >* cases,
std::vector<Bstatement*>* clauses);
typedef std::vector<Select_clause> Clauses;
Clauses clauses_;
......@@ -840,7 +857,7 @@ class Select_statement : public Statement
public:
Select_statement(Location location)
: Statement(STATEMENT_SELECT, location),
clauses_(NULL), break_label_(NULL), is_lowered_(false)
clauses_(NULL), sel_(NULL), break_label_(NULL), is_lowered_(false)
{ }
// Add the clauses.
......@@ -867,6 +884,10 @@ class Select_statement : public Statement
do_determine_types()
{ this->clauses_->determine_types(); }
void
do_check_types(Gogo*)
{ this->clauses_->check_types(); }
bool
do_may_fall_through() const
{ return this->clauses_->may_fall_through(); }
......@@ -880,6 +901,8 @@ class Select_statement : public Statement
private:
// The select clauses.
Select_clauses* clauses_;
// A temporary which holds the select structure we build up at runtime.
Temporary_statement* sel_;
// The break label.
Unnamed_label* break_label_;
// Whether this statement has been lowered.
......
......@@ -409,10 +409,7 @@ runtime_files = \
runtime/go-caller.c \
runtime/go-can-convert-interface.c \
runtime/go-cgo.c \
runtime/go-chan-cap.c \
runtime/go-chan-len.c \
runtime/go-check-interface.c \
runtime/go-close.c \
runtime/go-construct-map.c \
runtime/go-convert-interface.c \
runtime/go-copy.c \
......@@ -432,27 +429,16 @@ runtime_files = \
runtime/go-map-len.c \
runtime/go-map-range.c \
runtime/go-nanotime.c \
runtime/go-new-channel.c \
runtime/go-new-map.c \
runtime/go-new.c \
runtime/go-panic.c \
runtime/go-print.c \
runtime/go-rec-big.c \
runtime/go-rec-nb-big.c \
runtime/go-rec-nb-small.c \
runtime/go-rec-small.c \
runtime/go-recover.c \
runtime/go-reflect.c \
runtime/go-reflect-call.c \
runtime/go-reflect-chan.c \
runtime/go-reflect-map.c \
runtime/go-rune.c \
runtime/go-runtime-error.c \
runtime/go-select.c \
runtime/go-send-big.c \
runtime/go-send-nb-big.c \
runtime/go-send-nb-small.c \
runtime/go-send-small.c \
runtime/go-setenv.c \
runtime/go-signal.c \
runtime/go-strcmp.c \
......@@ -473,6 +459,7 @@ runtime_files = \
runtime/go-unsafe-newarray.c \
runtime/go-unsafe-pointer.c \
runtime/go-unwind.c \
runtime/chan.c \
runtime/cpuprof.c \
$(runtime_lock_files) \
runtime/mcache.c \
......@@ -488,7 +475,6 @@ runtime_files = \
runtime/thread.c \
runtime/yield.c \
$(rtems_task_variable_add_file) \
chan.c \
iface.c \
malloc.c \
map.c \
......
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package runtime
#include "config.h"
#include "channel.h"
#define nil NULL
typedef _Bool bool;
typedef unsigned char byte;
typedef struct __go_channel chan;
/* Do a channel receive with closed status. */
func chanrecv2(c *chan, val *byte) (received bool) {
uintptr_t element_size = c == nil ? 0 : c->element_type->__size;
if (element_size > 8) {
return __go_receive_big(c, val, 0);
} else {
union {
char b[8];
uint64_t v;
} u;
u.v = __go_receive_small_closed(c, 0, &received);
#ifndef WORDS_BIGENDIAN
__builtin_memcpy(val, u.b, element_size);
#else
__builtin_memcpy(val, u.b + 8 - element_size, element_size);
#endif
return received;
}
}
/* Do a channel receive with closed status for a select statement. */
func chanrecv3(c *chan, val *byte) (received bool) {
uintptr_t element_size = c->element_type->__size;
if (element_size > 8) {
return __go_receive_big(c, val, 1);
} else {
union {
char b[8];
uint64_t v;
} u;
u.v = __go_receive_small_closed(c, 1, &received);
#ifndef WORDS_BIGENDIAN
__builtin_memcpy(val, u.b, element_size);
#else
__builtin_memcpy(val, u.b + 8 - element_size, element_size);
#endif
return received;
}
}
/* channel.h -- the channel type for Go.
Copyright 2009 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file. */
#include <stdint.h>
#include <pthread.h>
#include "go-type.h"
/* This structure is used when a select is waiting for a synchronous
channel. */
struct __go_channel_select
{
/* A pointer to the next select waiting for this channel. */
struct __go_channel_select *next;
/* A pointer to the channel which this select will use. This starts
out as NULL and is set to the first channel which synchs up with
this one. This variable to which this points may only be
accessed when __go_select_data_mutex is held. */
struct __go_channel **selected;
/* A pointer to a variable which must be set to true if the
goroutine which sets *SELECTED wants to read from the channel,
false if it wants to write to it. */
_Bool *is_read;
};
/* A channel is a pointer to this structure. */
struct __go_channel
{
/* A mutex to control access to the channel. */
pthread_mutex_t lock;
/* A condition variable. This is signalled when data is added to
the channel and when data is removed from the channel. */
pthread_cond_t cond;
/* The type of elements on this channel. */
const struct __go_type_descriptor *element_type;
/* True if a goroutine is waiting to send on a synchronous
channel. */
_Bool waiting_to_send;
/* True if a goroutine is waiting to receive on a synchronous
channel. */
_Bool waiting_to_receive;
/* True if this channel was selected for send in a select statement.
This looks out all other sends. */
_Bool selected_for_send;
/* True if this channel was selected for receive in a select
statement. This locks out all other receives. */
_Bool selected_for_receive;
/* True if this channel has been closed. */
_Bool is_closed;
/* The list of select statements waiting to send on a synchronous
channel. */
struct __go_channel_select *select_send_queue;
/* The list of select statements waiting to receive on a synchronous
channel. */
struct __go_channel_select *select_receive_queue;
/* If a select statement is waiting for this channel, it sets these
pointers. When something happens on the channel, the channel
locks the mutex, signals the condition, and unlocks the
mutex. */
pthread_mutex_t *select_mutex;
pthread_cond_t *select_cond;
/* The number of entries in the circular buffer. */
unsigned int num_entries;
/* Where to store the next value. */
unsigned int next_store;
/* Where to fetch the next value. If next_fetch == next_store, the
buffer is empty. If next_store + 1 == next_fetch, the buffer is
full. */
unsigned int next_fetch;
/* The circular buffer. */
uint64_t data[];
};
/* Try to link up with the structure generated by the frontend. */
typedef struct __go_channel __go_channel;
/* The mutex used to control access to the value pointed to by the
__go_channel_select selected field. No additional mutexes may be
acquired while this mutex is held. */
extern pthread_mutex_t __go_select_data_mutex;
extern struct __go_channel *
__go_new_channel (const struct __go_type_descriptor *, uintptr_t);
extern _Bool __go_synch_with_select (struct __go_channel *, _Bool);
extern void __go_broadcast_to_select (struct __go_channel *);
extern void __go_send_acquire (struct __go_channel *, _Bool);
extern _Bool __go_send_nonblocking_acquire (struct __go_channel *);
extern void __go_send_release (struct __go_channel *);
extern void __go_send_small (struct __go_channel *, uint64_t, _Bool);
extern _Bool __go_send_nonblocking_small (struct __go_channel *, uint64_t);
extern void __go_send_big (struct __go_channel *, const void *, _Bool);
extern _Bool __go_send_nonblocking_big (struct __go_channel *, const void *);
extern _Bool __go_receive_acquire (struct __go_channel *, _Bool);
#define RECEIVE_NONBLOCKING_ACQUIRE_DATA 0
#define RECEIVE_NONBLOCKING_ACQUIRE_NODATA 1
#define RECEIVE_NONBLOCKING_ACQUIRE_CLOSED 2
extern int __go_receive_nonblocking_acquire (struct __go_channel *);
extern uint64_t __go_receive_small (struct __go_channel *, _Bool);
extern uint64_t __go_receive_small_closed (struct __go_channel *, _Bool,
_Bool *);
extern void __go_receive_release (struct __go_channel *);
struct __go_receive_nonblocking_small
{
/* Value read from channel, or 0. */
uint64_t __val;
/* True if value was read from channel. */
_Bool __success;
/* True if channel is closed. */
_Bool __closed;
};
extern struct __go_receive_nonblocking_small
__go_receive_nonblocking_small (struct __go_channel *);
extern _Bool __go_receive_big (struct __go_channel *, void *, _Bool);
extern _Bool __go_receive_nonblocking_big (struct __go_channel *, void *,
_Bool *);
extern void __go_unlock_and_notify_selects (struct __go_channel *);
extern _Bool __go_builtin_closed (struct __go_channel *);
extern void __go_builtin_close (struct __go_channel *);
extern int __go_chan_len (struct __go_channel *);
extern int __go_chan_cap (struct __go_channel *);
extern uintptr_t __go_select (uintptr_t, _Bool, struct __go_channel **,
_Bool *);
/* go-chan-cap.c -- the cap function applied to a channel.
Copyright 2009 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file. */
#include <stddef.h>
#include "go-assert.h"
#include "channel.h"
/* Return the cap function applied to a channel--the size of the
buffer. This could be done inline but I'm doing it as a function
for now to make it easy to change the channel structure. */
int
__go_chan_cap (struct __go_channel *channel)
{
int i;
int ret;
if (channel == NULL)
return 0;
i = pthread_mutex_lock (&channel->lock);
__go_assert (i == 0);
if (channel->num_entries == 0)
ret = 0;
else
{
/* One slot is always unused. We added 1 when we created the
channel. */
ret = channel->num_entries - 1;
}
i = pthread_mutex_unlock (&channel->lock);
__go_assert (i == 0);
return ret;
}
/* go-chan-len.c -- the len function applied to a channel.
Copyright 2009 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file. */
#include <stddef.h>
#include "go-assert.h"
#include "channel.h"
/* Return the len function applied to a channel--the number of
elements in the buffer. This could be done inline but I'm doing it
as a function for now to make it easy to change the channel
structure. */
int
__go_chan_len (struct __go_channel *channel)
{
int i;
int ret;
if (channel == NULL)
return 0;
i = pthread_mutex_lock (&channel->lock);
__go_assert (i == 0);
if (channel->num_entries == 0)
ret = 0;
else if (channel->next_fetch == channel->next_store)
ret = 0;
else
ret = ((channel->next_store + channel->num_entries - channel->next_fetch)
% channel->num_entries);
i = pthread_mutex_unlock (&channel->lock);
__go_assert (i == 0);
return ret;
}
/* go-close.c -- the builtin close function.
Copyright 2009 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file. */
#include "runtime.h"
#include "go-assert.h"
#include "go-panic.h"
#include "channel.h"
/* Close a channel. After a channel is closed, sends are no longer
permitted. Receives always return zero. */
void
__go_builtin_close (struct __go_channel *channel)
{
int i;
if (channel == NULL)
runtime_panicstring ("close of nil channel");
i = pthread_mutex_lock (&channel->lock);
__go_assert (i == 0);
while (channel->selected_for_send)
runtime_cond_wait (&channel->cond, &channel->lock);
if (channel->is_closed)
{
i = pthread_mutex_unlock (&channel->lock);
__go_assert (i == 0);
runtime_panicstring ("close of closed channel");
}
channel->is_closed = 1;
i = pthread_cond_broadcast (&channel->cond);
__go_assert (i == 0);
__go_unlock_and_notify_selects (channel);
}
/* go-new-channel.c -- allocate a new channel.
Copyright 2009 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file. */
#include <stddef.h>
#include <stdint.h>
#include "runtime.h"
#include "go-alloc.h"
#include "go-assert.h"
#include "channel.h"
struct __go_channel*
__go_new_channel (const struct __go_type_descriptor *channel_type,
uintptr_t entries)
{
const struct __go_channel_type *ctd;
const struct __go_type_descriptor *element_type;
uintptr_t element_size;
int ientries;
struct __go_channel* ret;
size_t alloc_size;
int i;
__go_assert (channel_type->__code == GO_CHAN);
ctd = (const struct __go_channel_type *) channel_type;
element_type = ctd->__element_type;
element_size = element_type->__size;
ientries = (int) entries;
if (ientries < 0
|| (uintptr_t) ientries != entries
|| (element_size > 0 && entries > (uintptr_t) -1 / element_size))
runtime_panicstring ("chan size out of range");
alloc_size = (element_size + sizeof (uint64_t) - 1) / sizeof (uint64_t);
/* We use a circular buffer which means that when next_fetch ==
next_store we don't know whether the buffer is empty or full. So
we allocate an extra space, and always leave a space open.
FIXME. */
if (entries != 0)
++entries;
ret = (struct __go_channel*) __go_alloc (sizeof (struct __go_channel)
+ ((entries == 0 ? 1 : entries)
* alloc_size
* sizeof (uint64_t)));
i = pthread_mutex_init (&ret->lock, NULL);
__go_assert (i == 0);
i = pthread_cond_init (&ret->cond, NULL);
__go_assert (i == 0);
ret->element_type = element_type;
ret->waiting_to_send = 0;
ret->waiting_to_receive = 0;
ret->selected_for_send = 0;
ret->selected_for_receive = 0;
ret->is_closed = 0;
ret->select_send_queue = NULL;
ret->select_receive_queue = NULL;
ret->select_mutex = NULL;
ret->select_cond = NULL;
ret->num_entries = entries;
ret->next_store = 0;
ret->next_fetch = 0;
return ret;
}
/* go-rec-big.c -- receive something larger than 64 bits on a channel.
Copyright 2009 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file. */
#include <stdint.h>
#include "go-panic.h"
#include "channel.h"
/* Returns true if a value was received, false if the channel is
closed. */
_Bool
__go_receive_big (struct __go_channel *channel, void *val, _Bool for_select)
{
uintptr_t element_size;
size_t alloc_size;
size_t offset;
if (channel == NULL)
{
/* Block forever. */
__go_select (0, 0, NULL, NULL);
}
element_size = channel->element_type->__size;
alloc_size = (element_size + sizeof (uint64_t) - 1) / sizeof (uint64_t);
if (!__go_receive_acquire (channel, for_select))
{
__builtin_memset (val, 0, element_size);
return 0;
}
offset = channel->next_fetch * alloc_size;
__builtin_memcpy (val, &channel->data[offset], element_size);
__go_receive_release (channel);
return 1;
}
/* go-rec-nb-big.c -- nonblocking receive of something big on a channel.
Copyright 2009 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file. */
#include <stdint.h>
#include "channel.h"
/* Return true if a value was received, false if not. */
_Bool
__go_receive_nonblocking_big (struct __go_channel* channel, void *val,
_Bool *closed)
{
uintptr_t element_size;
size_t alloc_size;
size_t offset;
if (channel == NULL)
{
if (closed != NULL)
*closed = 0;
return 0;
}
element_size = channel->element_type->__size;
alloc_size = (element_size + sizeof (uint64_t) - 1) / sizeof (uint64_t);
int data = __go_receive_nonblocking_acquire (channel);
if (data != RECEIVE_NONBLOCKING_ACQUIRE_DATA)
{
__builtin_memset (val, 0, element_size);
if (closed != NULL)
*closed = data == RECEIVE_NONBLOCKING_ACQUIRE_CLOSED;
return 0;
}
offset = channel->next_fetch * alloc_size;
__builtin_memcpy (val, &channel->data[offset], element_size);
__go_receive_release (channel);
return 1;
}
/* go-rec-nb-small.c -- nonblocking receive of something smal on a channel.
Copyright 2009 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file. */
#include <stdint.h>
#include "runtime.h"
#include "go-assert.h"
#include "go-panic.h"
#include "channel.h"
/* Prepare to receive something on a nonblocking channel. */
int
__go_receive_nonblocking_acquire (struct __go_channel *channel)
{
int i;
_Bool has_data;
i = pthread_mutex_lock (&channel->lock);
__go_assert (i == 0);
while (channel->selected_for_receive)
runtime_cond_wait (&channel->cond, &channel->lock);
if (channel->is_closed
&& (channel->num_entries == 0
? channel->next_store == 0
: channel->next_fetch == channel->next_store))
{
__go_unlock_and_notify_selects (channel);
return RECEIVE_NONBLOCKING_ACQUIRE_CLOSED;
}
if (channel->num_entries > 0)
has_data = channel->next_fetch != channel->next_store;
else
{
if (channel->waiting_to_receive)
{
/* Some other goroutine is already waiting for data on this
channel, so we can't pick it up. */
has_data = 0;
}
else if (channel->next_store > 0)
{
/* There is data on the channel. */
has_data = 1;
}
else if (__go_synch_with_select (channel, 0))
{
/* We synched up with a select sending data, so there will
be data for us shortly. Tell the select to go, and then
wait for the data. */
__go_broadcast_to_select (channel);
while (channel->next_store == 0)
runtime_cond_wait (&channel->cond, &channel->lock);
has_data = 1;
}
else
{
/* Otherwise there is no data. */
has_data = 0;
}
if (has_data)
{
channel->waiting_to_receive = 1;
__go_assert (channel->next_store == 1);
}
}
if (!has_data)
{
i = pthread_mutex_unlock (&channel->lock);
__go_assert (i == 0);
return RECEIVE_NONBLOCKING_ACQUIRE_NODATA;
}
return RECEIVE_NONBLOCKING_ACQUIRE_DATA;
}
/* Receive something 64 bits or smaller on a nonblocking channel. */
struct __go_receive_nonblocking_small
__go_receive_nonblocking_small (struct __go_channel *channel)
{
uintptr_t element_size;
struct __go_receive_nonblocking_small ret;
if (channel == NULL)
{
ret.__val = 0;
ret.__success = 0;
ret.__closed = 0;
return ret;
}
element_size = channel->element_type->__size;
__go_assert (element_size <= sizeof (uint64_t));
int data = __go_receive_nonblocking_acquire (channel);
if (data != RECEIVE_NONBLOCKING_ACQUIRE_DATA)
{
ret.__val = 0;
ret.__success = 0;
ret.__closed = data == RECEIVE_NONBLOCKING_ACQUIRE_CLOSED;
return ret;
}
ret.__val = channel->data[channel->next_fetch];
__go_receive_release (channel);
ret.__success = 1;
ret.__closed = 0;
return ret;
}
/* go-rec-small.c -- receive something smaller than 64 bits on a channel.
Copyright 2009 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file. */
#include <stdint.h>
#include "runtime.h"
#include "go-assert.h"
#include "go-panic.h"
#include "channel.h"
/* This mutex controls access to the selected field of struct
__go_channel_select. While this mutex is held, no other mutexes
may be acquired. */
pthread_mutex_t __go_select_data_mutex = PTHREAD_MUTEX_INITIALIZER;
/* Try to synchronize with a select waiting on a sychronized channel.
This is used by a send or receive. The channel is locked. This
returns true if it was able to synch. */
_Bool
__go_synch_with_select (struct __go_channel *channel, _Bool is_send)
{
struct __go_channel_select *p;
int i;
__go_assert (channel->num_entries == 0);
i = pthread_mutex_lock (&__go_select_data_mutex);
__go_assert (i == 0);
for (p = (is_send
? channel->select_receive_queue
: channel->select_send_queue);
p != NULL;
p = p->next)
{
if (*p->selected == NULL)
{
*p->selected = channel;
*p->is_read = !is_send;
if (is_send)
channel->selected_for_receive = 1;
else
channel->selected_for_send = 1;
break;
}
}
i = pthread_mutex_unlock (&__go_select_data_mutex);
__go_assert (i == 0);
/* The caller is responsible for signalling the select condition
variable so that the other select knows that something has
changed. We can't signal it here because we can't acquire the
select mutex while we hold a channel lock. */
return p != NULL;
}
/* If we synch with a select, then we need to signal the select that
something has changed. This requires grabbing the select mutex,
which can only be done when the channel is unlocked. This routine
does the signalling. It is called with the channel locked. It
unlocks the channel, broadcasts the signal and relocks the
channel. */
void
__go_broadcast_to_select (struct __go_channel *channel)
{
pthread_mutex_t *select_mutex;
pthread_cond_t *select_cond;
int i;
select_mutex = channel->select_mutex;
select_cond = channel->select_cond;
i = pthread_mutex_unlock (&channel->lock);
__go_assert (i == 0);
__go_assert (select_mutex != NULL && select_cond != NULL);
i = pthread_mutex_lock (select_mutex);
__go_assert (i == 0);
i = pthread_cond_broadcast (select_cond);
__go_assert (i == 0);
i = pthread_mutex_unlock (select_mutex);
__go_assert (i == 0);
i = pthread_mutex_lock (&channel->lock);
__go_assert (i == 0);
}
/* Prepare to receive something on a channel. Return true if the
channel is acquired (which implies that there is data available),
false if it is closed. */
_Bool
__go_receive_acquire (struct __go_channel *channel, _Bool for_select)
{
int i;
_Bool my_wait_lock;
_Bool synched_with_select;
my_wait_lock = 0;
synched_with_select = 0;
i = pthread_mutex_lock (&channel->lock);
__go_assert (i == 0);
while (1)
{
_Bool need_broadcast;
need_broadcast = 0;
/* Check whether the channel is closed. */
if (channel->is_closed
&& (channel->num_entries == 0
? channel->next_store == 0
: channel->next_fetch == channel->next_store))
{
channel->selected_for_receive = 0;
__go_unlock_and_notify_selects (channel);
return 0;
}
/* If somebody else has the channel locked for receiving, we
have to wait. If FOR_SELECT is true, then we are the one
with the lock. */
if (!channel->selected_for_receive || for_select)
{
if (channel->num_entries == 0)
{
/* If somebody else is waiting to receive, we have to
wait. */
if (!channel->waiting_to_receive || my_wait_lock)
{
_Bool was_marked;
/* Lock the channel so that we get to receive
next. */
was_marked = channel->waiting_to_receive;
channel->waiting_to_receive = 1;
my_wait_lock = 1;
/* See if there is a value to receive. */
if (channel->next_store > 0)
return 1;
/* If we haven't already done so, try to synch with
a select waiting to send on this channel. If we
have already synched with a select, we are just
looping until the select eventually causes
something to be sent. */
if (!synched_with_select && !for_select)
{
if (__go_synch_with_select (channel, 0))
{
synched_with_select = 1;
need_broadcast = 1;
}
}
/* If we marked the channel as waiting, we need to
signal, because something changed. It needs to
be a broadcast since there might be other
receivers waiting. */
if (!was_marked)
{
i = pthread_cond_broadcast (&channel->cond);
__go_assert (i == 0);
}
}
}
else
{
/* If there is a value on the channel, we are OK. */
if (channel->next_fetch != channel->next_store)
return 1;
}
}
/* If we just synched with a select, then we need to signal the
select condition variable. We can only do that if we unlock
the channel. So we need to unlock, signal, lock, and go
around the loop again without waiting. */
if (need_broadcast)
{
__go_broadcast_to_select (channel);
continue;
}
/* Wait for something to change, then loop around and try
again. */
runtime_cond_wait (&channel->cond, &channel->lock);
}
}
/* Finished receiving something on a channel. */
void
__go_receive_release (struct __go_channel *channel)
{
int i;
if (channel->num_entries != 0)
channel->next_fetch = (channel->next_fetch + 1) % channel->num_entries;
else
{
/* For a synchronous receiver, we tell the sender that we picked
up the value by setting the next_store field back to 0.
Using the mutexes should implement a memory barrier. */
__go_assert (channel->next_store == 1);
channel->next_store = 0;
channel->waiting_to_receive = 0;
}
channel->selected_for_receive = 0;
/* This is a broadcast to make sure that a synchronous sender sees
it. */
i = pthread_cond_broadcast (&channel->cond);
__go_assert (i == 0);
__go_unlock_and_notify_selects (channel);
}
/* Unlock a channel and notify any waiting selects that something
happened. */
void
__go_unlock_and_notify_selects (struct __go_channel *channel)
{
pthread_mutex_t* select_mutex;
pthread_cond_t* select_cond;
int i;
select_mutex = channel->select_mutex;
select_cond = channel->select_cond;
i = pthread_mutex_unlock (&channel->lock);
__go_assert (i == 0);
if (select_mutex != NULL)
{
i = pthread_mutex_lock (select_mutex);
__go_assert (i == 0);
i = pthread_cond_broadcast (select_cond);
__go_assert (i == 0);
i = pthread_mutex_unlock (select_mutex);
__go_assert (i == 0);
}
}
/* Receive something 64 bits or smaller on a channel. */
uint64_t
__go_receive_small_closed (struct __go_channel *channel, _Bool for_select,
_Bool *received)
{
uintptr_t element_size;
uint64_t ret;
if (channel == NULL)
{
/* Block forever. */
__go_select (0, 0, NULL, NULL);
}
element_size = channel->element_type->__size;
__go_assert (element_size <= sizeof (uint64_t));
if (!__go_receive_acquire (channel, for_select))
{
if (received != NULL)
*received = 0;
return 0;
}
ret = channel->data[channel->next_fetch];
__go_receive_release (channel);
if (received != NULL)
*received = 1;
return ret;
}
/* Called by the compiler. */
uint64_t
__go_receive_small (struct __go_channel *channel, _Bool for_select)
{
return __go_receive_small_closed (channel, for_select, NULL);
}
/* go-reflect-chan.c -- channel reflection support for Go.
Copyright 2009 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file. */
#include <stdlib.h>
#include <stdint.h>
#include "config.h"
#include "go-alloc.h"
#include "go-assert.h"
#include "go-panic.h"
#include "go-type.h"
#include "channel.h"
/* This file implements support for reflection on channels. These
functions are called from reflect/value.go. */
extern uintptr_t makechan (const struct __go_type_descriptor *, uint32_t)
asm ("libgo_reflect.reflect.makechan");
uintptr_t
makechan (const struct __go_type_descriptor *typ, uint32_t size)
{
struct __go_channel *channel;
void *ret;
channel = __go_new_channel (typ, size);
ret = __go_alloc (sizeof (void *));
__builtin_memcpy (ret, &channel, sizeof (void *));
return (uintptr_t) ret;
}
extern _Bool chansend (struct __go_channel_type *, uintptr_t, uintptr_t, _Bool)
asm ("libgo_reflect.reflect.chansend");
_Bool
chansend (struct __go_channel_type *ct, uintptr_t ch, uintptr_t val_i,
_Bool nb)
{
struct __go_channel *channel = (struct __go_channel *) ch;
uintptr_t element_size;
void *pv;
__go_assert (ct->__common.__code == GO_CHAN);
if (__go_is_pointer_type (ct->__element_type))
pv = &val_i;
else
pv = (void *) val_i;
element_size = ct->__element_type->__size;
if (element_size <= sizeof (uint64_t))
{
union
{
char b[sizeof (uint64_t)];
uint64_t v;
} u;
__builtin_memset (u.b, 0, sizeof (uint64_t));
#ifndef WORDS_BIGENDIAN
__builtin_memcpy (u.b, pv, element_size);
#else
__builtin_memcpy (u.b + sizeof (uint64_t) - element_size, pv,
element_size);
#endif
if (nb)
return __go_send_nonblocking_small (channel, u.v);
else
{
__go_send_small (channel, u.v, 0);
return 1;
}
}
else
{
if (nb)
return __go_send_nonblocking_big (channel, pv);
else
{
__go_send_big (channel, pv, 0);
return 1;
}
}
}
struct chanrecv_ret
{
uintptr_t val;
_Bool selected;
_Bool received;
};
extern struct chanrecv_ret chanrecv (struct __go_channel_type *, uintptr_t,
_Bool)
asm ("libgo_reflect.reflect.chanrecv");
struct chanrecv_ret
chanrecv (struct __go_channel_type *ct, uintptr_t ch, _Bool nb)
{
struct __go_channel *channel = (struct __go_channel *) ch;
void *pv;
uintptr_t element_size;
struct chanrecv_ret ret;
__go_assert (ct->__common.__code == GO_CHAN);
element_size = ct->__element_type->__size;
if (__go_is_pointer_type (ct->__element_type))
pv = &ret.val;
else
{
pv = __go_alloc (element_size);
ret.val = (uintptr_t) pv;
}
if (element_size <= sizeof (uint64_t))
{
union
{
char b[sizeof (uint64_t)];
uint64_t v;
} u;
if (!nb)
{
u.v = __go_receive_small_closed (channel, 0, &ret.received);
ret.selected = 1;
}
else
{
struct __go_receive_nonblocking_small s;
s = __go_receive_nonblocking_small (channel);
ret.selected = s.__success || s.__closed;
ret.received = s.__success;
u.v = s.__val;
}
#ifndef WORDS_BIGENDIAN
__builtin_memcpy (pv, u.b, element_size);
#else
__builtin_memcpy (pv, u.b + sizeof (uint64_t) - element_size,
element_size);
#endif
}
else
{
if (!nb)
{
ret.received = __go_receive_big (channel, pv, 0);
ret.selected = 1;
}
else
{
_Bool got;
_Bool closed;
got = __go_receive_nonblocking_big (channel, pv, &closed);
ret.selected = got || closed;
ret.received = got;
}
}
return ret;
}
extern void chanclose (uintptr_t) asm ("libgo_reflect.reflect.chanclose");
void
chanclose (uintptr_t ch)
{
struct __go_channel *channel = (struct __go_channel *) ch;
__go_builtin_close (channel);
}
extern int32_t chanlen (uintptr_t) asm ("libgo_reflect.reflect.chanlen");
int32_t
chanlen (uintptr_t ch)
{
struct __go_channel *channel = (struct __go_channel *) ch;
return (int32_t) __go_chan_len (channel);
}
extern int32_t chancap (uintptr_t) asm ("libgo_reflect.reflect.chancap");
int32_t
chancap (uintptr_t ch)
{
struct __go_channel *channel = (struct __go_channel *) ch;
return (int32_t) __go_chan_cap (channel);
}
/* go-send-big.c -- send something bigger than uint64_t on a channel.
Copyright 2009 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file. */
#include <stdint.h>
#include "go-panic.h"
#include "channel.h"
void
__go_send_big (struct __go_channel* channel, const void *val, _Bool for_select)
{
uintptr_t element_size;
size_t alloc_size;
size_t offset;
if (channel == NULL)
{
// Block forever.
__go_select (0, 0, NULL, NULL);
}
element_size = channel->element_type->__size;
alloc_size = (element_size + sizeof (uint64_t) - 1) / sizeof (uint64_t);
__go_send_acquire (channel, for_select);
offset = channel->next_store * alloc_size;
__builtin_memcpy (&channel->data[offset], val, element_size);
__go_send_release (channel);
}
/* go-send-nb-big.c -- nonblocking send of something big on a channel.
Copyright 2009 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file. */
#include <stdint.h>
#include "channel.h"
_Bool
__go_send_nonblocking_big (struct __go_channel* channel, const void *val)
{
uintptr_t element_size;
size_t alloc_size;
size_t offset;
if (channel == NULL)
return 0;
element_size = channel->element_type->__size;
alloc_size = (element_size + sizeof (uint64_t) - 1) / sizeof (uint64_t);
if (!__go_send_nonblocking_acquire (channel))
return 0;
offset = channel->next_store * alloc_size;
__builtin_memcpy (&channel->data[offset], val, element_size);
__go_send_release (channel);
return 1;
}
/* go-send-nb-small.c -- nonblocking send of something small on a channel.
Copyright 2009 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file. */
#include <stdint.h>
#include "runtime.h"
#include "go-assert.h"
#include "go-panic.h"
#include "channel.h"
/* Prepare to send something on a nonblocking channel. Return true if
we acquired the channel, false if we did not acquire it because
there is no space to send a value. */
_Bool
__go_send_nonblocking_acquire (struct __go_channel *channel)
{
int i;
_Bool has_space;
i = pthread_mutex_lock (&channel->lock);
__go_assert (i == 0);
while (channel->selected_for_send)
runtime_cond_wait (&channel->cond, &channel->lock);
if (channel->is_closed)
{
i = pthread_mutex_unlock (&channel->lock);
__go_assert (i == 0);
runtime_panicstring ("send on closed channel");
}
if (channel->num_entries > 0)
has_space = ((channel->next_store + 1) % channel->num_entries
!= channel->next_fetch);
else
{
/* This is a synchronous channel. If somebody is current
sending, then we can't send. Otherwise, see if somebody is
waiting to receive, or see if we can synch with a select. */
if (channel->waiting_to_send)
{
/* Some other goroutine is currently sending on this
channel, which means that we can't. */
has_space = 0;
}
else if (channel->waiting_to_receive)
{
/* Some other goroutine is waiting to receive a value, so we
can send directly to them. */
has_space = 1;
}
else if (__go_synch_with_select (channel, 1))
{
/* We found a select waiting to receive data, so we can send
to that. */
__go_broadcast_to_select (channel);
has_space = 1;
}
else
{
/* Otherwise, we can't send, because nobody is waiting to
receive. */
has_space = 0;
}
if (has_space)
{
channel->waiting_to_send = 1;
__go_assert (channel->next_store == 0);
}
}
if (!has_space)
{
i = pthread_mutex_unlock (&channel->lock);
__go_assert (i == 0);
return 0;
}
return 1;
}
/* Send something 64 bits or smaller on a channel. */
_Bool
__go_send_nonblocking_small (struct __go_channel *channel, uint64_t val)
{
if (channel == NULL)
return 0;
__go_assert (channel->element_type->__size <= sizeof (uint64_t));
if (!__go_send_nonblocking_acquire (channel))
return 0;
channel->data[channel->next_store] = val;
__go_send_release (channel);
return 1;
}
/* go-send-small.c -- send something 64 bits or smaller on a channel.
Copyright 2009 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file. */
#include <stdint.h>
#include "runtime.h"
#include "go-assert.h"
#include "go-panic.h"
#include "channel.h"
/* Prepare to send something on a channel. FOR_SELECT is true if this
call is being made after a select statement returned with this
channel selected. */
void
__go_send_acquire (struct __go_channel *channel, _Bool for_select)
{
int i;
i = pthread_mutex_lock (&channel->lock);
__go_assert (i == 0);
while (1)
{
if (channel->is_closed)
{
if (for_select)
channel->selected_for_send = 0;
i = pthread_mutex_unlock (&channel->lock);
__go_assert (i == 0);
runtime_panicstring ("send on closed channel");
}
/* If somebody else has the channel locked for sending, we have
to wait. If FOR_SELECT is true, then we are the one with the
lock. */
if (!channel->selected_for_send || for_select)
{
if (channel->num_entries == 0)
{
/* This is a synchronous channel. If nobody else is
waiting to send, we grab the channel and tell the
caller to send the data. We will then wait for a
receiver. */
if (!channel->waiting_to_send)
{
__go_assert (channel->next_store == 0);
return;
}
}
else
{
/* If there is room on the channel, we are OK. */
if ((channel->next_store + 1) % channel->num_entries
!= channel->next_fetch)
return;
}
}
/* Wait for something to change, then loop around and try
again. */
runtime_cond_wait (&channel->cond, &channel->lock);
}
}
/* Finished sending something on a channel. */
void
__go_send_release (struct __go_channel *channel)
{
int i;
if (channel->num_entries != 0)
{
/* This is a buffered channel. Bump the store count and signal
the condition variable. */
channel->next_store = (channel->next_store + 1) % channel->num_entries;
i = pthread_cond_signal (&channel->cond);
__go_assert (i == 0);
}
else
{
_Bool synched_with_select;
/* This is a synchronous channel. Indicate that we have a value
waiting. */
channel->next_store = 1;
channel->waiting_to_send = 1;
/* Tell everybody else to do something. This has to be a
broadcast because we might have both senders and receivers
waiting on the condition, but senders won't send another
signal. */
i = pthread_cond_broadcast (&channel->cond);
__go_assert (i == 0);
/* Wait until the value is received. */
synched_with_select = 0;
while (1)
{
if (channel->next_store == 0)
break;
/* If nobody is currently waiting to receive, try to synch
up with a select. */
if (!channel->waiting_to_receive && !synched_with_select)
{
if (__go_synch_with_select (channel, 1))
{
synched_with_select = 1;
__go_broadcast_to_select (channel);
continue;
}
}
runtime_cond_wait (&channel->cond, &channel->lock);
}
channel->waiting_to_send = 0;
/* Using the mutexes should implement a memory barrier. */
/* We have to signal again since we cleared the waiting_to_send
field. This has to be a broadcast because both senders and
receivers might be waiting, but only senders will be able to
act. */
i = pthread_cond_broadcast (&channel->cond);
__go_assert (i == 0);
}
channel->selected_for_send = 0;
__go_unlock_and_notify_selects (channel);
}
/* Send something 64 bits or smaller on a channel. */
void
__go_send_small (struct __go_channel *channel, uint64_t val, _Bool for_select)
{
if (channel == NULL)
{
// Block forever.
__go_select (0, 0, NULL, NULL);
}
__go_assert (channel->element_type->__size <= sizeof (uint64_t));
__go_send_acquire (channel, for_select);
channel->data[channel->next_store] = val;
__go_send_release (channel);
}
......@@ -55,14 +55,15 @@ typedef struct M M;
typedef union Note Note;
typedef struct MCache MCache;
typedef struct FixAlloc FixAlloc;
typedef struct Hchan Hchan;
typedef struct __go_defer_stack Defer;
typedef struct __go_open_array Slice;
typedef struct __go_string String;
typedef struct __go_interface Iface;
typedef struct __go_empty_interface Eface;
typedef struct __go_type_descriptor Type;
typedef struct __go_defer_stack Defer;
typedef struct __go_panic_stack Panic;
typedef struct __go_open_array Slice;
typedef struct __go_string String;
typedef struct __go_func_type FuncType;
typedef struct __go_map_type MapType;
......@@ -131,6 +132,7 @@ struct G
bool fromgogo; // reached from gogo
int16 status;
int32 goid;
uint32 selgen; // valid sudog pointer
const char* waitreason; // if status==Gwaiting
G* schedlink;
bool readyonstop;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment