Skip to content
This repository has been archived by the owner on Aug 4, 2018. It is now read-only.

Commit

Permalink
swecgo: use mutex invoker and prepare for Thread Local Storage support
Browse files Browse the repository at this point in the history
  • Loading branch information
dwlnetnl committed Oct 5, 2016
1 parent d64dd32 commit 6108731
Show file tree
Hide file tree
Showing 6 changed files with 1,212 additions and 1,214 deletions.
278 changes: 46 additions & 232 deletions swecgo/swecgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package swecgo

import (
"runtime"
"sync"

"github.com/dwlnetnl/swego"
Expand All @@ -13,86 +14,39 @@ import (
// this context is done by calling initFn. If initFn is nil, the default data
// path is set to DefaultPath. For more information see the Programmer's
// Documentation about swe_set_ephe_path.
//
// In non-TLS-mode only a single fn can be executed at any point time. This is
// achieved by sending fn over a channel to a separate goroutine that executes
// all closures it receives and Call blocks waiting until fn is done executing.
//
// TLS-mode (Thread Local Storage) is currently not implemented.
func Call(initFn func(swego.Interface), fn func(swego.Interface)) {
initGInvoker(initFn)
gInvoker.Invoke(fn)
// In TLS-mode fn would be called on a single thread in a pool of locked OS
// threads. For more information about this see runtime.LockOSThread.
inv := NewInvoker(initFn)
inv.Invoke(fn)
}

// NewInvoker initializes an execution context and returns it.
// If initFn is nil, the default data path is set to DefaultPath. For more
// information see the Programmer's Documentation about swe_set_ephe_path.
func NewInvoker(initFn func(swego.Interface)) swego.Invoker {
initGInvoker(initFn)
return gInvoker
}

// ----------

var gInvoker swego.Invoker
var gInvokerOnce sync.Once

func initGInvoker(initFn func(swego.Interface)) {
gInvokerOnce.Do(func() {
if supportsTLS() {
panic("Swiss Ephemeris library with Thread Local Storage enabled " +
"is not supported")
if initFn == nil {
initFn = func(swe swego.Interface) {
swe.SetPath(DefaultPath)
}
}

// gInvoker = newChanInvoker()
gInvoker = newMuInvoker()

if initFn == nil {
initFn = func(swe swego.Interface) {
swe.SetPath(DefaultPath)
}
}
if supportsTLS() {
panic("swecgo: Thread Local Storage (TLS) is not supported")
// inv := tlsInvoker{}
// inv.Invoke(initFn)
// return inv
}

gInvoker.Invoke(initFn)
gInvoker.once.Do(func() {
gInvoker.inv = newMuInvoker()
gInvoker.inv.Invoke(initFn)
})
}

// ----------

type chanInvoker struct {
fnCh chan func()
return gInvoker.inv
}

func newChanInvoker() *chanInvoker {
inv := &chanInvoker{fnCh: make(chan func())}
go inv.runLoop()
return inv
}

// Invoke implements interface swego.Invoker.
func (inv *chanInvoker) Invoke(fn func(swego.Interface)) error {
var wg sync.WaitGroup

wg.Add(1)
inv.fnCh <- func() {
fn(wrapper{})
wg.Done()
}

wg.Wait()
return nil
}

func (inv *chanInvoker) runLoop() {
// Run loop is a separate goroutine.
// The OS thread is always locked during a cgo call.
// This code might change if Thread Local Storage (TLS) is used.

for fn := range inv.fnCh {
fn()
}
var gInvoker struct {
inv swego.Invoker
once sync.Once
}

type muInvoker struct {
Expand All @@ -104,186 +58,46 @@ func newMuInvoker() *muInvoker { return &muInvoker{} }
// Invoke implements interface swego.Invoker.
func (inv *muInvoker) Invoke(fn func(swego.Interface)) error {
inv.mu.Lock()
fn(wrapper{})
fn(gWrapper)
inv.mu.Unlock()
return nil
}

// ----------

type wrapper struct{}

var _ swego.Interface = wrapper{} // assert interface

// Version implements swego.Interface.
func (wrapper) Version() string { return Version }

// GetLibraryPath implements swego.Interface.
func (wrapper) GetLibraryPath() string { return getLibraryPath() }

// SetPath implements swego.Interface.
func (wrapper) SetPath(ephepath string) { setEphePath(ephepath) }

// Close implements swego.Interface.
func (wrapper) Close() {
closeEphemeris()
// *chanInvoker.fnCh should be closed, but it is global.
}

func setCalcFlagsState(fl *swego.CalcFlags) {
if (fl.Flags & flgTopo) == flgTopo {
setTopo(fl.TopoLoc.Long, fl.TopoLoc.Lat, fl.TopoLoc.Alt)
}

if (fl.Flags & flgSidereal) == flgSidereal {
setSidMode(fl.SidMode.Mode, fl.SidMode.T0, fl.SidMode.AyanT0)
}

if fl.FileNameJPL != "" {
fl.FileNameJPL = swego.FnameDft
}

setFileNameJPL(fl.FileNameJPL)
}

// PlanetName implements swego.Interface.
func (wrapper) PlanetName(pl swego.Planet) string { return planetName(pl) }

// Calc implements swego.Interface.
func (wrapper) Calc(et float64, pl swego.Planet, fl *swego.CalcFlags) ([]float64, int, error) {
setCalcFlagsState(fl)
return calc(et, pl, fl.Flags)
}

// CalcUT implements swego.Interface.
func (wrapper) CalcUT(ut float64, pl swego.Planet, fl *swego.CalcFlags) ([]float64, int, error) {
setCalcFlagsState(fl)
return calcUT(ut, pl, fl.Flags)
}

// NodAps implements swego.Interface.
func (wrapper) NodAps(et float64, pl swego.Planet, fl *swego.CalcFlags, m swego.NodApsMethod) (nasc, ndsc, peri, aphe []float64, err error) {
setCalcFlagsState(fl)
return nodAps(et, pl, fl.Flags, m)
}

// NodApsUT implements swego.Interface.
func (wrapper) NodApsUT(ut float64, pl swego.Planet, fl *swego.CalcFlags, m swego.NodApsMethod) (nasc, ndsc, peri, aphe []float64, err error) {
setCalcFlagsState(fl)
return nodApsUT(ut, pl, fl.Flags, m)
}

// GetAyanamsa implements swego.Interface.
func (wrapper) GetAyanamsa(et float64, sidmode *swego.SidMode) float64 {
setSidMode(sidmode.Mode, sidmode.T0, sidmode.AyanT0)
return getAyanamsa(et)
}

// GetAyanamsaUT implements swego.Interface.
func (wrapper) GetAyanamsaUT(ut float64, sidmode *swego.SidMode) float64 {
setSidMode(sidmode.Mode, sidmode.T0, sidmode.AyanT0)
return getAyanamsaUT(ut)
}

// GetAyanamsaEx implements swego.Interface.
func (wrapper) GetAyanamsaEx(et float64, fl *swego.AyanamsaExFlags) (float64, error) {
setSidMode(fl.SidMode.Mode, fl.SidMode.T0, fl.SidMode.AyanT0)
return getAyanamsaEx(et, fl.Flags)
}

// GetAyanamsaExUT implements swego.Interface.
func (wrapper) GetAyanamsaExUT(ut float64, fl *swego.AyanamsaExFlags) (float64, error) {
setSidMode(fl.SidMode.Mode, fl.SidMode.T0, fl.SidMode.AyanT0)
return getAyanamsaExUT(ut, fl.Flags)
}

// GetAyanamsaName implements swego.Interface.
func (wrapper) GetAyanamsaName(ayan swego.Ayanamsa) string {
return getAyanamsaName(ayan)
}

// JulDay implements swego.Interface.
func (wrapper) JulDay(y, m, d int, h float64, ct swego.CalType) float64 {
return julDay(y, m, d, h, int(ct))
}

// RevJul implements swego.Interface.
func (wrapper) RevJul(jd float64, ct swego.CalType) (y, m, d int, h float64) {
return revJul(jd, int(ct))
}

// UTCToJD implements swego.Interface.
func (wrapper) UTCToJD(y, m, d, h, i int, s float64, ct swego.CalType) (et, ut float64, err error) {
return utcToJD(y, m, d, h, i, s, int(ct))
}
type tlsInvoker struct{}

// JdETToUTC implements swego.Interface.
func (wrapper) JdETToUTC(et float64, ct swego.CalType) (y, m, d, h, i int, s float64) {
return jdETToUTC(et, int(ct))
func (tlsInvoker) Invoke(fn func(swego.Interface)) error {
runtime.LockOSThread()
fn(gWrapper)
runtime.UnlockOSThread()
return nil
}

// JdUT1ToUTC implements swego.Interface.
func (wrapper) JdUT1ToUTC(ut1 float64, ct swego.CalType) (y, m, d, h, i int, s float64) {
return jdUT1ToUTC(ut1, int(ct))
type chanInvoker struct {
fnCh chan func()
}

// Houses implements swego.Interface.
func (wrapper) Houses(ut, geolat, geolon float64, hsys swego.HSys) ([]float64, []float64, error) {
return houses(ut, geolat, geolon, hsys)
func newChanInvoker() *chanInvoker {
inv := &chanInvoker{fnCh: make(chan func())}
go inv.runLoop()
return inv
}

// HousesEx implements swego.Interface.
func (wrapper) HousesEx(ut float64, fl *swego.HousesExFlags, geolat, geolon float64, hsys swego.HSys) ([]float64, []float64, error) {
if (fl.Flags & flgSidereal) == flgSidereal {
setSidMode(fl.SidMode.Mode, fl.SidMode.T0, fl.SidMode.AyanT0)
func (inv *chanInvoker) runLoop() {
for fn := range inv.fnCh {
fn()
}

return housesEx(ut, fl.Flags, geolat, geolon, hsys)
}

// HousesARMC implements swego.Interface.
func (wrapper) HousesARMC(armc, geolat, eps float64, hsys swego.HSys) ([]float64, []float64, error) {
return housesARMC(armc, geolat, eps, hsys)
}

// HousePos implements swego.Interface.
func (wrapper) HousePos(armc, geolat, eps float64, hsys swego.HSys, pllng, pllat float64) (float64, error) {
return housePos(armc, geolat, eps, hsys, pllng, pllat)
}

// HouseName implements swego.Interface.
func (wrapper) HouseName(hsys swego.HSys) string {
return houseName(hsys)
}

// DeltaT implements swego.Interface.
func (wrapper) DeltaT(jd float64) float64 { return deltaT(jd) }

// DeltaTEx implements swego.Interface.
func (wrapper) DeltaTEx(jd float64, eph swego.Ephemeris) (float64, error) {
return deltaTEx(jd, int32(eph))
}

// SetDeltaTUserDef implements swego.Interface.
func (wrapper) SetDeltaTUserDef(v float64) { setDeltaTUserDef(v) }

// TimeEqu implements swego.Interface.
func (wrapper) TimeEqu(jd float64) (float64, error) { return timeEqu(jd) }

// LMTToLAT implements swego.Interface.
func (wrapper) LMTToLAT(jdLMT, geolon float64) (float64, error) {
return lmtToLAT(jdLMT, geolon)
}
// Invoke implements interface swego.Invoker.
func (inv *chanInvoker) Invoke(fn func(swego.Interface)) error {
var wg sync.WaitGroup

// LATToLMT implements swego.Interface.
func (wrapper) LATToLMT(jdLAT, geolon float64) (float64, error) {
return latToLMT(jdLAT, geolon)
}
wg.Add(1)
inv.fnCh <- func() {
fn(gWrapper)
wg.Done()
}

// SidTime0 implements swego.Interface.
func (wrapper) SidTime0(ut, eps, nut float64) float64 {
return sidTime0(ut, eps, nut)
wg.Wait()
return nil
}

// SidTime implements swego.Interface.
func (wrapper) SidTime(ut float64) float64 { return sidTime(ut) }
Loading

0 comments on commit 6108731

Please sign in to comment.