summaryrefslogtreecommitdiff
path: root/xgob/util/atom.go
blob: 4000e02e133b683cff6f4977cde1f447457ac0cd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package atom

import (
	"fmt"
	"sync"
	"xgob"
	"xgob/xproto"
)

type state struct {
	c *xgob.Connection

	name map[xproto.Atom]string
	atom map[string]xproto.Atom

	pendingAtom map[string]chan xproto.InternAtomReply
}

var lock sync.RWMutex
var db = make(map[*xgob.Connection]state)

func newState (c *xgob.Connection) state {
	var rv state
	rv.c = c
	rv.name = make(map[xproto.Atom]string)
	rv.atom = make(map[string]xproto.Atom)

	rv.pendingAtom = make(map[string]chan xproto.InternAtomReply)
	return rv
}

/* internAtom must be called under a write lock */
func (s *state) internAtom (name string) xproto.Atom {
	c, ok := s.pendingAtom[name]
	if ok {
		s.pendingAtom[name] = nil, false
	} else {
		c = xproto.InternAtom(s.c, false, name)
	}
	reply, ok := <- c
	if !ok {
		s.c.Flush()
		reply = <- c
	}
	if reply.Error == nil {
		s.name[reply.Atom] = name
		s.atom[name] = reply.Atom
	} else {
		println("Atom Error",reply.Error.Error)
	}
	return reply.Atom
}

func getState(c *xgob.Connection, writelocked bool) *state {
	s, ok := db[c]
	if !ok {
		// Grab write lock, recheck, create state, downgrade to read lock
		if !writelocked {
			lock.RUnlock()
			lock.Lock()
			s, ok = db[c]
		}
		if !ok {
			db[c] = newState(c)
			s, ok = db[c]
		}
		if !writelocked {
			lock.Unlock()
			lock.RLock()
		}
	}
	return &s
}

func PreloadAtom(c *xgob.Connection, name string) {
	lock.Lock()
	
	s := getState(c, true)
	_, ok := s.atom[name]

	if !ok {
		_, ok := s.pendingAtom[name]
		if !ok {
			s.pendingAtom[name] = xproto.InternAtom(c, false, name)
		}
	}
	lock.Unlock()
}

func Atom (c *xgob.Connection, name string) xproto.Atom {
	lock.RLock()

	s := getState(c, false)
	atom, ok := s.atom[name]

	lock.RUnlock()

	if !ok {
		// Grab write lock, recheck, InternAtom
		lock.Lock()
		atom, ok = s.atom[name]
		if !ok {
			atom = s.internAtom(name)
		}
		lock.Unlock()
	}
	return atom
}

func AtomName (c *xgob.Connection, atom xproto.Atom) string {
	return fmt.Sprintf("0x%X", atom) // TODO
}