forked from SAP/cgo-ase
-
Notifications
You must be signed in to change notification settings - Fork 0
/
context.go
132 lines (106 loc) · 3.31 KB
/
context.go
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// SPDX-FileCopyrightText: 2020 SAP SE
// SPDX-FileCopyrightText: 2021 SAP SE
// SPDX-FileCopyrightText: 2022 SAP SE
// SPDX-FileCopyrightText: 2023 SAP SE
//
// SPDX-License-Identifier: Apache-2.0
package ase
//#include "ctlib.h"
//#include "bridge.h"
import "C"
import (
"fmt"
"sync"
)
// context wraps C.CS_CONTEXT to ensure that the context is being closed
// and deallocated after the last connection was closed.
type csContext struct {
ctx *C.CS_CONTEXT
info *Info
// connections is a counter that keeps track of the number of
// connections using the context to communicate with an ASE
// instance.
// lock is used to guard access to connections.
connections uint
lock sync.Mutex
}
func newCsContext(info *Info) (*csContext, error) {
ctx := &csContext{}
ctx.info = info
if err := ctx.init(); err != nil {
return nil, err
}
return ctx, nil
}
// newConn is called by new connections to register their creation with
// the context.
// If the context is not initialized it will be initialized.
func (context *csContext) newConn() error {
context.lock.Lock()
defer context.lock.Unlock()
if context.ctx == nil {
if err := context.init(); err != nil {
return err
}
}
context.connections++
return nil
}
// dropConn is called by connections to register their deallocation with
// the context.
// If no other connection uses the context it will be deallocated.
func (context *csContext) dropConn() error {
context.lock.Lock()
defer context.lock.Unlock()
context.connections--
if context.connections > 0 {
return nil
}
// No connections using the context left, proceed with deallocation.
return context.drop()
}
// init allocates and initializes the context.
// If a DSN is set the DSN options will be applied.
func (context *csContext) init() error {
if retval := C.cs_ctx_alloc(C.CS_CURRENT_VERSION, &context.ctx); retval != C.CS_SUCCEED {
return makeError(retval, "C.cs_ctx_alloc failed")
}
if retval := C.ct_init(context.ctx, C.CS_CURRENT_VERSION); retval != C.CS_SUCCEED {
if err := context.drop(); err != nil {
return err
}
return makeError(retval, "C.ct_init failed")
}
if err := context.applyDSN(context.info); err != nil {
return fmt.Errorf("Failed to apply DSN: %w", err)
}
return nil
}
// drop deallocates the context.
func (context *csContext) drop() error {
if retval := C.ct_exit(context.ctx, C.CS_UNUSED); retval != C.CS_SUCCEED {
return makeError(retval, "C.ct_exit failed, has results pending")
}
if retval := C.cs_ctx_drop(context.ctx); retval != C.CS_SUCCEED {
return makeError(retval, "C.cs_ctx_drop failed")
}
context.ctx = nil
return nil
}
// applyDSN applies the relevant connection properties of a DSN to the
// context.
func (context *csContext) applyDSN(info *Info) error {
if retval := C.ct_callback(context.ctx, nil, C.CS_SET, C.CS_CLIENTMSG_CB, C.ct_callback_client_message); retval != C.CS_SUCCEED {
return makeError(retval, "C.ct_callback failed for client messages")
}
if retval := C.ct_callback(context.ctx, nil, C.CS_SET, C.CS_SERVERMSG_CB, C.ct_callback_server_message); retval != C.CS_SUCCEED {
return makeError(retval, "C.ct_callback failed for server messages")
}
if info.LogClientMsgs {
GlobalClientMessageBroker.RegisterHandler(logCltMsg)
}
if info.LogServerMsgs {
GlobalServerMessageBroker.RegisterHandler(logSrvMsg)
}
return nil
}