diff options
Added "better" multi thread handling to AvatarFactoryModule, which uses EventWaitHandles, I'm a bit concerned about what effect this might have on performance in a large grid. But I've spent long enough on this problem for one day and as its valentines day, I will be killed and tortured (most likely not in that order) if I don't stop work for the day soon.
Diffstat (limited to 'OpenSim/Region')
-rw-r--r-- | OpenSim/Region/Environment/Modules/AvatarFactoryModule.cs | 143 |
1 files changed, 120 insertions, 23 deletions
diff --git a/OpenSim/Region/Environment/Modules/AvatarFactoryModule.cs b/OpenSim/Region/Environment/Modules/AvatarFactoryModule.cs index d7f23f8..7933593 100644 --- a/OpenSim/Region/Environment/Modules/AvatarFactoryModule.cs +++ b/OpenSim/Region/Environment/Modules/AvatarFactoryModule.cs | |||
@@ -28,6 +28,7 @@ | |||
28 | 28 | ||
29 | using System; | 29 | using System; |
30 | using System.Collections.Generic; | 30 | using System.Collections.Generic; |
31 | using System.Threading; | ||
31 | using libsecondlife; | 32 | using libsecondlife; |
32 | using Nini.Config; | 33 | using Nini.Config; |
33 | using OpenSim.Framework; | 34 | using OpenSim.Framework; |
@@ -51,18 +52,118 @@ namespace OpenSim.Region.Environment.Modules | |||
51 | private BaseDatabaseConnector m_databaseMapper; | 52 | private BaseDatabaseConnector m_databaseMapper; |
52 | private AppearanceTableMapper m_appearanceMapper; | 53 | private AppearanceTableMapper m_appearanceMapper; |
53 | 54 | ||
55 | private Dictionary<LLUUID, EventWaitHandle> m_fetchesInProgress = new Dictionary<LLUUID, EventWaitHandle>(); | ||
56 | private object m_syncLock = new object(); | ||
57 | |||
54 | public bool TryGetAvatarAppearance(LLUUID avatarId, out AvatarAppearance appearance) | 58 | public bool TryGetAvatarAppearance(LLUUID avatarId, out AvatarAppearance appearance) |
55 | { | 59 | { |
56 | //check cache | 60 | appearance = CheckCache(avatarId); |
57 | lock (m_avatarsAppearance) | 61 | if (appearance != null) |
58 | { | 62 | { |
59 | if (m_avatarsAppearance.ContainsKey(avatarId)) | 63 | return true; |
64 | } | ||
65 | |||
66 | //not in cache so check to see if another thread is already fetching it | ||
67 | //should only let one thread at a time do this part | ||
68 | EventWaitHandle waitHandle = null; | ||
69 | lock (m_syncLock) | ||
70 | { | ||
71 | if (m_fetchesInProgress.TryGetValue(avatarId, out waitHandle)) | ||
60 | { | 72 | { |
61 | appearance = m_avatarsAppearance[avatarId]; | 73 | waitHandle.WaitOne(); |
62 | return true; | 74 | appearance = CheckCache(avatarId); |
75 | if (appearance != null) | ||
76 | { | ||
77 | waitHandle = null; | ||
78 | return true; | ||
79 | } | ||
80 | else | ||
81 | { | ||
82 | waitHandle = null; | ||
83 | return false; | ||
84 | } | ||
85 | } | ||
86 | else | ||
87 | { | ||
88 | //no thread already fetching this appearance, so add a wait handle to list | ||
89 | //for any following threads that want the same appearance | ||
90 | waitHandle = new EventWaitHandle(false, EventResetMode.ManualReset); | ||
91 | m_fetchesInProgress.Add(avatarId, waitHandle); | ||
63 | } | 92 | } |
64 | } | 93 | } |
65 | 94 | ||
95 | |||
96 | //this is the first thread to request this appearance | ||
97 | //so let it check the db and if not found then create a default appearance | ||
98 | //and add that to the cache | ||
99 | appearance = CheckDatabase(avatarId); | ||
100 | if (appearance != null) | ||
101 | { | ||
102 | //appearance has now been added to cache so lets pulse any waiting threads | ||
103 | lock (m_syncLock) | ||
104 | { | ||
105 | m_fetchesInProgress.Remove(avatarId); | ||
106 | waitHandle.Set(); | ||
107 | } | ||
108 | waitHandle = null; | ||
109 | return true; | ||
110 | } | ||
111 | |||
112 | //not found a appearance for the user, so create a new default one | ||
113 | appearance = CreateDefault(avatarId); | ||
114 | if (appearance != null) | ||
115 | { | ||
116 | //update database | ||
117 | if (m_enablePersist) | ||
118 | { | ||
119 | m_appearanceMapper.Add(avatarId.UUID, appearance); | ||
120 | } | ||
121 | |||
122 | //add appearance to dictionary cache | ||
123 | lock (m_avatarsAppearance) | ||
124 | { | ||
125 | m_avatarsAppearance[avatarId] = appearance; | ||
126 | } | ||
127 | |||
128 | //appearance has now been added to cache so lets pulse any waiting threads | ||
129 | lock (m_syncLock) | ||
130 | { | ||
131 | m_fetchesInProgress.Remove(avatarId); | ||
132 | waitHandle.Set(); | ||
133 | } | ||
134 | waitHandle = null; | ||
135 | return true; | ||
136 | } | ||
137 | else | ||
138 | { | ||
139 | //something went wrong, so release the wait handle and remove it | ||
140 | //all waiting threads will fail to find cached appearance | ||
141 | //but its better for them to fail than wait for ever | ||
142 | lock (m_syncLock) | ||
143 | { | ||
144 | m_fetchesInProgress.Remove(avatarId); | ||
145 | waitHandle.Set(); | ||
146 | } | ||
147 | waitHandle = null; | ||
148 | return false; | ||
149 | } | ||
150 | |||
151 | } | ||
152 | |||
153 | private AvatarAppearance CreateDefault(LLUUID avatarId) | ||
154 | { | ||
155 | AvatarAppearance appearance = null; | ||
156 | AvatarWearable[] wearables; | ||
157 | byte[] visualParams; | ||
158 | GetDefaultAvatarAppearance(out wearables, out visualParams); | ||
159 | appearance = new AvatarAppearance(avatarId, wearables, visualParams); | ||
160 | |||
161 | return appearance; | ||
162 | } | ||
163 | |||
164 | private AvatarAppearance CheckDatabase(LLUUID avatarId) | ||
165 | { | ||
166 | AvatarAppearance appearance = null; | ||
66 | //check db | 167 | //check db |
67 | if (m_enablePersist) | 168 | if (m_enablePersist) |
68 | { | 169 | { |
@@ -74,28 +175,24 @@ namespace OpenSim.Region.Environment.Modules | |||
74 | { | 175 | { |
75 | m_avatarsAppearance[avatarId] = appearance; | 176 | m_avatarsAppearance[avatarId] = appearance; |
76 | } | 177 | } |
77 | return true; | ||
78 | } | 178 | } |
79 | } | 179 | } |
180 | return appearance; | ||
181 | } | ||
80 | 182 | ||
81 | //not found a appearance for the user, so create a new one | 183 | private AvatarAppearance CheckCache(LLUUID avatarId) |
82 | AvatarWearable[] wearables; | 184 | { |
83 | byte[] visualParams; | 185 | AvatarAppearance appearance = null; |
84 | GetDefaultAvatarAppearance(out wearables, out visualParams); | 186 | //check cache |
85 | appearance = new AvatarAppearance(avatarId, wearables, visualParams); | ||
86 | |||
87 | //add appearance to dictionary cache | ||
88 | lock (m_avatarsAppearance) | 187 | lock (m_avatarsAppearance) |
89 | { | 188 | { |
90 | m_avatarsAppearance[avatarId] = appearance; | 189 | if (m_avatarsAppearance.ContainsKey(avatarId)) |
91 | } | 190 | { |
92 | 191 | appearance = m_avatarsAppearance[avatarId]; | |
93 | //update database | 192 | // return true; |
94 | if (m_enablePersist) | 193 | } |
95 | { | ||
96 | m_appearanceMapper.Add(avatarId.UUID, appearance); | ||
97 | } | 194 | } |
98 | return true; | 195 | return appearance; |
99 | } | 196 | } |
100 | 197 | ||
101 | public void Initialise(Scene scene, IConfigSource source) | 198 | public void Initialise(Scene scene, IConfigSource source) |
@@ -175,11 +272,11 @@ namespace OpenSim.Region.Environment.Modules | |||
175 | if (baseItem != null) | 272 | if (baseItem != null) |
176 | { | 273 | { |
177 | assetId = baseItem.assetID; | 274 | assetId = baseItem.assetID; |
178 | 275 | ||
179 | if (m_avatarsAppearance.ContainsKey(clientView.AgentId)) | 276 | if (m_avatarsAppearance.ContainsKey(clientView.AgentId)) |
180 | { | 277 | { |
181 | AvatarAppearance avatAppearance = null; | 278 | AvatarAppearance avatAppearance = null; |
182 | 279 | ||
183 | lock (m_avatarsAppearance) | 280 | lock (m_avatarsAppearance) |
184 | { | 281 | { |
185 | avatAppearance = m_avatarsAppearance[clientView.AgentId]; | 282 | avatAppearance = m_avatarsAppearance[clientView.AgentId]; |